Get the auth token from the sign-in page

--- app/api/auth.coffee
+++ app/api/auth.coffee
@@ -24,7 +24,7 @@

   _getAuthToken: ->
     console?.log 'Getting auth token'
-    makeHTTPRequest 'GET', config.host + "/?now=#{Date.now()}", null, 'Accept': 'text/html'
+    makeHTTPRequest 'GET', config.host + "/users/sign_in/?now=#{Date.now()}", null, 'Accept': 'text/html'
       .then (request) ->
         [_, authTokenMatch1, authTokenMatch2] = request.responseText.match CSRF_TOKEN_PATTERN
         authToken = authTokenMatch1 ? authTokenMatch2


use http request not api client

use http request not api client

--- app/api/auth.coffee
+++ app/api/auth.coffee
@@ -257,7 +257,8 @@
         authenticity_token: token
         email: email

-      client.post '/../unsubscribe', data, JSON_HEADERS
+      makeHTTPRequest 'POST', config.host + '/unsubscribe', data, JSON_HEADERS
+
 # For quick debugging:
 window?.zooAuth = module.exports
 window?.log = console?.info.bind console, 'LOG'


Add babel.config to target_list

Add babel.config to target_list

--- build_detail.coffee
+++ build_detail.coffee
@@ -90,7 +90,7 @@
 target_list = [
   # Files
   'app.js', 'bower.json', 'config.cson',
-  'index.html', 'index.js', 'LICENSE', 'package.json',
+  'index.html', 'index.js', 'LICENSE', 'package.json', 'babel.config.js',
   # Folders
   'assets',
   'components',
@@ -152,7 +152,7 @@
           log "Extracting #{descript} finished"
           resolve()

-extractZipAsync =
+extractZipAsync =
   if process.platform == 'win32'
     extractZipNodeAsync
   else
@@ -308,18 +308,18 @@
   asar_path = path.join building_root, "app.asar"
   release_path = path.join release_dir, "app-#{poi_version}.7z"

-  try
+  try
     if !DONT_PACK_APP_IF_EXISTS
       throw true
     yield fs.accessAsync asar_path, fs.R_OK
-  catch
+  catch
     try
       fs.removeSync stage1_app
       fs.removeSync stage2_app
     catch e
     fs.ensureDirSync stage1_app
     fs.ensureDirSync stage2_app
-
+
     # Stage1: Everything downloaded and translated
     yield gitArchiveAsync tar_path, stage1_app
     download_themes = downloadThemesAsync theme_root
@@ -334,7 +334,7 @@
           )())
       )()
     yield Promise.join download_themes, prepare_app
-
+
     # Stage2: Filtered copy
     yield filterCopyAppAsync stage1_app, stage2_app


getting rid of console.log

getting rid of console.log

--- src/scripts/fishSanctuary.coffee
+++ src/scripts/fishSanctuary.coffee
@@ -6,5 +6,3 @@
   report.tabs [OverviewTab, HabitatTab, FishingValueTab]
   # path must be relative to dist/
   report.stylesheets ['./fishSanctuary.css']
-
-console.log 'working?'


adding Pug extension and grammar

adding Pug extension and grammar

*.pug files can't be beautified

--- src/languages/jade.coffee
+++ src/languages/jade.coffee
@@ -16,14 +16,14 @@
   Supported Grammars
   ###
   grammars: [
-    "Jade"
+    "Jade", "Pug"
   ]

   ###
   Supported extensions
   ###
   extensions: [
-    "jade"
+    "jade", "pug"
   ]

   options: [


Try catch format function

Try catch format function

--- lib/pg-hoff-results-view.coffee
+++ lib/pg-hoff-results-view.coffee
@@ -87,7 +87,10 @@
     createTd: (text, typeCode) ->
         td = document.createElement('td')
         td.textContent = text
-        td.textContent = Type[typeCode].format(text) if Type[typeCode]?.format and atom.config.get('pg-hoff.formatColumns')
+        try
+            td.textContent = Type[typeCode].format(text) if Type[typeCode]?.format and atom.config.get('pg-hoff.formatColumns')
+        catch err
+            console.error 'Could not format as ' + Type[typeCode].name, text

         return td


Refactor syntax

Refactor syntax

--- lib/rubocop-auto-correct.coffee
+++ lib/rubocop-auto-correct.coffee
@@ -1,5 +1,5 @@
 {CompositeDisposable} = require 'atom'
-spawnSync = require('child_process').spawnSync
+{spawnSync} = require 'child_process'
 which = require 'which'
 path = require 'path'
 fs = require 'fs-plus'


Better error handling for donation form if 500 response

--- static/src/scripts/app/views/DonationFormView.coffee
+++ static/src/scripts/app/views/DonationFormView.coffee
@@ -166,9 +166,11 @@
           else
             @donationThankYou()
         .fail (err) =>
-          console.log err
           @submitted = false
-          @donateFormResponse("alert", ["Donation failed: "])
+          if err.status == 500
+            @donateFormResponse("alert", ["We're suffering some technical difficulties please try again later today"])
+          else
+            @donateFormResponse("alert", ["Donation failed: " + err.responseJSON.message])

     donationThankYou: =>
       $("#donate-content").slideUp 300, =>


options -> items

options -> items


Remove access to buggy approximateSizes method

--- src/leveldb/handle.coffee
+++ src/leveldb/handle.coffee
@@ -93,7 +93,12 @@
   property: (name) ->
     @self.property(name)

+  ###
   approximateSizes: ->
-    @self.approximateSizes.apply @self, arguments
+    slices =
+      if Array.isArray arguments[0] then arguments[0]
+      else Array.prototype.slice.call arguments[0]
+    @self.approximateSizes slices
+  ###

   # TODO: compactRange


Bug in schema find method

Bug in schema find method

--- src/server/schema_manager.coffee
+++ src/server/schema_manager.coffee
@@ -85,8 +85,11 @@

   find: (options) ->
     if options.constructor == String
-      if options.indexOf("#") > 0
+      index = options.indexOf("#")
+      if index > 0
         options = {id: options}
+      else if index == 0
+        options = {name: options.slice(1)}
       else
         options = {name: options}


redirect after delete

redirect after delete

--- BackofficeBundle/Resources/public/coffee/treeAjaxDelete.coffee
+++ BackofficeBundle/Resources/public/coffee/treeAjaxDelete.coffee
@@ -8,8 +8,9 @@
       type: "DELETE"
       url: url
       success: (response) ->
-        Backbone.history.navigate "#", true
-        window.location.reload()
+        Backbone.history.history.back()
+        displayMenu(Backbone.history.fragment)
+        $("#OrchestraBOModal").modal "hide"
         return
       error: (response) ->
         $('.modal-footer', this.el).html response.responseJSON.error.message


fixed bug for #63

fixed bug for #63

--- src/lib/background.coffee
+++ src/lib/background.coffee
@@ -598,10 +598,10 @@
     runner.finish response
   runner.run (result = {}) ->
     $.extend data,
-      pageheight:     results.pageHeight ? 0
-      pagewidth:      results.pageWidth  ? 0
-      selection:      result.text        ? ''
-      selectionlinks: result.urls        ? []
+      pageheight:     result.pageHeight ? ''
+      pagewidth:      result.pageWidth  ? ''
+      selection:      result.text       ? ''
+      selectionlinks: result.urls       ? []
     callback?()

 # Creates an object containing data based on information derived from the


fixing company so _customers is cached

fixing company so _customers is cached

--- src/lib/company.js.coffee
+++ src/lib/company.js.coffee
@@ -308,14 +308,14 @@
       channel:      criteria.channels.join(",") if criteria.channels?
       sort:         if criteria.sort? then criteria.sort else "newest_first"
       search_text:  encodeURIComponent( criteria.searchString ) if criteria.searchString
-    customers = new Firehose.RemoteArray "companies/#{@id}/customers", params, (json) =>
+    @_customers = new Firehose.RemoteArray "companies/#{@id}/customers", params, (json) =>
       Firehose.Customer.customerWithID( json.id, this )
     if params.sort == 'newest_first'
-      customers.sortOn "newestInteractionReceivedAt", "desc"
+      @_customers.sortOn "newestInteractionReceivedAt", "desc"
     else
-      customers.sortOn "newestInteractionReceivedAt", "asc"
-    customers.onceParams = { pre_fetch: criteria.preFetch } if criteria.preFetch?
-    customers
+      @_customers.sortOn "newestInteractionReceivedAt", "asc"
+    @_customers.onceParams = { pre_fetch: criteria.preFetch } if criteria.preFetch?
+    @_customers

   ###


genericx86-64.coffee: Set yocto version to thud

Changelog-entry: Update to yocto thud
Signed-off-by: Andrei Gherzan <8dc7c09356db0b1762fb140617d7375c0a5a4131@balena.io>

--- genericx86-64.coffee
+++ genericx86-64.coffee
@@ -43,7 +43,7 @@
 		machine: 'genericx86-64'
 		image: 'resin-image-flasher'
 		fstype: 'resinos-img'
-		version: 'yocto-sumo'
+		version: 'yocto-thud'
 		deployArtifact: 'resin-image-flasher-genericx86-64.resinos-img'
 		compressed: true


Add groups.search API

Add groups.search API

--- src/Models/Groups.coffee
+++ src/Models/Groups.coffee
@@ -70,4 +70,10 @@
     @debug "Groups::addProject(#{groupId}, #{projectId})"
     @post "groups/#{parseInt groupId}/projects/#{parseInt projectId}", null, (data) -> fn data if fn

+  search: (nameOrPath, fn = null) =>
+    @debug "Groups::search(#{nameOrPath})"
+    params =
+      search: nameOrPath
+    @get "groups", params,  (data) -> fn data if fn
+
 module.exports = (client) -> new Groups client


embed raw markdown into enml

embed raw markdown into enml

--- app/assets/javascripts/app.coffee
+++ app/assets/javascripts/app.coffee
@@ -119,6 +119,12 @@
         # FIXME hack for strange class attr not removed
         cleaned_html = cleaned_html.replace(/class="[^"]*"/g, "")

+        # embbed raw markdown content into the html
+        cleaned_html = cleaned_html +
+            "" +
+            $('
').text(editor.getValue()).html() +
+            ""
+
         # add XML header & wrap with <en-note></en-note>
         final_note_xml = "<?xml version='1.0' encoding='utf-8'?>" +
                          "<!DOCTYPE en-note SYSTEM 'http://xml.evernote.com/pub/enml2.dtd'>" +


activity: add missing action docs

activity: add missing action docs

--- client/activity/lib/flux/actions/message.coffee
+++ client/activity/lib/flux/actions/message.coffee
@@ -132,6 +132,15 @@
           return { message }

+###*
+ * Action to put a loader marker to a certain position to a channel.
+ *
+ * @param {string} channelId
+ * @param {string} messageId
+ * @param {object} options
+ * @param {string} options.position - either 'before' or 'after'
+ * @param {boolean} options.autoload
+###
 putLoaderMarker = (channelId, messageId, options) ->

   dispatch actionTypes.ACTIVATE_LOADER_MARKER,


payment: remove unused code

payment: remove unused code

moar nonsense string conversion :)

--- client/app/MainApp/payment/vmproductview.coffee
+++ client/app/MainApp/payment/vmproductview.coffee
@@ -2,8 +2,7 @@
   constructor: (options = {}, data) ->
     super options, data

-  formatCurrency: (cents) ->
-    "#{ ( cents / 100 ).toFixed(2) }"
+  formatCurrency: (cents) -> ( cents / 100 ).toFixed 2

   pistachio: ->
     """


Remove dead code.

Remove dead code.

--- src/main/coffee/main.coffee
+++ src/main/coffee/main.coffee
@@ -11,10 +11,6 @@
                 post[k] = v
                 if k == "article"
                     v.titleEmpty = !v.title
-            #if (!this.localSettings.showOnlyUnread || (this.localSettings.showOnlyUnread && post.unread))
-            #    if post.unread
-            #        this._seenUnread = this._seenUnread + 1
-            #    this.articleList.add post
             this.articleList.add post

     _apiError: (type, desc, data) =>
@@ -191,7 +187,6 @@

     navigate: (uri, executeFn = false, addState = true) =>
         ret = super uri, executeFn, addState
-        #if ret
         $("#ad-body").html("<!-- newsrdr-new-site -->
 <div id='div-gpt-ad-1379655552510-0' style='width:728px; height:90px;'>
 <script type='text/javascript'>
@@ -482,14 +477,6 @@
                 this._maxId = data.id
                 this._postPage = this._postPage + 1
                 this._processFeedPosts data.list
-                #if (this._seenUnread - this._lastSeenUnread) < 10 && this.localSettings.showOnlyUnread
-                #    if this.newsFeedView? && this._seenUnread < this.newsFeedView.getSelectedUnread()
-                #        this.fetchMorePosts()
-                #    else
-                #        this._enableFetch = false
-                #        this._lastSeenUnread = null
-                #else
-                #    this._lastSeenUnread = null

         if not this._lastSeenUnread?
             this._lastSeenUnread = this._seenUnread


Remove obsolete keys

Remove obsolete keys

Complementary for
https://github.com/koding/koding/pull/9182/commits/aec83270f47844cd4897638eae5e4e6e82ab953c

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>

--- config/generateKonfig.coffee
+++ config/generateKonfig.coffee
@@ -149,13 +149,8 @@
     vmWatcherSecretKey: credentials.vmwatcher.secretKey
     terraformerSecretKey: credentials.terraformer.secretKey

-    awsAccessKeyId: credentials.awsKeys.vm_kloud.accessKeyId
-    awsSecretAccessKey: credentials.awsKeys.vm_kloud.secretAccessKey
-
   vmwatcher =
     port: vmwatcherPort
-    awsKey: credentials.awsKeys.vm_vmwatcher.accessKeyId
-    awsSecret: credentials.awsKeys.vm_vmwatcher.secretAccessKey
     kloudSecretKey: kloud.kloudSecretKey
     kloudAddr: kloud.address
     connectToKlient: options.vmwatcherConnectToKlient


minor additional tweaks -- far from perfect!

--- salvus/page/worksheet1.coffee
+++ salvus/page/worksheet1.coffee
@@ -26,7 +26,7 @@
             views.edit.find(".salvus-worksheet-edit-jsoneditor")[0],
             {change:() ->
                 views.edit.find('.btn').removeClass("disabled")
-                # Update formatter at most once every 2 seconds
+               # Update formatter at most once every 2 seconds
                 formatter = views.edit.data('formatter')
                 if not formatter._planned_updated?
                     formatter._planned_update = true
@@ -1125,8 +1125,8 @@
     worksheet1.find("a[href='#salvus-worksheet-edit-cancel']").button().click((e) -> worksheet_view(); return false)
     worksheet1.find("a[href='#salvus-worksheet-edit-save']").button().click((e) ->
         if edit_view_save_changes()
-            $(this).addClass('disabled')
-            worksheet_view()
+            views.edit.find('.btn').addClass("disabled")
+            worksheet_is_dirty()
         return false
     )


debugging methods

debugging methods

--- evolution.coffee
+++ evolution.coffee
@@ -213,7 +213,9 @@
   setTopic: ( x, y ) ->
     candidates = ( t for t in @thingsAt( x, y ) when t instanceof Animal )
     if candidates.length
-      @topic.topicColor  = null if @topic
+      if @topic
+        @highlightTopic false
+        @topic.topicColor = null
       @topic             = candidates[0]
       @topic.topicColor  = @outline
       @topic.inheritMark = false
@@ -535,10 +537,14 @@
             break
     @dead
   # set temporary outlines
-  highlightTopic: ->
+  highlightTopic: (turnOn=true) ->
     if @topic
-      color = @outline || 'yellow'
-      other.outline color for other in @topic.nearby()
+      others = @topic.nearby()
+      if turnOn
+        color = @outline || 'yellow'
+        other.outline color for other in others
+      else
+        other.outlined = false for other in others
   # paint a moment in time
   draw: ->
     @erase()
@@ -748,6 +754,24 @@
         ret.push t
         thing.others[t.id] ?= @universe.trig thing, t
     ret
+  # debugging methods
+  drawRadius: (radius) ->
+    n[0].draw 'grey', n[1] for n in @neighbors when n[1] <= radius
+    @draw 'black'
+  draw: (color='grey', noise...) ->
+    console.log 'cell', @x, @y, noise
+    @outline(color)
+    @showInhabitants()
+  outline: (color='grey') ->
+    ctx = @universe.ctx
+    ctx.rect @x, @y, @width, @width
+    ctx.strokeStyle = color
+    ctx.stroke()
+  showInhabitants: ->
+    color = @universe.outline
+    for t in @inhabitants
+      t.outline color
+      t.draw()

 # something in the universe
 class Thing


defaults function cant be bound

defaults function cant be bound

It is called from the constructor.

--- coffee/src/model.coffee
+++ coffee/src/model.coffee
@@ -10,7 +10,7 @@
       @acornid 'new'
     @

-  defaults: =>
+  defaults: ->
     shell: {shell: 'acorn.LinkShell'}

   idAttribute: 'acornid'


Add optional server and port

Add optional server and port

--- command-register.coffee
+++ command-register.coffee
@@ -1,15 +1,34 @@
 commander = require 'commander'
+_         = require 'lodash'
 meshblu   = require 'meshblu'
+url       = require 'url'
+
+DEFAULT_HOST = 'meshblu.octoblu.com'
+DEFAULT_PORT = 80

 class KeygenCommand
   parseOptions: =>
     commander
+      .option '-s, --server <host[:port]>', 'Meshblu host'
       .parse process.argv
+
+  parseConfig: =>
+    unless commander.server?
+      return {server: DEFAULT_HOST, port: DEFAULT_PORT}
+
+    server = commander.server
+    unless _.startsWith server, 'ws'
+      protocol = if port == 443 then 'wss://' else 'ws://'
+      server = protocol + server
+
+    {hostname, port} = url.parse server
+    port ?= 80
+    {server: hostname, port: port}

   run: =>
     @parseOptions()
-
-    @config = {server: 'meshblu.octoblu.com', port: 80, uuid: 'wrong'}
+    @config = @parseConfig()
+    @config.uuid = 'wrong' # to force a notReady
     @conn = meshblu.createConnection @config
     @conn.on 'notReady', @onReady


Make sure non visible axes are not considered for layout updates.

--- bokehjs/src/coffee/renderer/guide/axis.coffee
+++ bokehjs/src/coffee/renderer/guide/axis.coffee
@@ -361,6 +361,7 @@
   update_layout: (view, solver) ->
     if not @get('visible')
       size = 0
+      @_last_size = 0
     else
       size = (@_tick_extent(view) + @_tick_label_extent(view) +
               @_axis_label_extent(view))


coerce value to float

coerce value to float

--- configs/test/matchers.coffee
+++ configs/test/matchers.coffee
@@ -18,6 +18,7 @@
     failures = []
     utils = @utils
     testValue = (i, type, expected, actual) ->
+      actual = parseFloat(actual) unless typeof actual is 'number'
       if test.precision
         utils.ensureNumbers(actual, expected, "test index #{i}")
         pass = Math.abs(expected - actual) < Math.pow(10, -test.precision) / 2


Simplify null-checks in mapper

Simplify null-checks in mapper

--- src/liquid/context.coffee
+++ src/liquid/context.coffee
@@ -171,12 +171,12 @@
       return future.resolve(object) if parts.length == 0

       mapper = (part, next) =>
-        return next() if object == null
+        return next() unless object?

         Q.when(object).done (_object) =>
           object = @liquify(_object)

-          return next() if object == null
+          return next() unless object?

           bracketMatch = squareBracketed.exec(part)


ignore Expanded pods

ignore Expanded pods

--- commands/wolfram.coffee
+++ commands/wolfram.coffee
@@ -46,8 +46,9 @@
                 message = null

             try
-                pods = root.children.filter (node) -> node.name is 'pod' and node.attributes.id isnt 'Result'
+                pods = root.children
                 for pod in pods
+                    continue if /Result|UnitConversion|:Expanded/.test pod.attributes.id
                     if message.length > 800 then break
                     subpods = pod.children.filter (node) -> node.name is 'subpod'
                     for subpod in subpods


subtree branching spec & more hardcore spec for layout simulation with branching

--- spec/Thenable.coffee
+++ spec/Thenable.coffee
@@ -1228,7 +1228,7 @@
           chai.expect(res).to.eql [15]
           done()

-    describe 'contest via subbranching w/ abortions & branch order', ->
+    describe 'contest via 1 level multi-branch w/ abortions & branch order', ->
       it 'should resolve', (done) ->

         failedComponent = (n,d) ->
@@ -1256,9 +1256,9 @@
             c.branch 'three', (b, data) ->
               data * 3

-          # .maybe failedComponent
-          # .else (c,d) ->
-          #   d
+          .maybe failedComponent
+          .else (c,d) ->
+            d

           .then 'variation', (c, d) ->
             c.branch 'four', (b, data) ->
@@ -1282,6 +1282,81 @@
           results[0]
         .finally (c, res) ->
           chai.expect(res).to.eql [8]
+          done()
+
+
+    describe 'contest via sub-branch w/ abortions & branch order', ->
+      it 'should resolve', (done) ->
+
+        failedComponent = (n,d) ->
+          n.continue 'failedComponent'
+          .deliver()
+          .then (n) ->
+            n.abort('failedComponent aborted')
+
+        component = (n,d) ->
+          n.continue 'component'
+          .deliver d
+          .then failedComponent
+          .else (c,d) ->
+            d
+          .then (c, d) ->
+            d
+
+        section = (c, data) ->
+          c.tree 'multiply'
+          .deliver data
+
+          .then 'continue-branching', (c, d) ->
+            c.continue()
+            .deliver(d)
+            .then 'whatever variation', (c, d) ->
+              c.branch 'zero', (b, data) ->
+                data + '-zero'
+              c.branch 'one', (b, data) ->
+                data + '-one'
+
+          .then 'choice-branching', (c, d) ->
+            c.branch 'two', (b, data) ->
+              data + '-two'
+            c.branch 'three', (b, data) ->
+              data + '-three'
+
+          .then 'tree-branching', (c, d) ->
+            c.tree()
+            .deliver(d)
+            .then 'variation', (c, d) ->
+              c.branch 'four', (b, data) ->
+                data + '-four'
+              c.branch 'five', (b, data) ->
+                data + '-five'
+            .then 'subtree-branching', (c, d) ->
+              c.tree()
+              .deliver(d)
+              .then 'variation', (c, d) ->
+                c.branch 'six', (b, data) ->
+                  data + '-six'
+                c.branch 'seven', (b, data) ->
+                  data + '-seven'
+
+          #
+          # @bergie, uncommenting this & commennting out 'subtree-branching' makes this spec pass???
+          #
+          #.maybe failedComponent
+          #.else (c,d) ->
+          #  d
+
+           .all [component]
+           .then (n,results) ->
+             results[0]
+        Root()
+        .deliver "start"
+        .contest "contest-multiply", [
+          section
+        ], (c, results) ->
+          results[0]
+        .finally (c, res) ->
+          chai.expect(res).to.eql ['start-zero-two-four-six']
           done()

@@ -1653,13 +1728,16 @@
           .all components
           .else (n,d) ->
             n.abort('post: missing required component',d)
+          .then (n,d) ->
+            n.branch 'post-style-1', ->
+              'post-style-1'
+            n.branch 'post-style-2', ->
+              'post-style-2'

       section = (name,posts) ->
         return (n,d) ->
-          n.tree 'section'
+          n.tree name
           .deliver()
-          .then name, ->
-            true
           .then (c, d) ->
             c.branch 'left', ->
               d
@@ -1671,22 +1749,19 @@
             c.branch 'bottom', ->
               d
           .all posts
-          .then (n,results) ->
-            name
           .else (n,d) ->
             n.abort('section: posts failed',d)
-
-      imageSection = section('imageSection',[
-          post([image])
-        ])
-
-      textSection = section('textSection',[
-          post([text])
-        ])
-
-      videoTextSection = section('videoTextSection',[
-          post([video,text])
-        ])
+          .then (n,results) ->
+            return {
+              pathString:n.namedPath().join('-')
+              postResults:results
+            }
+
+      imageSection = section('imageSection',[post([image])])
+
+      textSection = section('textSection',[post([text])])
+
+      videoTextSection = section('videoTextSection',[post([video,text])])

       sections = [videoTextSection, imageSection, textSection]

@@ -1721,8 +1796,18 @@
         ]
       .then layout
       .finally (n, results) ->
+        console.log results
         chai.expect(results.length).to.equal 3
-        chai.expect(results).to.eql ['videoTextSection','textSection','imageSection']
+        pathStrings = results.map (r) ->
+          r.pathString
+        chai.expect(pathStrings).to.eql ['videoTextSection-left-top','textSection-left-top','imageSection-left-top']
+        postResults = results.map (r) ->
+          r.postResults
+        chai.expect(postResults).to.eql [
+          ['post-style-1']
+          ['post-style-1']
+          ['post-style-1']
+        ]
         done()

     it 'should work', (done) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/active_bounds.coffee
+++ src/active_bounds.coffee
@@ -4,7 +4,7 @@
     y: 0
     width: 8
     height: 8
-    activeBounds: Rectangle(0, 0, App.width, App,)
+    activeBounds: Rectangle(0, 0, App.width, App.height)

   position: (newPosition) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/active_bounds.coffee
+++ src/active_bounds.coffee
@@ -28,7 +28,7 @@

     # => bullet2 will be removed when it
          goes outside unless 30 <= I.x <= 130
-
+         and

 ActiveBounds module
 @name ActiveBounds


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/active_bounds.coffee
+++ src/active_bounds.coffee
@@ -11,5 +11,5 @@
       height: App.height

   self.bind 'update', ->
-    self.destroy() unless I.activeBounds.left <= I.x <= I.activeBounds.x + I.activeBounds.width
+    self.destroy() unless I.activeBounds.left <= I.x <= I.activeBounds.right
     self.destroy() unless I.activeBounds.y <= I.y <= I.activeBounds.y + I.activeBounds.height


pick media list

pick media list

--- electron/ui/editor.coffee
+++ electron/ui/editor.coffee
@@ -3,6 +3,13 @@
  'Wallapatta.Parser'
  'HLJS'
  (Base, CodeMirror, Parser, HLJS) ->
+
+  decodeURL = (url) ->
+   if window?.wallapattaDecodeURL?
+    return window.wallapattaDecodeURL url
+   else
+    return url
+

   class Editor  extends Base
    @extend()
@@ -328,12 +335,13 @@
      if n._path?
       path = n._path
       break
-     n = e.parentNode
+     n = n.parentNode

     if not path?
-     return @wrapSelection '[[', ']]'
-
-    @wrapSelection "[[#{path}]]", ''
+     return @addSegment '!image_url'
+
+    #@wrapSelection "[[#{path}]]", ''
+    return @addSegment "!#{path}(", ")"

    @listen 'pickMediaCancel', (e) ->
     @elems.pickMedia.style.display = 'none'
@@ -342,21 +350,23 @@

    pickMediaPane: ->
     if not @app?
-     return @addSegment '!'
+     return @addSegment '!image_url'
     resources = @app.getResources()
     if not resources?
-     return @addSegment '!'
+     return @addSegment '!image_url'

     @elems.pickMedia.style.display = 'block'
     @elems.preview.style.display = 'none'
     @elems.pickMediaList.innerHTML = ''

-    return
     Weya elem: @elems.pickMediaList, ->
      for path in resources
-      d = @div path
-      d._path = path
-
+      @li ".list-group-item", data: {_path: path}, ->
+       @img ".media-object.pull-left",
+        src: (decodeURL path)
+        width: 64
+       @div ".media-body", ->
+        @strong path


Fix build.

Fix build.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -3,7 +3,7 @@
 GulpEste = require 'gulp-este'
 runSequence = require 'run-sequence'
 taskBackup = require './tasks/backup.coffee'
-taskFixtures = require './tasks/fixtures.coffee'
+# taskFixtures = require './tasks/fixtures.coffee'
 yargs = require 'yargs'

 args = yargs
@@ -169,6 +169,6 @@
 gulp.task 'bump', (done) ->
   este.bump './*.json', yargs, done

-gulp.task 'fixtures', taskFixtures
+# gulp.task 'fixtures', taskFixtures

 gulp.task 'backup', taskBackup


Fix gulp task 'compile'

Fix gulp task 'compile'

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -82,7 +82,7 @@
     .pipe gp.plumber()
     .pipe g.dest('public/font/')

-g.task 'compile', ['js-dev', 'json', 'css', 'img', 'font']
+g.task 'compile', ['js-prod', 'json', 'css', 'img', 'font']

 g.task 'default', ['init', 'compile'], ->
   g.watch 'src/app/**', ->


builds 32bit deb using right name

builds 32bit deb using right name

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -425,12 +425,10 @@

         if arch is 'ia32'
             options.arch = 'i386'
-            archNameSuffix = 'ia32'
         else
             options.arch = 'amd64'
-            archNameSuffix = 'amd64'
-
-        options.rename = (dest, src) -> path.join(dest, "#{json.name}-#{json.version}-linux-#{archNameSuffix}.deb")
+
+        options.rename = (dest, src) -> path.join(dest, "#{json.name}-#{json.version}-linux-#{options.arch}.deb")
         debian options

     gulp.task "deploy:linux-#{arch}:deb",


See #8: Add Java implementation of initial page access

Task-Url: https://bitbucket.org/pughlab/tracker-java-restlet/issue/8/

Added an extra /statics to the client-side code, except index.html, to
make redirection just that much simpler

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -52,7 +52,7 @@
 gulp.task 'coffee', () ->
   gulp.src ['./src/main/client/**/*.*coffee']
     .pipe gulpCoffee()
-    .pipe gulp.dest('./target/client/tmp/app/js')
+    .pipe gulp.dest('./target/client/tmp/statics/app/js')

 gulp.task 'bootstrap', () ->
@@ -63,9 +63,9 @@
     bootstrapStream
       .pipe gulpLess({paths: lessPaths, verbose: true})
       .pipe gulpRename('bootstrap.css')
-      .pipe gulp.dest('./target/client/tmp/vendors/css')
+      .pipe gulp.dest('./target/client/tmp/statics/vendors/css')
     fontsStream
-      .pipe gulp.dest('./target/client/tmp/app/statics/fonts')
+      .pipe gulp.dest('./target/client/tmp/statics/fonts')
   )

@@ -83,16 +83,16 @@
   gulp
     .src './src/main/client/app.less'
     .pipe gulpLess {paths: lessPaths}
-    .pipe gulp.dest './target/client/tmp/app/css/'
+    .pipe gulp.dest './target/client/tmp/statics/app/css/'

 gulp.task 'vendors', () ->
   bowerStream = gulp.src(mainBowerFiles(), {base: 'bower_components'}).pipe(gulpIgnore.exclude('bower_components/bootstrap/**/*.*'))
   es.merge(
     bowerStream.pipe(gulpFilter('**/*.css'))
-      .pipe(gulp.dest('./target/client/tmp/vendors/css'))
+      .pipe(gulp.dest('./target/client/tmp/statics/vendors/css'))
     bowerStream.pipe(gulpFilter('**/*.js'))
-      .pipe(gulp.dest('./target/client/tmp/vendors/js')))
+      .pipe(gulp.dest('./target/client/tmp/statics/vendors/js')))

 index = () ->
@@ -101,14 +101,14 @@
   cssVendorFiles = bowerStream
     .pipe(gulpIgnore.exclude('bower_components/bootstrap/**/*.*'))
     .pipe(gulpFilter('**/*.css'))
-    .pipe(gulp.dest('./vendors/css'))
+    .pipe(gulp.dest('./target/client/tmp/statics/vendors/css'))

   jsVendorFiles = bowerStream
     .pipe(gulpFilter('**/*.js'))
-    .pipe(gulp.dest('./vendors/js'))
+    .pipe(gulp.dest('./target/client/tmp/statics/vendors/js'))

   gulp.src('./src/main/client/index.html')
-    .pipe(gulpInject(gulp.src('./target/client/tmp/vendors/css/bootstrap.css'), {ignorePath: ['target/client/tmp'], starttag: '<!-- inject:bootstrap:{{ext}} -->'}))
+    .pipe(gulpInject(gulp.src('./target/client/tmp/statics/vendors/css/bootstrap.css'), {ignorePath: ['target/client/tmp'], starttag: '<!-- inject:bootstrap:{{ext}} -->'}))
     .pipe(gulpInject(es.merge(jsVendorFiles, cssVendorFiles), {ignorePath: ['target/client/tmp'], starttag: '<!-- inject:vendor:{{ext}} -->'}))
     .pipe(gulpInject(es.merge(appFiles(), cssFiles()), {ignorePath: ['target/client/tmp', 'src/main/client']}))
     .pipe(gulp.dest('./target/client/tmp/'))
@@ -131,7 +131,7 @@
       stripPrefix: '/src/app'
     })
     .pipe(gulpConcat, bower.name + '-templates.js')
-    .pipe(gulp.dest, './target/client/tmp/app/js')()
+    .pipe(gulp.dest, './target/client/tmp/statics/app/js')()

 gulp.task 'build-all', ['styles', 'bootstrap', 'templates', 'coffee', 'vendors', 'assets'], index
@@ -142,15 +142,15 @@
     .pipe(if opt and opt.min then gulpHtmlmin(htmlminOpts) else gulpUtil.noop())

 appVendorFiles = () ->
-  gulp.src('./target/client/tmp/vendors/**/*.js')
+  gulp.src('./target/client/tmp/statics/vendors/**/*.js')

 cssVendorFiles = () ->
-  gulp.src('./target/client/tmp/vendors/css/**/*.css')
+  gulp.src('./target/client/tmp/statics/vendors/css/**/*.css')

 appFiles = () ->
   files = [
-    './target/client/tmp/app/' + bower.name + '-templates.js',
-    './target/client/tmp/app/**/*.js',
+    './target/client/tmp/statics/app/' + bower.name + '-templates.js',
+    './target/client/tmp/statics/app/**/*.js',
     '!./target/client/tmp/**/*_test.js'
   ]
   gulp.src(files)
@@ -158,7 +158,7 @@
     .pipe gulpAngularFilesort()

 cssFiles = (opt) ->
-  gulp.src('./target/client/tmp/app/css/**/*.css', opt)
+  gulp.src('./target/client/tmp/statics/app/css/**/*.css', opt)
     .pipe(gulpOrder([
-      'target/client/tmp/app/css/app.css'
+      'target/client/tmp/statics/app/css/app.css'
     ], {base: '.'}))


support for sub folders in jade

support for sub folders in jade

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -41,7 +41,7 @@
       console.error "Error loading comparisons tree", err
       return

-    gulp.src('./jade/*')
+    gulp.src('./jade/**/*.jade')
       .pipe(jade({pretty: true, data: {comparisons, titleCase, getNamePart, fullLanguage}}))
       .pipe(gulp.dest('./'))

@@ -55,5 +55,5 @@
   gulp.watch './styl/*', ->
     gulp.run 'stylus'

-  gulp.watch ['./jade/*', './comparisons/**/*'], ->
+  gulp.watch ['./jade/**/*.jade', './comparisons/**/*'], ->
     gulp.run 'jade'


added baseUrl argument to pane options

added baseUrl argument to pane options

--- lib/Pane.coffee
+++ lib/Pane.coffee
@@ -8,7 +8,7 @@
     @window.initialize()

     if opt?
-      {js, html, url, height, width, resizable, title, fullscreen, maximized, minimized} = opt
+      {js, html, baseUrl, url, height, width, resizable, title, fullscreen, maximized, minimized} = opt
       @window.resize height, width if height? and width?
       @window.maximize maximized if maximized?
       @window.minimize minimized if minimized?
@@ -17,7 +17,8 @@
       @window.title title if title?

       @window.url url if url?
-      @window.html html if html?
+      @window.html html, baseUrl if html? and baseUrl?
+      @window.html html if html? and not baseUrl?
       @window.execute js if js?
     @window.processEvents()
     return @window


remove dynamic

remove dynamic

--- src/test/services/dynamic.coffee
+++ src/test/services/dynamic.coffee
@@ -1,67 +0,0 @@
-##
-## dynamic.coffee
-##
-## Created by Frederic DELBOS - fred.delbos@gmail.com on Dec 11 2013.
-## This file is subject to the terms and conditions defined in
-## file 'LICENSE.txt', which is part of this source code package.
-##
-
-# describe 'dynamic directive tests', ->
-
-#   beforeEach(module('CoolFormServices'))
-
-#   form =
-#     name: "form"
-#     action: "/"
-#     dependencies: [
-#       type: 'directive'
-#       field_type: 'text'
-#       name: 'test-text'
-#       ]
-#     pages: [
-#       lines:[
-#         fields:[
-#           {
-#             name: "field"
-#             type: "text"
-#             label: "field"
-#             validation: [
-#               validator:"email"
-#               options: [
-#                 message: "not an email"
-#                 ]
-#               ]
-#             }
-#           ]
-#         ]
-#       ]
-
-#   # directiveLoaded = false
-#   # angular.module('TestModule', []).directive('testText', ->
-#   #     l = (scope) ->
-#   #       handlers =
-#   #         change: (nVal) ->
-#   #           if scope.value != nVal then scope.value = nVal
-#   #       dump "hello"
-#   #       directiveLoaded = true
-#   #       setValue = scope.service.registerField(scope.field.name, handlers)
-#   #       scope.value = if scope.field.value then value else ""
-#   #       scope.$watch('value', (v, o) -> setValue(v))
-
-
-#   #     return {
-#   #       restrict: 'E'
-#   #       scope:
-#   #         field: '='
-#   #         service: '='
-#   #       template: """<input type="text" ng-model="value"/>"""
-#   #       link: l
-#   #     }
-#   #   )
-
-#   it 'load directive and override default text input', inject((registrationService) ->
-#     service = registrationService(form)
-#     expect(service.directives.get('text')).toEqual('test-text')
-#   )
-
-


CSRF token can be passed fixes #399

CSRF token can be passed fixes #399

--- wzk/net/AuthMiddleware.coffee
+++ wzk/net/AuthMiddleware.coffee
@@ -1,4 +1,8 @@
+goog.require 'goog.array'
+goog.require 'goog.dom.dataset'
 goog.require 'goog.net.Cookies'
+goog.require 'goog.json'
+

 class wzk.net.AuthMiddleware extends wzk.net.HeadersMiddleware

@@ -20,17 +24,46 @@
   ###
   constructor: (doc) ->
     super()
-    @cookies = new goog.net.Cookies doc
+    @cookies = new goog.net.Cookies(doc)
+    @csrf = @parseCSRFFromInitialData(doc)

   ###*
     @param {Object} headers
   ###
   apply: (headers) ->
-    csrf = @cookies.get wzk.net.AuthMiddleware.COOKIES.CSRF
-    if csrf
-      headers[wzk.net.AuthMiddleware.HEADER.CSRF] = csrf
+    csrfs = (token for token in [@cookies.get(wzk.net.AuthMiddleware.COOKIES.CSRF), @csrf] when token?)
+    if not goog.array.isEmpty(csrfs)
+      headers[wzk.net.AuthMiddleware.HEADER.CSRF] = csrfs[0]
     else
       auth  = @cookies.get wzk.net.AuthMiddleware.HEADER.AUTH
       if auth?
         headers[wzk.net.AuthMiddleware.HEADER.AUTH] = auth
     undefined # Coffee & Closure
+
+  ###*
+    @protected
+    @param {string?} str
+    @return {boolean}
+  ###
+  isBlank: (str) ->
+    not str? or str is ''
+
+  ###*
+    @protected
+    @param {Element?} el
+    @return {boolean}
+  ###
+  hasInitialData: (el) ->
+    el? and not @isBlank(goog.dom.dataset.get(el, 'initial'))
+
+  ###*
+    @protected
+    @param {Document} doc
+    @return {string?}
+  ###
+  parseCSRFFromInitialData: (doc) ->
+    elements = (el for el in [document.body, document.getElementById('wrapper')] when @hasInitialData(el))
+    if goog.array.isEmpty(elements)
+      return null
+    else
+      return goog.json.parse(goog.dom.dataset.get(elements[0], 'initial'))?['net']?['csrf-token']


Avoid traversing through shadow root on mousewheel events

--- src/text-editor-component.coffee
+++ src/text-editor-component.coffee
@@ -908,10 +908,10 @@
   lineNumberNodeForScreenRow: (screenRow) -> @refs.gutter.lineNumberNodeForScreenRow(screenRow)

   screenRowForNode: (node) ->
-    while node isnt document
+    while node?
       if screenRow = node.dataset.screenRow
         return parseInt(screenRow)
-      node = node.parentNode
+      node = node.parentElement
     null

   getFontSize: ->


neater filter

neater filter

--- urb/zod/pub/work/src/js/components/FilterComponent.coffee
+++ urb/zod/pub/work/src/js/components/FilterComponent.coffee
@@ -14,45 +14,24 @@
     txt = $t.find('.input').text().trim()
     key = $t.attr('data-key')
     if txt.length is 0 then txt = null
-    if key is 'audience' then txt = txt.split " "
-    if key is 'tags' then txt = [txt]
+    else switch key
+      when 'audience' then txt = txt.split " "
+      when 'tags'     then txt = [txt]
     @props.onChange key,txt
+
+  fields: [ {filter:'owned',   key:'owner',    title: 'Owned by:'},
+            {filter:'tag',     key:'tags',     title: 'Tag:'},
+            {filter:'channel', key:'audience', title: 'Audience:'},
+            {filter:'status',  key:'status',   title: 'Status:'} ]
+
   render: ->
-    (div {className:'filters'}, [
-      (div {className:'owned filter ib','data-key':'owner'}, [
-        (label {}, 'Owned by:')
+    (div {className:'filters'}, @fields.map ({filter,key,title})=>
+      (div {key, 'data-key':key, className:"#{filter} filter ib"}, [
+        (label {}, title)
         (div {
             contentEditable:true
             className:'input ib'
             onKeyDown:@_onKeyDown
             onBlur:@_onBlur
-            },@props.filters.owned)
-      ])
-      (div {className:'tag filter ib','data-key':'tags'}, [
-        (label {}, 'Tag:')
-        (div {
-            contentEditable:true
-            className:'input ib'
-            onKeyDown:@_onKeyDown
-            onBlur:@_onBlur
-            },@props.filters.tag)
-      ])
-      (div {className:'channel filter ib','data-key':'audience'}, [
-        (label {}, 'Audience:')
-        (div {
-            contentEditable:true
-            className:'input ib'
-            onKeyDown:@_onKeyDown
-            onBlur:@_onBlur
-            },@props.filters.channel)
-      ])
-      (div {className:'status filter ib','data-key':'status'}, [
-        (label {}, 'Status:')
-        (div {
-            contentEditable:true
-            className:'input ib'
-            onKeyDown:@_onKeyDown
-            onBlur:@_onBlur
-            },@props.filters.status)
-      ])
-    ])+            },@props.filters[filter])
+      ]))


Added more typeof matchers. Added all-purpose inclusion matcher. Improved display of values in error messages.

--- lib/Spec.coffee
+++ lib/Spec.coffee
@@ -11,6 +11,39 @@
   # Escapes text for HTML
   Escape: (string) ->
     $('<div/>').text(String(string)).html()
+
+  Display: (object) ->
+    if object instanceof Array
+      s = '['
+      first = true
+      for item in object
+        if first
+          first = false
+        else
+          first += ', '
+        s += '“' + @Escape(String(item)) + '”'
+      s + ']'
+    else if object is null
+      'null'
+    else if object is undefined
+      'undefined'
+    else if object is true
+      'true'
+    else if object is false
+      'false'
+    else if typeof object == 'object'
+      s = "{"
+      first = true
+      for key of object
+        if object.hasOwnProperty key
+          if first
+            first = false
+          else
+            s += ", "
+          s += @Escape(key) + ': “' + @Escape(String(object[key])) + '”'
+      s + "}"
+    else
+      "“#{@Escape(object)}”"

   # Executes a test case
   describe: (title, definition) ->
@@ -250,19 +283,35 @@
     # Tests if matched value is a function
     window.beAFunction = (value) ->
       [typeof value is 'function', "to have type “function”, actual “#{typeof value}”"]
+
+    # Tests if matched value is a string
+    window.beAString = (value) ->
+      [typeof value is 'string', "to have type “string”, actual “#{typeof value}”"]
+
+    # Tests if matched value is a number
+    window.beANumber = (value) ->
+      [typeof value is 'number', "to have type “number”, actual “#{typeof value}”"]
+
+    # Tests if matched value is a boolean
+    window.beABoolean = (value) ->
+      [typeof value is 'boolean', "to have type “boolean”, actual “#{typeof value}”"]
+
+    # Tests if matched value is an object
+    window.beAnObject = (value) ->
+      [typeof value is 'object', "to have type “object”, actual “#{typeof value}”"]

     # Tests if matched value === expected value
     window.be = (expected) ->
       (value) ->
-        [value is expected, "to be “#{Spec.Escape expected}”, actual “#{Spec.Escape value}”"]
+        [value is expected, "to be #{Spec.Display expected}, actual #{Spec.Display value}"]

     # Tests if matched value is boolean true
     window.beTrue = (value) ->
-      [String(value) == 'true', "to be true, got “#{Spec.Escape value}”"]
+      [String(value) == 'true', "to be true, got #{Spec.Display value}"]

     # Tests if matched value is boolean false
     window.beFalse = (value) ->
-      [String(value) == 'false', "to be false, got “#{Spec.Escape value}”"]
+      [String(value) == 'false', "to be false, got #{Spec.Display value}"]

     # Tests if matched value is an instance of class
     window.beAnInstanceOf = (klass) ->
@@ -272,7 +321,28 @@
     # Tests if matched value == expected value
     window.equal = (expected) ->
       (value) ->
-        [String(value) == String(expected), "to equal “#{Spec.Escape expected}”, actual “#{Spec.Escape value}”"]
+        [String(value) == String(expected), "to equal #{Spec.Display String(expected)}, actual #{Spec.Display String(value)}"]
+
+    # All-purpose inclusion matcher
+    window.include = (expected) ->
+      if expected instanceof Array
+        (value) ->
+          match = true
+          for test in expected
+            match = false unless (value.indexOf && value.indexOf(test) >= 0) || value[test]?
+          [match, "to include #{Spec.Display expected}, actual #{Spec.Display value}"]
+      else if typeof expected == 'object'
+        (value) ->
+          missing = {}
+          match = true
+          for test of expected
+            if expected.hasOwnProperty test
+              unless value[test] isnt undefined && String(value[test]) == String(expected[test])
+                match = false
+                missing[test] = expected[test]
+          [match, "to include #{Spec.Display expected}, actual #{Spec.Display value}, missing #{Spec.Display missing}"]
+      else
+        include([expected])

   # Fails test, with an error message
   fail: (message) ->
@@ -302,9 +372,13 @@
     delete window.context
     delete window.it
     delete window.beAFunction
-    delete window.be
+    delete window.beAString
+    delete window.beANumber
+    delete window.beABoolean
+    delete window.beAnObject
     delete window.beTrue
     delete window.beFalse
     delete window.beAnInstanceOf
     delete window.equal
+    delete window.include
 }


Add one parameter test

Add one parameter test

--- test/test.coffee
+++ test/test.coffee
@@ -4,6 +4,7 @@
 describe 'existential default ::', ->

   describe 'behavior', ->
+
     it "don't modify input values", ->
       expected =
         foo: 'bar'
@@ -20,6 +21,10 @@
       result.should.be.eql expected
       value1.should.be.eql value1Copy
       value2.should.be.eql value2Copy
+
+    it 'works with one parameter', ->
+      existsAssign('hello world').should.be.equal 'hello world'
+      existsAssign(foo: 'bar').should.be.eql foo: 'bar'

     it 'works with more than one source', ->
       existsAssign(null, undefined, 'hello world').should.be.equal 'hello world'


Microfix

Microfix

--- test/test.coffee
+++ test/test.coffee
@@ -14,7 +14,7 @@
   style: fs.readFileSync stylePath, 'utf-8'
   answer: fs.readFileSync answerPath, 'utf-8'

-describe 'postcss-cson-cssvars', ->
+describe 'postcss-namespace', ->
   it 'expect concat chain token', ->
     {style, answer} = set 'chain'


Use resetting of fragment, as this works more reliably

--- core/app/assets/javascripts/intermediate.coffee
+++ core/app/assets/javascripts/intermediate.coffee
@@ -40,8 +40,8 @@
   backbone = showFrame.contentWindow.Backbone
   history = backbone?.history
   if history && backbone.History.started
-    history.loadUrl url # Force loading the url, even if already showing that url
-    history.navigate url
+    history.fragment = null # Force loading the url, even if already showing that url
+    history.navigate url, true
   else
     showFrame.onload = -> window.onModalReady()
     showFrame.src = url


Update test cases.

Update test cases.

--- test/deploy-test.coffee
+++ test/deploy-test.coffee
@@ -1,18 +1,91 @@
 chai = require 'chai'
-sinon = require 'sinon'
+sinon  = require 'sinon'
 chai.use require 'sinon-chai'
+expect = require('chai').expect
+path   = require 'path'

-expect = chai.expect
+Robot       = require 'hubot/src/robot'
+TextMessage = require('hubot/src/message').TextMessage

-describe 'deploy', ->
-  beforeEach ->
-    @robot =
-      respond: sinon.spy()
-      hear: sinon.spy()
-      logger:
-        warning: sinon.spy()
+CONFIG = """
+{
+  "staging": {
+    "job": "deploy-staging",
+    "role": "*"
+  },
+  "production": {
+    "job": "deploy-production",
+    "role": "deploy"
+  },
+  "image": {
+    "job": "build-image",
+    "role": "*"
+  }
+}
+"""

-    require('../src/deploy')(@robot)
+describe 'auth', ->
+  robot = {}
+  adminUser = {}
+  roleUser = {}
+  adapter = {}

-  it 'registers a deploy/build listener', ->
-    expect(@robot.respond).to.have.been.calledWith(/(deploy|build) ([\w\.\-_]+) (.+)?/i)
+  beforeEach (done) ->
+    # Fake environment variables
+    process.env.HUBOT_AUTH_ADMIN = "1"
+    process.env.HUBOT_DEPLOY_CONFIG = CONFIG
+
+    # Create new robot, without http, using mock adapter
+    robot = new Robot null, "mock-adapter", false
+
+    # Fake Jenkins
+    robot.jenkins =
+      build: sinon.spy()
+
+    robot.adapter.on "connected", ->
+      # load modules and configure it for the robot. This is in place of
+      # external-scripts
+      require(path.resolve path.join("node_modules/hubot-auth/src"), "auth")(@robot)
+      require('../src/deploy')(@robot)
+
+      adminUser = robot.brain.userForId "1", {
+        name: "admin-user"
+        room: "#test"
+      }
+
+      roleUser = robot.brain.userForId "2", {
+        name: "role-user"
+        room: "#test"
+        roles: [
+          'deploy'
+        ]
+      }
+
+      adapter = robot.adapter
+
+    robot.run()
+
+    done()
+
+  afterEach ->
+    robot.shutdown()
+
+  it 'deploy master to staging', (done) ->
+    adapter.receive(new TextMessage adminUser, "hubot deploy staging master")
+    expect(robot.jenkins.build).to.be.calledOnce
+    done()
+
+  it 'deploy master to production', (done) ->
+    adapter.receive(new TextMessage roleUser, "hubot deploy production master")
+    expect(robot.jenkins.build).to.be.calledOnce
+    done()
+
+  it 'unauthorized deploy to production', (done) ->
+    adapter.receive(new TextMessage adminUser, "hubot deploy production master")
+    expect(robot.jenkins.build).to.be.not.called
+    done()
+
+  it 'build image', (done) ->
+    adapter.receive(new TextMessage adminUser, "hubot build image master")
+    expect(robot.jenkins.build).to.be.calledOnce
+    done()


[skip] Fixed a misnamed method (1m)

[skip] Fixed a misnamed method (1m)

--- vendor/assets/javascripts/neat/renderer/basic.coffee
+++ vendor/assets/javascripts/neat/renderer/basic.coffee
@@ -70,16 +70,16 @@

   _collectionHasBeenReset: ->
-    @render()
+    @_render()

   _collectionHasBeenSorted: ->
-    @render()
+    @_render()

   _modelHasBeenAddedToCollection: ->
-    @render()
+    @_render()

   _modelHasBeenRemovedFromCollection: ->
-    @render()
+    @_render()

   _modelHasBeenChanged: ->
-    @render()
+    @_render()


Add number input type to modla dialog form (used for batch actions)

--- app/assets/javascripts/active_admin/lib/modal_dialog.js.coffee
+++ app/assets/javascripts/active_admin/lib/modal_dialog.js.coffee
@@ -1,7 +1,7 @@
 ActiveAdmin.modal_dialog = (message, inputs, callback)->
   html = """<form id="dialog_confirm" title="#{message}"><ul>"""
   for name, type of inputs
-    if /^(datepicker|checkbox|text)$/.test type
+    if /^(datepicker|checkbox|text|number)$/.test type
       wrapper = 'input'
     else if type is 'textarea'
       wrapper = 'textarea'


Fix delete operation, and prevent duplicate change when applying remote operations

--- app/assets/javascripts/collaborate/adapters/ace_adapter.coffee
+++ app/assets/javascripts/collaborate/adapters/ace_adapter.coffee
@@ -1,6 +1,8 @@
 # Parts of this inspired by
 # https://github.com/Operational-Transformation/ot.js/blob/c4f27a/lib/codemirror-adapter.js
 window.Collaborate.Adapters.AceAdapter = class AceAdapter
+  applyingRemoteOperation: false
+
   constructor: (@collaborativeAttribute, @editor) ->
     @collaborativeAttribute.on 'remoteOperation', @applyRemoteOperation

@@ -14,12 +16,15 @@
   # We want to generate an operation that describes the change, and send it off
   # to our server.
   textChange: (e) =>
+    # Ace fires a change event when we insert or delete text as well.
+    return if @applyingRemoteOperation
+
     startIndex = @document.positionToIndex(e.start)
     endIndex = @document.positionToIndex(e.end)
     documentLength = @document.getValue().length
+    changedText = e.lines.join('\n')
+
     endRetain = documentLength - endIndex
-
-    changedText = e.lines.join('\n')

     operation = new ot.TextOperation()

@@ -27,16 +32,19 @@
       when 'insert'
         operation.retain(startIndex).insert(changedText).retain(endRetain)
       when 'remove'
-        operation.retain(startIndex).delete(changedText).retain(endRetain)
+        removedLength = changedText.length
+        operation.retain(startIndex).delete(removedLength).retain(endRetain + removedLength)

     @collaborativeAttribute.localOperation(operation)

   applyRemoteOperation: (operation) =>
-    Range = ace.require('ace/range')
+    Range = ace.require('ace/range').Range

     ops = operation.ops

     cursor = 0
+
+    @applyingRemoteOperation = true

     for op in ops
       if ot.TextOperation.isRetain(op)
@@ -49,10 +57,14 @@
         cursor += op.length
       else if ot.TextOperation.isDelete(op)
         startPosition = @document.indexToPosition(cursor)
-        endPosition = @document.indexToPosition(cursor + op)
+        # op is the negative number of places to delete
+        endPosition = @document.indexToPosition(cursor - op)
+
         deleteRange = new Range(startPosition.row, startPosition.column, endPosition.row, endPosition.column)

         @session.remove(deleteRange)

         # Op is negative
         cursor += op
+
+    @applyingRemoteOperation = false


progress bar fill was disabled, this uncomments it

--- app/controllers/home_page.coffee
+++ app/controllers/home_page.coffee
@@ -24,10 +24,10 @@
       el: @el.find '.recents .image-changer'
       sources: []

-    # Api.get '/projects/serengeti', (data) =>
-    #   @classificationCount.html data.classification_count
-    #   @userCount.html data.user_count
-    #   @progressFill.width "#{100 * (data.complete_count / @totalSubjects)}%"
+    Api.get '/projects/serengeti', (data) =>
+      @classificationCount.html data.classification_count
+      @userCount.html data.user_count
+      @progressFill.width "#{100 * (data.complete_count / @totalSubjects)}%"

     Api.get '/projects/serengeti/recents', (recents) =>
       # Prefer to show favorites


added comment about flushing to track changes

--- app/coffee/DocumentManager.coffee
+++ app/coffee/DocumentManager.coffee
@@ -100,6 +100,11 @@

 		DocumentManager.flushDocIfLoaded project_id, doc_id, (error) ->
 			return callback(error) if error?
+			# We should flush pending ops to track-changes here but this is
+			# already done in the real-time WebsocketController.leaveProject
+			# method so we leave it there.  Note, if you ever add the flush
+			# in here be sure to do it in the background because it can take
+			# a long time.
 			RedisManager.removeDocFromMemory project_id, doc_id, (error) ->
 				return callback(error) if error?
 				callback null


snooze the for is optionnal

snooze the for is optionnal

--- scripts/pagerv2_commands.coffee
+++ scripts/pagerv2_commands.coffee
@@ -332,7 +332,7 @@

 #   hubot pager snooze <#,#,#> [for] [<duration>] [min] - snoozes incident <number> for [<duration>] (default 120m)
   robot.respond (
-    /pager snooze #?([^ ]+)(?: (?:for )(\d+)(?: min(?:utes)?)?)?\s*$/
+    /pager snooze #?([^ ]+)(?: (?:for )?(\d+)(?: min(?:utes)?)?)?\s*$/
   ), 'pager_snooze_one', (res) ->
     [ _, incidents, duration ] = res.match
     pagerv2.getPermission(res.envelope.user, 'pageruser')


Remove thumbnail, add map link

Remove thumbnail, add map link

--- src/bcycle-finder.coffee
+++ src/bcycle-finder.coffee
@@ -190,6 +190,7 @@
     status = if station.is_renting == 1 then 'Active' else 'Inactive'
     stationName = formatStationName station
     stationColor = if station.is_renting == 1 then 'good' else 'danger'
+    stationMapLink = 'https://maps.google.com/?q=' + encodeURIComponent("#{station.address}, #{config.city}")

     switch robot.adapterName
       when 'slack'
@@ -197,14 +198,13 @@
           fallback: "#{stationName} > #{status} | Bikes: #{station.num_bikes_available} | Docks: #{station.num_docks_available}",
           title: stationName,
           color: stationColor,
-          thumb_url: 'https://github.com/bcycle.png',
           author_name: 'BCycle',
           author_link: "https://#{config.city}.bcycle.com/",
           author_icon: "https://github.com/bcycle.png",
           fields: [
             {
               title: "Address",
-              value: station.address,
+              value: ":world_map: <#{stationMapLink}|#{station.address}>",
               short: false
             }
             {


Fix typo

Fix typo

--- lib/haskell-ghc-mod-message.coffee
+++ lib/haskell-ghc-mod-message.coffee
@@ -11,7 +11,7 @@
     @emitter.emit 'did-change-message', name

   destroy: ->
-    @emtitter?.destroy()
+    @emitter.destroy()

 class HaskellGhcModMessageElement extends HTMLElement
   setModel: (@model) ->


JVerificationToken: code tidy up

JVerificationToken: code tidy up

--- workers/social/lib/social/models/verificationtoken.coffee
+++ workers/social/lib/social/models/verificationtoken.coffee
@@ -54,22 +54,17 @@
       @one {username, action}, (err, confirmation)=>

         if confirmation
+          createdAt = Math.round((Date.now()-confirmation.createdAt)/60000)

-          if Math.round((Date.now()-confirmation.createdAt)/60000) < 20
-
-            unless resendIfExists
-
+          if createdAt < 20 # min
+            if resendIfExists
+              confirmation.sendEmail {subject, firstName}, callback
+            else
               callback if err then err else new PINExistsError \
                 "PIN exists and not expired,
                 try again after 20 min for new PIN."

-
-            else
-
-              confirmation.sendEmail {subject, firstName}, callback
-
             return
-

         # Remove all waiting pins for given action and email
         @remove {username, action}, (err, count)->
@@ -102,7 +97,7 @@
         callback null, Math.round((Date.now()-confirmation.createdAt)/60000) < 20
         confirmation.remove()
       else
-        callback null
+        callback null, false

   sendEmail: ({subject, firstName}, callback)->
@@ -115,6 +110,8 @@
       subject : subject
       content : @getTextBody firstName, @pin
       force   : yes
+
+    console.log "Pin (#{@pin}) sent to #{@email} for #{@action} action."

     email.save callback


Add a +1

Add a +1

--- scripts/seal-of-approval.coffee
+++ scripts/seal-of-approval.coffee
@@ -18,7 +18,8 @@
  "http://i.imgur.com/eyo3k.jpg",
  "http://i.imgur.com/xpGee.gif",
  "http://i.imgur.com/PtlSV.gif",
- "http://mlkshk.com/r/AHDT.gif"
+ "http://mlkshk.com/r/AHDT.gif",
+ "http://mlkshk.com/r/CIUZ.gif"
 ]

 module.exports = (robot) ->


Reduce ornithological badge breakpoints

Reduce ornithological badge breakpoints

--- app/lib/badge-definitions.coffee
+++ app/lib/badge-definitions.coffee
@@ -199,24 +199,24 @@
   ,
     name: 'Fledgling'
     url: 'badges/ornithology/fledgling.png'
-    description: 'You earn this badge for transcribing 100 Ornithological records'
-    awardText: 'You earned this badge for transcribing 100 Ornithological records'
-    condition:
-      func: (details) ->
-        if details.user.project.groups?[details.archive.id]?
-          return details.user.project.rows_transcribed >= 100
+    description: 'You earn this badge for transcribing 25 Ornithological records'
+    awardText: 'You earned this badge for transcribing 25 Ornithological records'
+    condition:
+      func: (details) ->
+        if details.user.project.groups?[details.archive.id]?
+          return details.user.project.rows_transcribed >= 25
         else
           return false
     collection: 'ornithological'
   ,
     name: 'Adult Bird'
     url: 'badges/ornithology/adult.png'
-    description: 'You earn this badge for transcribing 500 Ornithological records'
-    awardText: 'You earned this badge for transcribing 500 Ornithological records'
-    condition:
-      func: (details) ->
-        if details.user.project.groups?[details.archive.id]?
-          return details.user.project.rows_transcribed >= 500
+    description: 'You earn this badge for transcribing 200 Ornithological records'
+    awardText: 'You earned this badge for transcribing 200 Ornithological records'
+    condition:
+      func: (details) ->
+        if details.user.project.groups?[details.archive.id]?
+          return details.user.project.rows_transcribed >= 200
         else
           return false
     collection: 'ornithological'


Fix spectate mode

Fix spectate mode

--- server/handlers/level_handler.coffee
+++ server/handlers/level_handler.coffee
@@ -232,7 +232,7 @@
       findTop20Players = (sessionQueryParams, team, cb) ->
         sessionQueryParams['team'] = team
         aggregate = Session.aggregate [
-          {$match: sessionQueryParams}
+          {$match: _.clone(sessionQueryParams)}
           {$sort: {'totalScore': -1}}
           {$limit: 20}
           {$project: {'totalScore': 1}}


embedly: deal with special extension

embedly: deal with special extension

--- server/lib/server/image_cache.coffee
+++ server/lib/server/image_cache.coffee
@@ -28,6 +28,11 @@
   ext      = splitUrl.pop()
   noExt    = splitUrl.join(".")

+  # deal with extensions like:
+  #   'com/LbobbpWTGJSa45Mhrb6g_y3YjLn5OthdnugrHZJQqom1eduFCnFmqdmOOZmUttP8hLg=h310'
+  if ext.indexOf("com") > -1
+    ext = "com"
+
   md5      = crypto.createHash("md5").update(noExt).digest("hex")
   filename = "#{imagePath}/#{md5}.#{ext}"


quickScale option for thangs to avoid elastic animation (#6562)

Co-authored-by: Valentin Briukhanov <1c97702c869e5316729111bfe70e91c2a0718c7e@Valentins-MacBook-Air.local>

--- app/lib/surface/Lank.coco.coffee
+++ app/lib/surface/Lank.coco.coffee
@@ -353,7 +353,7 @@

     newScaleFactorX = @thang?.scaleFactorX ? @thang?.scaleFactor ? 1
     newScaleFactorY = @thang?.scaleFactorY ? @thang?.scaleFactor ? 1
-    if @layer?.name is 'Land' or @thang?.isLand or @thang?.spriteName is 'Beam' or @isCinematicLank
+    if @layer?.name is 'Land' or @thang?.isLand or @thang?.spriteName is 'Beam' or @isCinematicLank or @thang?.quickScale
       @scaleFactorX = newScaleFactorX
       @scaleFactorY = newScaleFactorY
     else if @thang and (newScaleFactorX isnt @targetScaleFactorX or newScaleFactorY isnt @targetScaleFactorY)


run: update tests

run: update tests

--- test/promise.run.coffee
+++ test/promise.run.coffee
@@ -102,7 +102,7 @@
         err.message.should.eql 'Catchme'
         next()

-    it.only 'throw error when then not defined', (next) ->
+    it 'throw error when then not defined', (next) ->
       d = domain.create()
       d.run ->
         mecano


Added support for recursively handling directories

--- cmd.coffee
+++ cmd.coffee
@@ -30,6 +30,21 @@
 #     else
 #       fs.unlinkSync curPath
 #   fs.rmdirSync path
+
+getValidatedDirectory = (dir)->
+
+  dir = path.normalize dir
+
+  unless fs.existsSync dir
+    console.log "Directory '#{dir}' does not exist"
+    process.exit(3)
+
+  stat = fs.statSync dir
+  unless stat.isDirectory()
+    console.log "Directory '#{dir}' is not a directory"
+    process.exit(4)
+
+  return dir

 getValidatedSource = (source)->

@@ -72,9 +87,22 @@
   fs.writeFileSync destination, output, { encoding: encoding }
   if isVerboseMode
     if wasAltered
-      console.log "corrected contents outputted to '#{destination}'"
+      console.log "correct content saved to '#{destination}'"
     else
-      console.log "'#{source}' already has the correct line endings. The file was copied to '#{destination}' as-is."
+      if source is destination
+        console.log "'#{source}' already has the correct line endings."
+      else
+        console.log "'#{source}' already has the correct line endings. The file was copied to '#{destination}' as-is."
+
+
+correctDir = (dir)->
+  fs.readdirSync(dir).forEach (file, index) ->
+    curPath = path.join dir, file
+    stat = fs.lstatSync(curPath)
+    if stat.isDirectory()
+      correctDir curPath
+    else
+      correct curPath, curPath

 if commander.directory
@@ -84,6 +112,10 @@
   if commander.output
     console.log "You may not provide an output path for directories"
     process.exit(1)
+
+  directory = getValidatedDirectory commander.args[0]
+
+  correctDir directory

 else


Fix gap in water when gaining height

Fix gap in water when gaining height

--- app/scripts/scenery/Lunch.coffee
+++ app/scripts/scenery/Lunch.coffee
@@ -53,9 +53,9 @@
     super

     height = 65
-    @add(0, @level.visibleHeight - 10, Crafty.e('2D, Solid').attr(w: @delta.x, h: 10))
+    @add(0, @level.visibleHeight - 45, Crafty.e('2D, Solid').attr(w: @delta.x, h: 45))
     @add(0, @level.visibleHeight - height, Crafty.e('2D, WebGL, Color, SunBlock').attr(w: @delta.x, h: height, z: -300).color('#000080'))
-    @addBackground(0, @level.visibleHeight - 175, Crafty.e('2D, WebGL, Color, SunBlock').color('#6060E0').attr({ z: -600, w: (@delta.x * .25) + 1, h: 155 }), .25)
+    @addBackground(0, @level.visibleHeight - 175, Crafty.e('2D, WebGL, Color, SunBlock').color('#6060E0').attr({ z: -600, w: (@delta.x * .25) + 1, h: 200 }), .25)

     goldenStripe = Crafty.e('2D, WebGL, Gradient, GoldenStripe')
       .topColor('#DDDD00')
@@ -103,7 +103,7 @@
     @addBackground(0, @level.visibleHeight - 125, Crafty.e('2D, WebGL, waterMiddleOld, SunBlock').attr(z: -500), .5)

     height = 65
-    @add(0, @level.visibleHeight - 10, Crafty.e('2D, Solid').attr(w: @delta.x, h: 10))
+    @add(0, @level.visibleHeight - 45, Crafty.e('2D, Solid').attr(w: @delta.x, h: 45))
     #@add(0, @level.visibleHeight - height, Crafty.e('2D, WebGL, Image').image('images/water-front-old.png').attr(z: -300))

     water1 = Crafty.e('2D, WebGL, waterFront1Old, Wave1, SunBlock').attr(z: -300)


only show the slider handle when zoomed

only show the slider handle when zoomed

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -57,7 +57,7 @@
     @el.find("#scale-slider").attr "min", @canvasGraph.smallestX

     $(document).on 'mark-change', => @updateButtons()
-
+    setTimeout => @el.find(".faux-range-thumb").hide()
     @drawSliderAxisNums()

   loadSubject: (data) ->
@@ -96,11 +96,13 @@
       zoomButton.innerHTML = 'Zoom'
       @el.find("#toggle-zoom").addClass("toggled")
       @el.find("#scale-slider").addClass("active")
+      @el.find(".faux-range-thumb").fadeIn(150)
     else
       @canvasGraph.plotPoints()
       zoomButton.innerHTML = 'Zoom'
       @el.find("#toggle-zoom").removeClass("toggled")
       @el.find("#scale-slider").removeClass("active")
+      @el.find(".faux-range-thumb").fadeOut(150)

   onToggleFav: ->
     favButton = @el.find("#toggle-fav")[0]


Improve lifecycle

Improve lifecycle

--- hdp/lib/lifecycle.coffee
+++ hdp/lib/lifecycle.coffee
@@ -3,30 +3,45 @@
 http://hadoop.apache.org/docs/r2.1.0-beta/hadoop-project-dist/hadoop-common/ClusterSetup.html
 ###
 lifecyle = module.exports =
+  jn_status: (ctx, callback) ->
+    {hdfs_user, hdfs_pid_dir} = ctx.config.hdp
+    ctx.log "JournalNode status"
+    lifecyle.is_pidfile_running ctx, "#{hdfs_pid_dir}/#{hdfs_user}/hadoop-#{hdfs_user}-journalnode.pid", (err, running) ->
+      callback err, running
   jn_start: (ctx, callback) ->
     {hdfs_user, hadoop_conf_dir} = ctx.config.hdp
-    ctx.execute
-      # su -l hdfs -c "/usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf --script hdfs start journalnode"
-      cmd: "su -l #{hdfs_user} -c \"/usr/lib/hadoop/sbin/hadoop-daemon.sh --config #{hadoop_conf_dir} --script hdfs start journalnode\""
-      code_skipped: 1
-    , (err, started) ->
-      callback err, started
+    lifecyle.jn_status ctx, (err, running) ->
+      return callback err, false if err or running
+      ctx.log "JournalNode start"
+      ctx.execute
+        # su -l hdfs -c "/usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf --script hdfs start journalnode"
+        cmd: "su -l #{hdfs_user} -c \"/usr/lib/hadoop/sbin/hadoop-daemon.sh --config #{hadoop_conf_dir} --script hdfs start journalnode\""
+        code_skipped: 1
+      , (err, started) ->
+        return callback err if err
+        ctx.waitForConnection ctx.config.host, 8485, timeout: 20000, (err) ->
+          callback err, started
   jn_stop: (ctx, callback) ->
     {hdfs_user, hadoop_conf_dir} = ctx.config.hdp
-    ctx.execute
-      # su -l hdfs -c "/usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf --script hdfs stop journalnode"
-      cmd: "su -l #{hdfs_user} -c \"/usr/lib/hadoop/sbin/hadoop-daemon.sh --config #{hadoop_conf_dir} --script hdfs stop journalnode\""
-      code_skipped: 1
-    , (err, started) ->
-      callback err, started
+    lifecyle.jn_status ctx, (err, running) ->
+      return callback err, false if err or not running
+      ctx.log "JournalNode stop"
+      ctx.execute
+        # su -l hdfs -c "/usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf --script hdfs stop journalnode"
+        cmd: "su -l #{hdfs_user} -c \"/usr/lib/hadoop/sbin/hadoop-daemon.sh --config #{hadoop_conf_dir} --script hdfs stop journalnode\""
+        code_skipped: 1
+      , (err, stopped) ->
+        callback err, stopped
   nn_status: (ctx, callback) ->
     {hdfs_pid_dir} = ctx.config.hdp
-    lifecyle.is_pidfile_running ctx, "/var/run/hive/hadoop-hdfs-namenode.pid", (err, running) ->
+    ctx.log "NameNode status"
+    lifecyle.is_pidfile_running ctx, "/var/run/hadoop-hdfs/hdfs/hadoop-hdfs-namenode.pid", (err, running) ->
       callback err, running
   nn_start: (ctx, callback) ->
     {hdfs_user, hadoop_conf_dir, hdfs_namenode_ipc_port, hdfs_namenode_http_port, hdfs_namenode_timeout} = ctx.config.hdp
     lifecyle.nn_status ctx, (err, running) ->
       return callback err, false if err or running
+      ctx.log "NameNode start"
       ctx.execute
         # su -l hdfs -c "/usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf --script hdfs start namenode"
         cmd: "su -l #{hdfs_user} -c \"/usr/lib/hadoop/sbin/hadoop-daemon.sh --config #{hadoop_conf_dir} --script hdfs start namenode\""
@@ -35,19 +50,50 @@
         return callback err if err
         ctx.waitForConnection [
           host: ctx.config.host, port: hdfs_namenode_ipc_port
+        ,
           host: ctx.config.host, port: hdfs_namenode_http_port
-        ], hdfs_namenode_timeout, (err) ->
+        ], timeout: hdfs_namenode_timeout, (err) ->
           callback err, started
   nn_stop: (ctx, callback) ->
     {hdfs_user, hadoop_conf_dir} = ctx.config.hdp
+    ctx.log "NameNode stop"
     ctx.execute
       # su -l hdfs -c "/usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf --script hdfs stop namenode"
       cmd: "su -l #{hdfs_user} -c \"/usr/lib/hadoop/sbin/hadoop-daemon.sh --config #{hadoop_conf_dir} --script hdfs stop namenode\""
       code_skipped: 1
-    , (err, stoped) ->
-      callback err, stoped
+    , (err, stopped) ->
+      callback err, stopped
+  zkfc_status: (ctx, callback) ->
+    {hdfs_user, hdfs_pid_dir} = ctx.config.hdp
+    ctx.log "ZKFC status"
+    lifecyle.is_pidfile_running ctx, "#{hdfs_pid_dir}/#{hdfs_user}/hadoop-#{hdfs_user}-zkfc.pid", (err, running) ->
+      callback err, running
+  zkfc_start: (ctx, callback) ->
+    {hdfs_user} = ctx.config.hdp
+    lifecyle.zkfc_status ctx, (err, running) ->
+      return callback err, false if err or running
+      ctx.log "ZKFC start"
+      ctx.execute
+        # su -l hdfs -c "/usr/lib/hadoop/sbin/hadoop-daemon.sh start zkfc"
+        cmd: "su -l #{hdfs_user} -c \"/usr/lib/hadoop/sbin/hadoop-daemon.sh start zkfc\""
+        code_skipped: 1
+      , (err, started) ->
+        return callback err if err
+        callback err, started
+  zkfc_stop: (ctx, callback) ->
+    {hdfs_user} = ctx.config.hdp
+    lifecyle.zkfc_status ctx, (err, running) ->
+      return callback err, false if err or not running
+      ctx.log "ZKFC stop"
+      ctx.execute
+        # su -l hdfs -c "/usr/lib/hadoop/sbin/hadoop-daemon.sh stop zkfc"
+        cmd: "su -l #{hdfs_user} -c \"/usr/lib/hadoop/sbin/hadoop-daemon.sh stop zkfc\""
+        code_skipped: 1
+      , (err, stopped) ->
+        callback err, stopped
   snn_start: (ctx, callback) ->
     {hdfs_user, hadoop_conf_dir} = ctx.config.hdp
+    ctx.log "SNN start"
     ctx.execute
       # su -l hdfs -c "/usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf start secondarynamenode"
       cmd: "su -l #{hdfs_user} -c \"/usr/lib/hadoop/sbin/hadoop-daemon.sh --config #{hadoop_conf_dir} start secondarynamenode\""
@@ -56,32 +102,43 @@
       callback err, started
   snn_stop: (ctx, callback) ->
     {hdfs_user, hadoop_conf_dir} = ctx.config.hdp
+    ctx.log "SNN stop"
     ctx.execute
       # su -l hdfs -c "/usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf stop secondarynamenode"
       cmd: "su -l #{hdfs_user} -c \"/usr/lib/hadoop/sbin/hadoop-daemon.sh --config #{hadoop_conf_dir} stop secondarynamenode\""
       code_skipped: 1
-    , (err, stoped) ->
-      callback err, stoped
+    , (err, stopped) ->
+      callback err, stopped
+  dn_status: (ctx, callback) ->
+    {hdfs_pid_dir} = ctx.config.hdp
+    ctx.log "DataNode status"
+    lifecyle.is_pidfile_running ctx, "/var/run/hadoop-hdfs/hdfs/hadoop-hdfs-datanode.pid", (err, running) ->
+      callback err, running
   dn_start: (ctx, callback) ->
     {hdfs_user, hadoop_conf_dir} = ctx.config.hdp
-    ctx.execute
-      # HADOOP_SECURE_DN_USER=hdfs /usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf --script hdfs start datanode
-      cmd: "HADOOP_SECURE_DN_USER=hdfs /usr/lib/hadoop/sbin/hadoop-daemon.sh --config #{hadoop_conf_dir} --script hdfs start datanode"
-      code_skipped: 1
-    , (err, started) ->
-      callback err, if started then ctx.OK else ctx.PASS
+    lifecyle.dn_status ctx, (err, running) ->
+      return callback err, false if err or running
+      ctx.log "DataNode start"
+      ctx.execute
+        # HADOOP_SECURE_DN_USER=hdfs /usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf --script hdfs start datanode
+        cmd: "HADOOP_SECURE_DN_USER=hdfs /usr/lib/hadoop/sbin/hadoop-daemon.sh --config #{hadoop_conf_dir} --script hdfs start datanode"
+        code_skipped: 1
+      , (err, started) ->
+        callback err, started
   dn_stop: (ctx, callback) ->
     {hdfs_user, hadoop_conf_dir} = ctx.config.hdp
-    ctx.execute
-      # su -l hdfs -c "/usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf stop datanode"
-      # cmd: "su -l #{hdfs_user} -c \"/usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf stop datanode\""
-      # /usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf stop datanode
-      cmd: "/usr/lib/hadoop/sbin/hadoop-daemon.sh --config #{hadoop_conf_dir} stop datanode"
-      code_skipped: 1
-    , (err, stoped) ->
-      callback err, if stoped then ctx.OK else ctx.PASS
+    lifecyle.dn_status ctx, (err, running) ->
+      return callback err, false if err or not running
+      ctx.log "DataNode stop"
+      ctx.execute
+        # /usr/lib/hadoop/sbin/hadoop-daemon.sh --config /etc/hadoop/conf stop datanode
+        cmd: "/usr/lib/hadoop/sbin/hadoop-daemon.sh --config #{hadoop_conf_dir} stop datanode"
+        code_skipped: 1
+      , (err, stopped) ->
+        callback err, stopped
   rm_start: (ctx, callback) ->
     {yarn_user, hadoop_conf_dir} = ctx.config.hdp
+    ctx.log "ResourceManager start"
     ctx.execute
       # su -l yarn -c "export HADOOP_LIBEXEC_DIR=/usr/lib/hadoop/libexec && /usr/lib/hadoop-yarn/sbin/yarn-daemon.sh --config /etc/hadoop/conf start resourcemanager"
       cmd: "su -l #{yarn_user} -c \"export HADOOP_LIBEXEC_DIR=/usr/lib/hadoop/libexec && /usr/lib/hadoop-yarn/sbin/yarn-daemon.sh --config #{hadoop_conf_dir} start resourcemanager\""
@@ -90,14 +147,16 @@
       callback err, started
   rm_stop: (ctx, callback) ->
     {yarn_user, hadoop_conf_dir} = ctx.config.hdp
+    ctx.log "ResourceManager stop"
     ctx.execute
       # su -l yarn -c "export HADOOP_LIBEXEC_DIR=/usr/lib/hadoop/libexec && /usr/lib/hadoop-yarn/sbin/yarn-daemon.sh --config /etc/hadoop/conf stop resourcemanager"
       cmd: "su -l #{yarn_user} -c \"export HADOOP_LIBEXEC_DIR=/usr/lib/hadoop/libexec && /usr/lib/hadoop-yarn/sbin/yarn-daemon.sh --config #{hadoop_conf_dir} stop resourcemanager\""
       code_skipped: 1
-    , (err, stoped) ->
-      callback err, stoped
+    , (err, stopped) ->
+      callback err, stopped
   nm_start: (ctx, callback) ->
     {yarn_user, hadoop_conf_dir} = ctx.config.hdp
+    ctx.log "NodeManager start"
     ctx.execute
       # su -l yarn -c "export HADOOP_LIBEXEC_DIR=/usr/lib/hadoop/libexec && /usr/lib/hadoop-yarn/sbin/yarn-daemon.sh --config /etc/hadoop/conf start nodemanager"
       cmd: "su -l #{yarn_user} -c \"export HADOOP_LIBEXEC_DIR=/usr/lib/hadoop/libexec && /usr/lib/hadoop-yarn/sbin/yarn-daemon.sh --config #{hadoop_conf_dir} start nodemanager\""
@@ -106,14 +165,16 @@
       callback err, started
   nm_stop: (ctx, callback) ->
     {yarn_user, hadoop_conf_dir} = ctx.config.hdp
+    ctx.log "NodeManager stop"
     ctx.execute
       # su -l yarn -c "export HADOOP_LIBEXEC_DIR=/usr/lib/hadoop/libexec && /usr/lib/hadoop-yarn/sbin/yarn-daemon.sh --config /etc/hadoop/conf stop nodemanager"
       cmd: "su -l #{yarn_user} -c \"export HADOOP_LIBEXEC_DIR=/usr/lib/hadoop/libexec && /usr/lib/hadoop-yarn/sbin/yarn-daemon.sh --config #{hadoop_conf_dir} stop nodemanager\""
       code_skipped: 1
-    , (err, stoped) ->
-      callback err, stoped
+    , (err, stopped) ->
+      callback err, stopped
   jhs_start: (ctx, callback) ->
     {mapred_user, hadoop_conf_dir} = ctx.config.hdp
+    ctx.log "JobHistoryServer start"
     ctx.execute
       # su -l mapred -c "export HADOOP_LIBEXEC_DIR=/usr/lib/hadoop/libexec/ && /usr/lib/hadoop-mapreduce/sbin/mr-jobhistory-daemon.sh --config /etc/hadoop/conf start historyserver"
       cmd: "su -l #{mapred_user} -c \"export HADOOP_LIBEXEC_DIR=/usr/lib/hadoop/libexec/ && /usr/lib/hadoop-mapreduce/sbin/mr-jobhistory-daemon.sh --config #{hadoop_conf_dir} start historyserver\""
@@ -122,6 +183,7 @@
       callback err, started
   jhs_stop: (ctx, callback) ->
     {mapred_user, hadoop_conf_dir} = ctx.config.hdp
+    ctx.log "JobHistoryServer stop"
     ctx.execute
       # su -l mapred -c "export HADOOP_LIBEXEC_DIR=/usr/lib/hadoop/libexec/ && /usr/lib/hadoop-mapreduce/sbin/mr-jobhistory-daemon.sh --config /etc/hadoop/conf stop historyserver"
       cmd: "su -l #{mapred_user} -c \"export HADOOP_LIBEXEC_DIR=/usr/lib/hadoop/libexec/ && /usr/lib/hadoop-mapreduce/sbin/mr-jobhistory-daemon.sh --config #{hadoop_conf_dir} stop historyserver\""
@@ -130,12 +192,14 @@
       callback err, started
   hive_metastore_status: (ctx, callback) ->
     # {hive_metastore_pid} = ctx.config.hdp
+    ctx.log "Hive Metastore status"
     lifecyle.is_pidfile_running ctx, "/var/run/hive/metastore.pid", (err, running) ->
       callback err, running
   hive_metastore_start: (ctx, callback) ->
     {hive_user, hive_log_dir, hive_pid_dir, hive_metastore_host, hive_metastore_port, hive_metastore_timeout} = ctx.config.hdp
     lifecyle.hive_metastore_status ctx, (err, running) ->
       return callback err, false if err or running
+      ctx.log "Hive Metastore start"
       ctx.execute
         # su -l hive -c 'nohup hive --service metastore >/var/log/hive/hive.out 2>/var/log/hive/hive.log & echo $! >/var/run/hive/metastore.pid'
         # su -l hive -c 'hive --service metastore'
@@ -143,29 +207,33 @@
         code_skipped: 1
       , (err, started) ->
         return callback err if err
-        ctx.waitForConnection hive_metastore_host, hive_metastore_port, hive_metastore_timeout, (err) ->
+        ctx.waitForConnection hive_metastore_host, hive_metastore_port, timeout: hive_metastore_timeout, (err) ->
           callback err, started
   hive_metastore_stop: (ctx, callback) ->
     {hive_user, hive_pid_dir} = ctx.config.hdp
     lifecyle.hive_metastore_status ctx, (err, running) ->
       return callback err, false if err or not running
+      ctx.log "Hive Metastore stop"
       ctx.execute
         # su -l hive -c "kill `cat /var/run/hive/metastore.pid`"
         cmd: "su -l #{hive_user} -c \"kill `cat #{hive_pid_dir}/metastore.pid`\""
         code_skipped: 1
-      , (err, started) ->
-        callback err, started
+      , (err, stopped) ->
+        callback err, stopped
   hive_metastore_restart: (ctx, callback) ->
+    ctx.log "Hive Metastore restart"
     lifecyle.hive_metastore_stop ctx, (err) ->
       return callback err if err
       lifecyle.hive_metastore_start ctx, callback
   hive_server2_status: (ctx, callback) ->
+    ctx.log "Hive Server2 status"
     lifecyle.is_pidfile_running ctx, "/var/run/hive/server2.pid", (err, running) ->
       callback err, running
   hive_server2_start: (ctx, callback) ->
     {hive_user, hive_log_dir, hive_pid_dir, hive_server2_host, hive_server2_port, hive_server2_timeout} = ctx.config.hdp
     lifecyle.hive_server2_status ctx, (err, running) ->
       return callback err, false if err or running
+      ctx.log "Hive Server2 start"
       ctx.execute
         # su -l hive -c 'nohup /usr/lib/hive/bin/hiveserver2 >/var/log/hive/hiveserver2.out 2>/var/log/hive/hiveserver2.log & echo $! >/var/run/hive/server2.pid'
         # su -l hive -c '/usr/lib/hive/bin/hiveserver2'
@@ -173,12 +241,13 @@
         code_skipped: 1
       , (err, started) ->
         return callback err if err
-        ctx.waitForConnection hive_server2_host, hive_server2_port, hive_server2_timeout, (err) ->
+        ctx.waitForConnection hive_server2_host, hive_server2_port, timeout: hive_server2_timeout, (err) ->
           callback err, started
   hive_server2_stop: (ctx, callback) ->
     {hive_user, hive_pid_dir} = ctx.config.hdp
     lifecyle.hive_server2_status ctx, (err, running) ->
       return callback err, false if err or not running
+      ctx.log "Hive Server2 stop"
       ctx.execute
         # su -l hive -c "kill `cat /var/run/hive/server2.pid"
         cmd: "su -l #{hive_user} -c \"kill `cat #{hive_pid_dir}/server2.pid`\""
@@ -186,10 +255,12 @@
       , (err, started) ->
         callback err, started
   hive_server2_restart: (ctx, callback) ->
+    ctx.log "Hive Server2 restart"
     lifecyle.hive_server2_stop ctx, (err) ->
       return callback err if err
       lifecyle.hive_server2_start ctx, callback
   oozie_status: (ctx, callback) ->
+    ctx.log "Oozie status"
     {oozie_pid_dir} = ctx.config.hdp
     lifecyle.is_pidfile_running ctx, "#{oozie_pid_dir}/oozie.pid", (err, running) ->
       callback err, running
@@ -197,6 +268,7 @@
     {oozie_user} = ctx.config.hdp
     lifecyle.oozie_status ctx, (err, running) ->
       return callback err, false if err or running
+      ctx.log "Oozie start"
       ctx.execute
         # su -l oozie -c "/usr/lib/oozie/bin/oozied.sh start"
         cmd: "su -l #{oozie_user} -c \"/usr/lib/oozie/bin/oozied.sh start\""
@@ -207,42 +279,60 @@
     {oozie_user} = ctx.config.hdp
     lifecyle.oozie_status ctx, (err, running) ->
       return callback err, false if err or not running
+      ctx.log "Oozie stop"
       ctx.execute
         # su -l oozie -c "/usr/lib/oozie/bin/oozied.sh stop"
         cmd: "su -l #{oozie_user} -c \"/usr/lib/oozie/bin/oozied.sh stop\""
         code_skipped: 1
-      , (err, stoped) ->
-        callback err, stoped
+      , (err, stopped) ->
+        callback err, stopped
+  zookeeper_status: (ctx, callback) ->
+    {zookeeper_pid_dir} = ctx.config.hdp
+    ctx.log "Zookeeper status"
+    {oozie_pid_dir} = ctx.config.hdp
+    lifecyle.is_pidfile_running ctx, "#{zookeeper_pid_dir}/zookeeper_server.pid", (err, running) ->
+      callback err, running
   zookeeper_start: (ctx, callback) ->
-    {zookeeper_user, zookeeper_conf_dir} = ctx.config.hdp
-    ctx.execute
-      # su -l zookeeper -c "/usr/lib/zookeeper/bin/zkServer.sh start /etc/zookeeper/conf/zoo.cfg"
-      cmd: "su -l #{zookeeper_user} -c \"/usr/lib/zookeeper/bin/zkServer.sh start #{zookeeper_conf_dir}/zoo.cfg\""
-    , (err, stoped) ->
-      callback err, stoped
+    {zookeeper_user, zookeeper_conf_dir, zookeeper_port} = ctx.config.hdp
+    lifecyle.zookeeper_status ctx, (err, running) ->
+      return callback err, false if err or running
+      ctx.log "Zookeeper start"
+      ctx.execute
+        # su -l zookeeper -c "/usr/lib/zookeeper/bin/zkServer.sh start /etc/zookeeper/conf/zoo.cfg"
+        cmd: "su -l #{zookeeper_user} -c \"/usr/lib/zookeeper/bin/zkServer.sh start #{zookeeper_conf_dir}/zoo.cfg\""
+      , (err, started) ->
+        return callback err if err
+        ctx.waitForConnection ctx.config.host, zookeeper_port, timeout: 20000, (err) ->
+          callback err, started
   zookeeper_stop: (ctx, callback) ->
     {zookeeper_user, zookeeper_conf_dir} = ctx.config.hdp
-    ctx.execute
-      # su -l zookeeper -c "/usr/lib/zookeeper/bin/zkServer.sh stop /etc/zookeeper/conf/zoo.cfg"
-      cmd: "su -l #{zookeeper_user} -c \"/usr/lib/zookeeper/bin/zkServer.sh stop #{zookeeper_conf_dir}/zoo.cfg\""
-    , (err, stoped) ->
-      callback err, stoped
+    lifecyle.zookeeper_status ctx, (err, running) ->
+      return callback err, false if err or not running
+      ctx.log "Zookeeper stop"
+      ctx.execute
+        # su -l zookeeper -c "/usr/lib/zookeeper/bin/zkServer.sh stop /etc/zookeeper/conf/zoo.cfg"
+        cmd: "su -l #{zookeeper_user} -c \"/usr/lib/zookeeper/bin/zkServer.sh stop #{zookeeper_conf_dir}/zoo.cfg\""
+      , (err, stopped) ->
+        callback err, stopped
   hbase_master_start: (ctx, callback) ->
     {hbase_user, hbase_conf_dir} = ctx.config.hdp
+    ctx.log "HBase Master start"
     ctx.execute
       # su -l hbase -c "/usr/lib/hbase/bin/hbase-daemon.sh --config /etc/hbase/conf start master"
       cmd: "su -l #{hbase_user} -c \"/usr/lib/hbase/bin/hbase-daemon.sh --config #{hbase_conf_dir} start master\""
-    , (err, stoped) ->
-      callback err, stoped
+    , (err, stopped) ->
+      callback err, stopped
   hbase_master_stop: (ctx, callback) ->
     {hbase_user, hbase_conf_dir} = ctx.config.hdp
+    ctx.log "HBase Master stop"
     ctx.execute
       # su -l hbase -c "/usr/lib/hbase/bin/hbase-daemon.sh --config /etc/hbase/conf stop master"
       cmd: "su -l #{hbase_user} -c \"/usr/lib/hbase/bin/hbase-daemon.sh --config #{hbase_conf_dir} stop master\""
-    , (err, stoped) ->
-      callback err, stoped
+    , (err, stopped) ->
+      callback err, stopped
   hbase_regionserver_start: (ctx, callback) ->
     {hbase_user, hbase_conf_dir} = ctx.config.hdp
+    ctx.log "HBase RegionServer start"
     ctx.execute
       # su -l hbase -c "/usr/lib/hbase/bin/hbase-daemon.sh --config /etc/hbase/conf start regionserver"
       cmd: "su -l #{hbase_user} -c \"/usr/lib/hbase/bin/hbase-daemon.sh --config #{hbase_conf_dir} start regionserver\""
@@ -250,19 +340,22 @@
       callback err, started
   hbase_regionserver_stop: (ctx, callback) ->
     {hbase_user, hbase_conf_dir} = ctx.config.hdp
+    ctx.log "HBase RegionServer stop"
     ctx.execute
       # su -l hbase -c "/usr/lib/hbase/bin/hbase-daemon.sh --config /etc/hbase/conf stop regionserver"
       cmd: "su -l #{hbase_user} -c \"/usr/lib/hbase/bin/hbase-daemon.sh --config #{hbase_conf_dir} stop regionserver\""
-    , (err, stoped) ->
-      callback err, stoped
+    , (err, stopped) ->
+      callback err, stopped
   webhcat_status: (ctx, callback) ->
     {webhcat_pid_dir} = ctx.config.hdp
+    ctx.log "WebHCat status"
     lifecyle.is_pidfile_running ctx, "#{webhcat_pid_dir}/webhcat.pid", (err, running) ->
       callback err, running
   webhcat_start: (ctx, callback) ->
     {webhcat_user, webhcat_conf_dir} = ctx.config.hdp
     lifecyle.webhcat_status ctx, (err, running) ->
       return callback err, false if err or running
+      ctx.log "WebHCat start"
       ctx.execute
         # su -l hcat -c "export WEBHCAT_CONF_DIR=/etc/hcatalog/conf/webhcat; /usr/lib/hcatalog/sbin/webhcat_server.sh start"
         cmd: "su -l #{webhcat_user} -c \"export WEBHCAT_CONF_DIR=#{webhcat_conf_dir}; /usr/lib/hcatalog/sbin/webhcat_server.sh start\""
@@ -272,12 +365,14 @@
     {webhcat_user} = ctx.config.hdp
     lifecyle.webhcat_status ctx, (err, running) ->
       return callback err, false if err or not running
+      ctx.log "WebHCat stop"
       ctx.execute
         # su -l hcat -c "/usr/lib/hcatalog/sbin/webhcat_server.sh stop"
         cmd: "su -l #{webhcat_user} -c \"/usr/lib/hcatalog/sbin/webhcat_server.sh stop\""
-      , (err, stoped) ->
-        callback err, stoped
+      , (err, stopped) ->
+        callback err, stopped
   hue_status: (ctx, callback) ->
+    ctx.log "Hue status"
     ctx.execute
       cmd: "service hue status"
       code_skipped: 1
@@ -287,19 +382,20 @@
     {hue_user} = ctx.config.hdp
     lifecyle.hue_status ctx, (err, running) ->
       return callback err, false if err or not running
+      ctx.log "Hue start"
       ctx.execute
         cmd: "service hue start"
-      , (err, stoped) ->
-        callback err, stoped
+      , (err, stopped) ->
+        callback err, stopped
   hue_stop: (ctx, callback) ->
     {hue_user} = ctx.config.hdp
-    console.log 'ok'
     lifecyle.webhcat_status ctx, (err, running) ->
       return callback err, false if err or not running
+      ctx.log "Hue stop"
       ctx.execute
         cmd: "service hue stop"
-      , (err, stoped) ->
-        callback err, stoped
+      , (err, stopped) ->
+        callback err, stopped

 module.exports.is_pidfile_running = (ctx, path, callback) ->
   ctx.execute


Remove dead create

Remove dead create

--- src/config/sprites.coffee
+++ src/config/sprites.coffee
@@ -5,13 +5,11 @@
 exports.sprites = {
   _LABEL: "sprites"
   _KIND: "sprite"
-  _EXPORTS: ['create']
   _SETUP: (world) ->
     this_level = world.get 'current_level'
     console.log 'this_level', this_level.name
     for sprite in this_level.sprites
       world.call 'sprite', sprite
-
   sprite: (world, args) ->
     world.authority = world.make_world(args)
     dict = {_LABEL: args.name, _EXPORTS: SPRITE_EXPORTS}
@@ -19,18 +17,11 @@
     world.authority = undefined
     world.send 'programs', args, (value) -> world.put 'programs', value
     world.send 'inspect', child
-
-  create: (world, args) ->
-    world.authority = args
-    dict = {_LABEL: args.label(), _EXPORTS: SPRITE_EXPORTS}
-    child = world.add_child dict
-    world.authority = undefined
-    world.send 'created', child
-    world.send 'inspect', child
+
+  p: [0, 0]
+  v: [1, 0]
   i: (world) -> world.get('p').at(0)
   j: (world) -> world.get('p').at(1)
-  p: [0, 0]
-  v: [1, 0]
   v_i: (world) -> world.get('v').at(0)
   v_j: (world) -> world.get('v').at(1)
   name_style: (world) ->


bugfix: none milestone

bugfix: none milestone

--- iced2/main.iced
+++ iced2/main.iced
@@ -136,10 +136,11 @@
             for org in orgs
                 for repo in org.repos
                     {full_name} = repo
-                    repo.milestones.none = {number:'none', title:'none'}
+                    repo.milestones.push {number:'none', title:'none'}
                     for milestone in repo.milestones
                         {number, title} = milestone
                         req.github.issues {repo:full_name, milestone:number, state:'all'}, defer err[title], milestone.issues
+        # XXX comments
     res.render 'index', {inspect:util.inspect, authorize_uri, token, repos, orgs}
 app.get '/oauth2callback', (req, res, next) ->
     {code, state} = req.query


Validate request before bouncing it

Validate request before bouncing it

--- src/scripts/singularity.coffee
+++ src/scripts/singularity.coffee
@@ -48,8 +48,9 @@
       return false
     return true

-  showRequests = (msg, alias) ->
-    if not isAliasDefined msg, alias
+  getRequests = (alias, cb) ->
+    if not _singularityAliases[alias]?
+      cb("I don't know that singularity alias")
       return

     url = _singularityAliases[alias].url + '/api/requests'
@@ -57,23 +58,29 @@
       .header('Accept', 'application/json')
       .get() (err, res, body) ->
         if (err)
-          msg.send "There was an error while making the request:"
-          msg.send err
-          return
-
-        data = null
+          cb(err)
+          return
+
         try
           requests = JSON.parse body
-          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}"
+          cb(null, requests)
         catch error
-          msg.send "Ran into an error parsing JSON :"
-          msg.send error
+          cb(error)
+
+  showRequests = (msg, alias) ->
+    getRequests(alias, (err, requests) ->
+      if (err)
+        msg.send err
+        return
+
+      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}"
+    )

   showCommands = (msg) ->
     msg.send "Singularity commands:"
@@ -166,18 +173,29 @@
           msg.send error

   bounceRequest = (msg, alias, request) ->
-    if not isAliasDefined msg, alias
-      return
-
-    url = _singularityAliases[alias].url + '/api/requests/request/' + request + '/bounce'
-
-    robot.http(url)
-      .post() (err, res, body) ->
-        if (err)
-          msg.send "There was an error while making the request:"
-          msg.send err
-          return
-        msg.send "Bouncing '#{request}' on '#{alias}'"
+    if not request?
+      msg.send "Please provide a valid request id"
+      return
+
+    getRequests(alias, (err, requests) ->
+      if (err)
+        msg.send err
+        return
+
+      for r in requests
+        if r.request.id == request
+          url = _singularityAliases[alias].url + '/api/requests/request/' + request + '/bounce'
+
+          robot.http(url)
+            .post() (error, res, body) ->
+              if (error)
+                msg.send "There was an error while making the request:"
+                msg.send err
+                return
+              msg.send "Bouncing '#{request}' on '#{alias}'"
+          return
+      msg.send "Request '#{request}' not found on #{alias}"
+    )

   robot.brain.on 'loaded', ->
     if robot.brain.data.singularity_aliases?


fixed global alpha on dynamic image renderer

--- bokehjs/src/coffee/renderer/tile/dynamic_image_renderer.coffee
+++ bokehjs/src/coffee/renderer/tile/dynamic_image_renderer.coffee
@@ -74,6 +74,9 @@
   _draw_image: (image_key) ->
     image_obj = @mget('image_source').images[image_key]
     if image_obj?
+      @map_canvas.save()
+      @_set_rect()
+      @map_canvas.globalAlpha = @mget('alpha')
       [sxmin, symin] = @plot_view.frame.map_to_screen([image_obj.bounds[0]], [image_obj.bounds[3]], @plot_view.canvas)
       [sxmax, symax] = @plot_view.frame.map_to_screen([image_obj.bounds[2]], [image_obj.bounds[1]], @plot_view.canvas)
       sxmin = sxmin[0]
@@ -85,6 +88,7 @@
       sx = sxmin
       sy = symin
       @map_canvas.drawImage(image_obj.img, sx, sy, sw, sh)
+      @map_canvas.restore()

   _set_rect:() ->
     outline_width = @plot_view.outline_props.width.value()
@@ -94,14 +98,6 @@
     h = @map_frame.get('height') - outline_width
     @map_canvas.rect(l, t, w, h)
     @map_canvas.clip()
-
-  _render_images: (image_keys) ->
-    @map_canvas.save()
-    @_set_rect()
-    @map_canvas.globalAlpha = @mget('alpha')
-    for image_key in image_keys
-      @_draw_tile(image_key)
-    @map_canvas.restore()

 class DynamicImageRenderer extends HasParent
   default_view: DynamicImageView


plot title rendered on canvas now

plot title rendered on canvas now

--- static/coffee/common/plot.coffee
+++ static/coffee/common/plot.coffee
@@ -189,7 +189,6 @@
     # TODO use template
     @$el.append($("""

-
#{@mget('title')}

         <canvas class='bokeh_canvas'></canvas>


remove console.log

remove console.log

--- static/coffee/completions.coffee
+++ static/coffee/completions.coffee
@@ -264,8 +264,6 @@
         caretPos = $inputor.caret 'pos'
         subtext = content.slice(0, caretPos)
         nextPart = content.slice(caretPos)
-        console.log(subtext)
-        console.log(nextPart)
         if subtext.slice(-2) is '@(' and (not nextPart or nextPart.slice(0,1) is not ')')
           text = subtext + ')' + content.slice(caretPos + 1)
           $inputor.val(text)


Stop fetching new items if we're at max item limit

--- static/coffee/controllers.coffee
+++ static/coffee/controllers.coffee
@@ -240,12 +240,13 @@
     afterTime = $scope.newItemsMaxTime or $scope.startTime
     $scope.newItemsMaxTime = new Date()

-    MessageService.fetchNew $scope.activeSearch, afterTime, $scope.newItemsMaxTime, (cases) ->
+    MessageService.fetchNew $scope.activeSearch, afterTime, $scope.newItemsMaxTime, (items) ->
       if timeCode == $scope.activeSearch.timeCode
-        $scope.items = cases.concat($scope.items)
+        $scope.items = items.concat($scope.items)
         $scope.newItemsCount += cases.length

-      $timeout($scope.refreshNewItems, INTERVAL_MESSAGES_NEW)
+      if items.length < INFINITE_SCROLL_MAX_ITEMS
+        $timeout($scope.refreshNewItems, INTERVAL_MESSAGES_NEW)

   $scope.onExpandMessage = (message) ->
     $scope.expandedMessageId = message.id


handle modal message when delete a vdi in sr page

--- app/scripts/controllers/sr.coffee
+++ app/scripts/controllers/sr.coffee
@@ -1,7 +1,7 @@
 'use strict'

 angular.module('xoWebApp')
-  .controller 'SrCtrl', ($scope, $stateParams, xoApi, xo) ->
+  .controller 'SrCtrl', ($scope, $stateParams, xoApi, xo, modal) ->
     $scope.$watch(
       -> xo.revision
       -> $scope.SR = xo.get $stateParams.id
@@ -24,7 +24,11 @@
     $scope.deleteVDI = (UUID) ->
       console.log "Delete VDI #{UUID}"

-      xoApi.call 'vdi.delete', {id: UUID}
+      modal.confirm({
+        title: 'VDI deletion'
+        message: 'Are you sure you want to delete this VDI? This operation is irreversible.'
+      }).then ->
+        xoApi.call 'vdi.delete', {id: UUID}

     $scope.disconnectVBD = (UUID) ->
       console.log "Disconnect VBD #{UUID}"


Guard against missing links (profile follows)

--- apps/artwork_2/lib/follow.coffee
+++ apps/artwork_2/lib/follow.coffee
@@ -6,6 +6,8 @@

 module.exports = ($el) ->
   following = null
+
+  return unless $el.length

   { id, type } = $el.data()


double arrow

double arrow

--- apps/fair/client/overview.coffee
+++ apps/fair/client/overview.coffee
@@ -19,7 +19,7 @@
     @fair = options.fair
     @renderClock()
     if sd.CURRENT_USER?
-      _.delay ->
+      _.delay =>
         if analytics.abTest 'forYouAtFair'
           analytics.track.click "Viewed fair overview with ForYou module"
           @$('.for-you-container').html forYouTemplate()