More refactoring of yield.

More refactoring of yield.

resuming_stack is no longer managed by the native functions, and we no
longer use secret_stash.

--- src/methods.coffee
+++ src/methods.coffee
@@ -359,11 +359,9 @@
         o 'isAlive()Z', (rs) -> false
         o 'start0()V', (rs) -> # NOP
         o 'sleep(J)V', (rs, millis) ->
-            if rs.resuming_stack?
-              rs.resuming_stack = null
-            else
-              throw new util.YieldException (cb) ->
-                setTimeout(cb, millis.toNumber())
+            rs.curr_frame().resume = -> # NOP
+            throw new util.YieldException (cb) ->
+              setTimeout(cb, millis.toNumber())
       ]
       Throwable: [
         o 'fillInStackTrace()L!/!/!;', (rs, _this) ->
@@ -391,20 +389,14 @@
       ]
       FileInputStream: [
         o 'available()I', (rs) -> 1 # TODO
-        o 'readBytes([BII)I', (rs) ->
-            if rs.resuming_stack?
-              rs.resuming_stack = null
-              rv = rs.secret_stash
-              rs.secret_stash = null
-              rv
-            else
-              args = rs.curr_frame().locals
-              [offset,n_bytes] = args[2..3]
-              throw new util.YieldException (cb) ->
-                rs.async_input n_bytes, (bytes) ->
-                  rs.get_obj(args[1]).array[offset...offset+bytes.length] = bytes
-                  rs.secret_stash = bytes.length
-                  cb()
+        o 'readBytes([BII)I', (rs, _this, byte_arr, offset, n_bytes) ->
+            result = null # will be filled in after the yield
+            rs.curr_frame().resume = -> result
+            throw new util.YieldException (cb) ->
+              rs.async_input n_bytes, (bytes) ->
+                byte_arr.array[offset...offset+bytes.length] = bytes
+                result = bytes.length
+                cb()
       ]
       ObjectStreamClass: [
         o 'initNative()V', (rs) ->  # NOP
@@ -602,6 +594,8 @@
         cf = runtime_state.curr_frame()
         runtime_state.resuming_stack--
         return cf.method.run(runtime_state) unless cf.method is @
+      if runtime_state.resuming_stack == runtime_state.meta_stack.length - 1
+        runtime_state.resuming_stack = null
     else
       caller_stack = runtime_state.curr_frame().stack
       if virtual
@@ -615,11 +609,15 @@
         return m.run(runtime_state)
       params = @take_params caller_stack
       runtime_state.meta_stack.push(new runtime.StackFrame(this,params,[]))
-      padding = (' ' for [2...runtime_state.meta_stack.length]).join('')
+    padding = (' ' for [2...runtime_state.meta_stack.length]).join('')
     sig = "#{@class_name}::#{@name}#{@raw_descriptor}"
     debug "#{padding}entering method #{sig}"
     # check for trapped and native methods, run those manually
-    if trapped_methods[sig]
+    cf = runtime_state.curr_frame()
+    if cf.resume? # we are resuming from a yield, and this was a manually run method
+      @run_manually cf.resume, runtime_state
+      cf.resume = null
+    else if trapped_methods[sig]
       @run_manually trapped_methods[sig], runtime_state
     else if @access_flags.native
       if sig.indexOf('::registerNatives()V',1) >= 0 or sig.indexOf('::initIDs()V',1) >= 0


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/movable.coffee
+++ src/movable.coffee
@@ -43,6 +43,7 @@
   I.velocity = Point(I.velocity.x, I.velocity.y)

   self.bind 'update', ->
+    log I.acceleration
     I.velocity.add$(I.acceleration)

     if I.maxSpeed?


Updating a comment.

Updating a comment.

--- src/natives.coffee
+++ src/natives.coffee
@@ -1409,7 +1409,6 @@
                 # native functions.
                 except_cb ->
                   # XXX: Kludge to get around my nice abstraction. :-/
-                  # except_cb pops the frame off before running the callback.
                   rs.meta_stack().push my_sf
                   # Push the constructor's frame onto the stack.
                   method.setup_stack(rs)


Making comments one-liners instead of multi-liners, since native.coffee doesn't abide by the 80-character-limit on lines anyway.

--- src/natives.coffee
+++ src/natives.coffee
@@ -310,17 +310,14 @@
             # Needs to be checked *even if length is 0*.
             if !src? or !dest?
               exceptions.java_throw rs, 'java/lang/NullPointerException', 'Cannot copy to/from a null array.'
-            # Can't do this on non-array types. Need to check before I check
-            # bounds below, or else I'll get an exception.
+            # Can't do this on non-array types. Need to check before I check bounds below, or else I'll get an exception.
             if !(src.type instanceof types.ArrayType) or !(dest.type instanceof types.ArrayType)
               exceptions.java_throw rs, 'java/lang/ArrayStoreException', 'src and dest arguments must be of array type.'
             # Also needs to be checked *even if length is 0*.
             if src_pos < 0 or (src_pos+length) > src.array.length or dest_pos < 0 or (dest_pos+length) > dest.array.length or length < 0
-              # System.arraycopy requires IndexOutOfBoundsException, but Java
-              # throws an array variant of the exception in practice.
+              # System.arraycopy requires IndexOutOfBoundsException, but Java throws an array variant of the exception in practice.
               exceptions.java_throw rs, 'java/lang/ArrayIndexOutOfBoundsException', 'Tried to write to an illegal index in an array.'
-            # Special case; need to copy the section of src that is being copied
-            # into a temporary array before actually doing the copy.
+            # Special case; need to copy the section of src that is being copied into a temporary array before actually doing the copy.
             if src == dest
               src = {type: src.type, array: src.array.slice(src_pos, src_pos+length)}
               src_pos = 0
@@ -330,8 +327,7 @@
               arraycopy_no_check(src, src_pos, dest, dest_pos, length)
             else
               # Slow path
-              # Absolutely cannot do this when two different primitive types, or
-              # a primitive type and a reference type.
+              # Absolutely cannot do this when two different primitive types, or a primitive type and a reference type.
               if (src.type.component_type instanceof types.PrimitiveType) or (dest.type.component_type instanceof types.PrimitiveType)
                 exceptions.java_throw rs, 'java/lang/ArrayStoreException', 'If calling arraycopy with a primitive array, both src and dest must be of the same primitive type.'
               else


fix: jQuery animations work again (bootstrap javascripts messed something up)

--- app/assets/javascripts/application.coffee
+++ app/assets/javascripts/application.coffee
@@ -16,8 +16,8 @@
 #= require jquery.ui.sortable
 #= require_tree ./vendor
 #= require js-routes
-#= require twitter/bootstrap
-
+#= require twitter/bootstrap/bootstrap-tooltip
+#= require twitter/bootstrap/bootstrap-popover
 #= require variables
 #= require_tree ./libraries
 #= require profile


reactivate LiveChat

reactivate LiveChat

--- app/assets/javascripts/application.coffee
+++ app/assets/javascripts/application.coffee
@@ -42,7 +42,7 @@
 # = require vendor/inviewport
 # = require vendor/ios-orientationchange-fix
 # = require vendor/image_path_helper
-# require vendor/livechat
+# = require vendor/livechat
 #
 # = require bootstrap-sprockets
 # = require algolia/algoliasearch.min


Removed done

Removed done

--- src/lib/iteration_utils.coffee
+++ src/lib/iteration_utils.coffee
@@ -37,15 +37,15 @@
       for start_index in [0..count] by IterationUtils.MAX_ITERATION_COUNT
         do (start_index) => queue.defer (callback) ->
           iteration_end = Math.min(start_index+IterationUtils.MAX_ITERATION_COUNT, count)
-          next = (err, done) =>
-            return callback(err) if err or done or (index >= iteration_end)
+          next = (err) =>
+            return callback(err) if err or (index >= iteration_end)
             iterator(array[index++], next)
           next()
       queue.await callback
     else
       safeStackCall ((callback) ->
-        iterate = -> iterator array[index++], (err, done) ->
-          return callback(err) if err or done or (index >= count)
+        iterate = -> iterator array[index++], (err) ->
+          return callback(err) if err or (index >= count)
           if index and (index % IterationUtils.MAX_ITERATION_COUNT is 0) then IterationUtils.nextTick(iterate) else iterate()
         iterate()
       ), callback
@@ -60,15 +60,15 @@
       for start_index in [0..count] by IterationUtils.MAX_ITERATION_COUNT
         do (start_index) => queue.defer (callback) ->
           iteration_end = Math.min(start_index+IterationUtils.MAX_ITERATION_COUNT, count)
-          next = (err, done) =>
-            return callback(err) if err or done or (index >= iteration_end) or (array.length is 0)
+          next = (err) =>
+            return callback(err) if err or (index >= iteration_end) or (array.length is 0)
             index++; iterator(array.pop(), next)
           next()
       queue.await callback
     else
       safeStackCall ((callback) ->
-        iterate = -> index++; iterator array.pop(), (err, done) ->
-          return callback(err) if err or done or (index >= count) or (array.length is 0)
+        iterate = -> index++; iterator array.pop(), (err) ->
+          return callback(err) if err or (index >= count) or (array.length is 0)
           if index and (index % IterationUtils.MAX_ITERATION_COUNT is 0) then IterationUtils.nextTick(iterate) else iterate()
         iterate()
       ), callback


Pushing up latest, still having issues with D3.

--- app/assets/javascripts/capacity.js.coffee
+++ app/assets/javascripts/capacity.js.coffee
@@ -1,12 +1,13 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://coffeescript.org/
+
+# Setting up values for the graph
 data = {
   "total": 0,
   "reserved": 0,
   "standby": 0
 }
-
 gWidth = 960
 gHeight = 500
 radius = Math.min(gWidth, gHeight) / 2;
@@ -20,24 +21,29 @@
   .append("g")
   .attr("transform", "translate(" + gWidth / 2 + "," + gHeight / 2 + ")")

-addTotal = (num) ->
-  data["total"] += num
-  $("#total")[0].innerHTML = data["total"]
-
+# Updates the number of reserverd spots by the given amount
 addReserved = () ->
   num = getIncrement()
   data["reserved"] += num
   $("#reserved-total")[0].innerHTML = data["reserved"]
   addTotal(num)

+# Updates the number of standby spots by the given amount
 addStandby = () ->
   num = getIncrement()
   data["standby"] += num
   $("#standby-total")[0].innerHTML = data["standby"]
   addTotal(num)

+# Updates the total number of spots used
+addTotal = (num) ->
+  data["total"] += num
+  $("#total")[0].innerHTML = data["total"]
+
+# Returns the number of spots to increment by
 getIncrement = () ->
   return parseInt($("#inc").val())
+

 transform = () ->
   r = "translate(" + arc.centroid(this) + ")"
@@ -47,9 +53,22 @@
 updateGraph = () ->
   for label, val of data
     console.log(label + " " + val)
-    g = svg.selectAll(".arc").data(val).enter().append("g").attr("class", "arc")
-    g.append("path").attr("d", arc).style("fill", "#FFFFFF")
-    g.append("text").attr("transform", transform()).attr("dy", ".35em").style("text-anchor", "middle").text(label)
+    g = svg.selectAll(".arc")
+          .data(val)
+          .enter()
+          .append("g")
+          .attr("class", "arc")
+
+    g.append("path")
+      .attr("d", arc)
+      .style("fill", "#FFFFFF")
+    # g.append("text")
+    #   .attr("transform", ->
+    #       console.log("test")
+    #     )
+    #   .attr("dy", ".35em")
+    #   .style("text-anchor", "middle")
+    #   .text(label)

 $("#reserved").on("click", addReserved)
 $("#standby").on("click", addStandby)


Add chronograph millisecond, second, minute

--- app/assets/javascripts/chronograph.coffee
+++ app/assets/javascripts/chronograph.coffee
@@ -4,7 +4,22 @@
   constructor: (id, settings) ->
     @element = document.getElementById(id)
     @settings = settings
+    @millisecond = 0
+    @second = 0
+    @minute = 0
     @init()
+
+  ###
+    @second: 0 ~ 360
+  ###
+  setSecond: (second) ->
+    @second = second
+
+  ###
+    @minute: 0 ~ 360
+  ###
+  setMinute: (minute) ->
+    @minute = minute

   init: ->
     @canvas = document.createElement('canvas');
@@ -14,46 +29,83 @@
     @element.appendChild(@canvas);
     @dashboard()

-  deg: (deg) ->
+  ###
+    @deg: 0 ~ 360
+  ###
+  deg: (deg) =>
     (Math.PI/180) * deg - (Math.PI/180)*90

-  dashboard: ->
+  dashboard: =>
     ctx = @canvas.getContext("2d")
-
     ctx.clearRect(0, 0, @settings.size, @settings.size)

-
-    # hour
+    # minute
     ctx.beginPath()
     ctx.arc(@settings.size/2, @settings.size/2, @settings.size/2 - 12, 0, Math.PI*2, true)
     ctx.lineWidth = 22
-    ctx.strokeStyle = "#000000"
+    ctx.strokeStyle = "#000"
     ctx.globalAlpha = 0.325
-    ctx.shadowBlur  = 0
     ctx.stroke()
     ctx.closePath()

-    # minute
-    ctx.beginPath()
-    ctx.arc(@settings.size/2, @settings.size/2, @settings.size/2 - 38.5, 0, Math.PI*2, true)
-    ctx.lineWidth = 22
-    ctx.strokeStyle = "#000000"
-    ctx.globalAlpha = 0.225
-    ctx.shadowBlur  = 0
-    ctx.stroke()
-    ctx.closePath()
+    (=>
+      degs = @minute / 180 * 360
+      ctx.beginPath()
+      ctx.arc(@settings.size/2, @settings.size/2, @settings.size/2 - 12, @deg(0), @deg(degs))
+      ctx.lineWidth = 22
+      ctx.strokeStyle = "#FF00FF"
+      ctx.globalAlpha = 0.4
+      ctx.stroke()
+      ctx.closePath();
+    )()

     # second
     ctx.beginPath()
-    ctx.arc(@settings.size/2, @settings.size/2, @settings.size/2 - 55, 0, Math.PI*2, true)
-    ctx.lineWidth = 2
-    ctx.strokeStyle = "#000000"
+    ctx.arc(@settings.size/2, @settings.size/2, @settings.size/2 - 38.5, 0, Math.PI*2, true)
+    ctx.lineWidth = 22
+    ctx.strokeStyle = "#000"
     ctx.globalAlpha = 0.225
-    ctx.shadowBlur  = 0
     ctx.stroke()
     ctx.closePath()

-    ctx.globalAlpha = 125
+    (=>
+      degs = @second / 180 * 360
+      ctx.beginPath()
+      ctx.arc(@settings.size/2, @settings.size/2, @settings.size/2 - 38.5, @deg(0), @deg(degs))
+      ctx.lineWidth = 22
+      ctx.strokeStyle = "#0FF"
+      ctx.globalAlpha = 0.4
+      ctx.stroke()
+      ctx.closePath();
+    )()

-window.Chronograph = Chronograph+
+    # millisecond
+    ctx.beginPath()
+    ctx.arc(@settings.size/2, @settings.size/2, @settings.size/2 - 55, 0, Math.PI*2, true)
+    ctx.lineWidth = 2
+    ctx.strokeStyle = "#000"
+    ctx.globalAlpha = 0.225
+    ctx.stroke()
+    ctx.closePath()
+
+    ctx.beginPath()
+    degs = @millisecond / 180 * 360
+    ctx.arc(@settings.size/2, @settings.size/2, @settings.size/2 - 55, @deg(0), @deg(degs))
+    ctx.lineWidth = 2
+    ctx.strokeStyle = "#FFF"
+    ctx.globalAlpha = 0.155
+    ctx.stroke()
+    ctx.closePath();
+
+  run: ->
+    timer = setInterval((=>
+      @millisecond = 0 if @millisecond == 180
+      @millisecond++
+      @dashboard()
+    ), 1)
+
+window.Chronograph = Chronograph
+
+


fixed error on window init in projects js

--- app/assets/javascripts/projects.js.coffee
+++ app/assets/javascripts/projects.js.coffee
@@ -45,8 +45,7 @@

   return image

-$(window).load ->
-
+load = ->
   if $(".radio")
     $(".radio").click ->
       val = $($($(this).parent()).children()[0]).is ':checked'
@@ -168,3 +167,5 @@
     map.addLayer(window.markers)

   return
+
+$(document).ready load


fix!!!!!!!!!!

fix!!!!!!!!!!

--- app/assets/javascripts/restaurants.coffee
+++ app/assets/javascripts/restaurants.coffee
@@ -34,7 +34,7 @@
     thisId = "not-good-#{restaurantNumber}"
     newId = "good-#{restaurantNumber}"

-    document.getElementById(thisId).value = "いいね!"
+    document.getElementById(thisId).value = "いいね！取り消し"
     document.getElementById(thisId).id = newId
     document.getElementById(newID).parentNode.setAttribute('action', newURL)

@@ -46,7 +46,7 @@
     thisId = "good-#{restaurantNumber}"
     newId = "not-good-#{restaurantNumber}"

-    document.getElementById(thisId).value = "いいね!取り消し"
+    document.getElementById(thisId).value = "いいね！"
     document.getElementById(thisId).id = newId
     document.getElementById(newID).parentNode.setAttribute('action', newURL)


Fix backspace (0x8)

Fix backspace (0x8)

--- app/assets/javascripts/terminal.js.coffee
+++ app/assets/javascripts/terminal.js.coffee
@@ -189,8 +189,7 @@

   bs: ->
     if @cursorX > 0
-      @getLine()[@cursorX - 1] = " "
-      @cursorX -= 1
+      @cursorLeft()
       @updateLine()

   print: (text) ->


we aren't checking the dots any more

we aren't checking the dots any more

--- client/landing/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
+++ client/landing/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
@@ -205,7 +205,6 @@
           @showSuccess domain

     else # create a subdomain
-      # replace the dots at the end if there are any
       subDomainPattern = /^[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9]$/
       unless subDomainPattern.test domainName
         createButton.hideLoader()


Only normalize unpublished packages

Only normalize unpublished packages

Packages published to APM are normalized already, this catches locally
linked/developed packages.

--- src/package.coffee
+++ src/package.coffee
@@ -1,5 +1,5 @@
 path = require 'path'
-normalizePackageData = require 'normalize-package-data'
+normalizePackageData = null

 _ = require 'underscore-plus'
 async = require 'async'
@@ -24,6 +24,11 @@

     @resourcePathWithTrailingSlash ?= "#{atom.packages.resourcePath}#{path.sep}"
     packagePath?.startsWith(@resourcePathWithTrailingSlash)
+
+  @normalizeMetadata: (metadata) ->
+    unless metadata?._id
+      normalizePackageData ?= require 'normalize-package-data'
+      normalizePackageData(metadata)

   @loadMetadata: (packagePath, ignoreErrors=false) ->
     packageName = path.basename(packagePath)
@@ -33,7 +38,7 @@
       if metadataPath = CSON.resolve(path.join(packagePath, 'package'))
         try
           metadata = CSON.readFileSync(metadataPath)
-          normalizePackageData(metadata)
+          @normalizeMetadata(metadata)
         catch error
           throw error unless ignoreErrors


using dashboard defaults

using dashboard defaults

--- src/ubret/sw_viewer.coffee
+++ src/ubret/sw_viewer.coffee
@@ -28,11 +28,6 @@
   dimension: 440
   bands:  ['u', 'g', 'r', 'i', 'z']
   source: 'http://spacewarps.org.s3.amazonaws.com/subjects/raw/'
-
-  # Default parameters
-  defaultAlpha: 0.09
-  defaultQ: 1.0
-  defaultScales: [0.4, 0.6, 1.7]

   events:
@@ -51,9 +46,6 @@
     _.extend @, Ubret.Sequential

     super selector
-
-    # Set parameters
-    @stretch = 'linear'
     @collection = new Layers()
     @ready = false

@@ -152,6 +144,7 @@
     @wfits = new astro.WebFITS(@el, @dimension)
     unless @wfits.ctx?
       alert 'Something went wrong initializing the context'
+      return

     # Load offsets if they exists
     @wfits.xOffset = @opts.xOffset or @wfits.xOffset
@@ -160,6 +153,7 @@

   # Call when all FITS received and WebFITS library is received
   allChannelsReceived: (e) =>
+    @dfs = undefined
     @ready = true

     # Get extent for each layer and add to settings
@@ -171,20 +165,15 @@
     @opts.gMin = gMin
     @opts.gMax = gMax

-    @dfs = undefined
-
     @wfits.setCalibrations(1, 1, 1)

     # Get settings or fallback to defaults
-    scales  = @opts.scales or @defaultScales
-    alpha   = @opts.alpha or @defaultAlpha
-    Q       = @opts.q or @defaultQ
-    min     = @opts.extent?.min or gMin
-    max     = @opts.extent?.max or gMax
-
-    @wfits.setScales.apply(@wfits, scales)
-    @wfits.setAlpha(alpha)
-    @wfits.setQ(Q)
+    min = @opts.extent?.min or gMin
+    max = @opts.extent?.max or gMax
+
+    @wfits.setScales.apply(@wfits, @opts.scales)
+    @wfits.setAlpha(@opts.alpha)
+    @wfits.setQ(@opts.q)
     @wfits.setExtent(min, max)

     # Enable mouse controls
@@ -200,28 +189,18 @@
         })
     })

-    band = @opts.band
-    if band?
-      if band is 'gri'
-        @wfits.drawColor('i', 'r', 'g')
-      else
-        @wfits.setImage(band)
-        @wfits.setStretch(@opts.stretch or 'linear')
-    else
-      # Default to color composite
-      @wfits.drawColor('i', 'r', 'g')
-
+    @updateBand()
     @trigger 'swviewer:loaded'

   updateBand: =>
-    band = @opts.band
-
-    if @wfits?
-      if band is 'gri'
-        @wfits.drawColor('i', 'r', 'g')
-      else
-        @wfits.setImage(band)
-        @wfits.setStretch(@opts.stretch)
+    if @ready
+      band = @opts.band
+      if @wfits?
+        if band is 'gri'
+          @wfits.drawColor('i', 'r', 'g')
+        else
+          @wfits.setImage(band)
+          @wfits.setStretch(@opts.stretch)

   updateAlpha: =>
     @wfits?.setAlpha(@opts.alpha) if @ready
@@ -233,8 +212,7 @@
     @wfits?.setScales.apply(@wfits, @opts.scales) if @ready

   updateStretch: =>
-    @stretch = @opts.stretch
-    @wfits?.setStretch(@stretch) if @ready
+    @wfits?.setStretch(@opts.stretch) if @ready

   updateExtent: =>
     @wfits?.setExtent(@opts.extent.min, @opts.extent.max) if @ready


Open directory path instead of project path

This ensures that the repository opened is always project’s
path instead of the potentially non-existent path or file
specified to Project::setPath

--- src/project.coffee
+++ src/project.coffee
@@ -74,7 +74,7 @@
     if projectPath?
       directory = if fs.isDirectorySync(projectPath) then projectPath else path.dirname(projectPath)
       @rootDirectory = new Directory(directory)
-      if @repo = Git.open(projectPath, project: this)
+      if @repo = Git.open(directory, project: this)
         @repo.refreshIndex()
         @repo.refreshStatus()
     else


breaking up lines with semicolons

breaking up lines with semicolons

--- src/runtime.coffee
+++ src/runtime.coffee
@@ -222,7 +222,9 @@
   put_cl: (idx,val) -> @curr_frame().locals[idx] = val
   # Category 2 values (longs, doubles) take two slots in Java. Since we only
   # need one slot to represent a double in JS, we pad it with a null.
-  put_cl2: (idx,val) -> @put_cl(idx,val); UNSAFE? || @put_cl(idx+1,null)
+  put_cl2: (idx,val) ->
+    @put_cl(idx,val)
+    UNSAFE? || @put_cl(idx+1,null)

   push: (arg) -> @curr_frame().stack.push(arg)
   push2: (arg1, arg2) -> @curr_frame().stack.push(arg1, arg2)
@@ -230,7 +232,10 @@
     cs = @curr_frame().stack
     Array::push.apply(cs, args)
   pop: () -> @curr_frame().stack.pop()
-  pop2: () -> @pop(); @pop() # For category 2 values.
+  # For category 2 values.
+  pop2: () ->
+    @pop()
+    @pop()

   # Program counter manipulation.
   curr_pc: ()   -> @curr_frame().pc
@@ -500,7 +505,9 @@
           @meta_stack().pop()
           # Throw the exception.
           throw (new JavaException(rv))
-        nf.error = => @meta_stack().pop(); failure_fn(()->throw e)
+        nf.error = =>
+          @meta_stack().pop()
+          failure_fn (-> throw e)

         cls = @class_lookup c2t('java/lang/ExceptionInInitializerError')
         v = new JavaObject @, cls # new
@@ -678,7 +685,9 @@
         failure_fn = (e_cb, bytecode) =>
           if bytecode
             @meta_stack().push root.StackFrame.fake_frame("async_op")
-          @curr_frame().runner = ()=> @meta_stack().pop(); e_cb()
+          @curr_frame().runner = =>
+            @meta_stack().pop()
+            e_cb()
           @run_until_finished (->), no_threads, done_cb
         e.condition success_fn, failure_fn
       else


use setImmediate instead of process.nextTick

--- src/sequent.coffee
+++ src/sequent.coffee
@@ -1,6 +1,6 @@
 DEBUG = false

-doImmediately = process?.nextTick ? (callback) -> setTimeout callback, 0
+doImmediately = setImmediate ? (callback) -> setTimeout callback, 0
 idCounter = 1

 class Sequent


extract method for mongoimport and find where mongoimport is

--- src/service.coffee
+++ src/service.coffee
@@ -42,34 +42,7 @@
       unzip.pipe fstream
       unzip.on 'end', ->
         logger.info "downloaded #{datetime}.json"
-        args = [
-          '--host', process.env['MONGODB_HOST']
-          '--port', process.env['MONGODB_PORT']
-          '--db', 'popular_convention'
-          '--collection', datetime
-          '--file', "#{archiveDir}/#{datetime}.json"
-          '--type', 'json'
-        ]
-        if process.env['NODE_ENV'] is 'production'
-          args = args.concat [
-            '--username', process.env["MONGODB_USER"]
-            '--password', process.env["MONGODB_PASS"]
-          ]
-        mongoimport = spawn '/Users/outsider/bin/mongoimport', args
-        mongoimport.stderr.on 'data', (data) ->
-          logger.error "mongoimport error occured"
-        mongoimport.on 'exit', (code) ->
-          logger.info "mongoimport exited with code #{code}"
-          doc =
-            file: datetime
-            inProgress: false
-            completed: false
-            summarize: false
-          persistence.insertWorklogs doc, (->
-            callback()) if code is 0
-          callback(code) if code isnt 0
-          fs.unlink "#{archiveDir}/#{datetime}.json", (err) ->
-            logger.error "delete #{archiveDir}/#{datetime}.json" if err
+        importIntoMongodb(datetime, callback)
     ).on 'error', (e) ->
       logger.error 'fetch githubarchive: ', {err: e}

@@ -378,6 +351,56 @@
     scala: 'scala'
   map[lang]

+importIntoMongodb = (datetime, callback) ->
+  console.log "datetime #{datetime}"
+
+  mongoImportCmd = ""
+
+  which = spawn 'which', ["mongoimport"]
+  which.stdout.on 'data', (data) ->
+    mongoImportCmd = data.toString()
+  which.on 'exit', (code) ->
+    if (code is 0)
+      args = [
+        '--host', process.env['MONGODB_HOST']
+        '--port', process.env['MONGODB_PORT']
+        '--db', 'popular_convention'
+        '--collection', datetime
+        '--file', "#{archiveDir}/#{datetime}.json"
+        '--type', 'json'
+      ]
+      if process.env['NODE_ENV'] is 'production'
+        args = args.concat [
+          '--username', process.env["MONGODB_USER"]
+          '--password', process.env["MONGODB_PASS"]
+        ]
+
+      # remove trailing new line
+      mongoImportCmd = mongoImportCmd.match(/([\w\/]+)/)[0]
+
+      mongoimport = spawn mongoImportCmd, args
+      mongoimport.stderr.on 'data', (data) ->
+        logger.error "mongoimport error occured"
+      mongoimport.on 'exit', (code) ->
+        logger.info "mongoimport exited with code #{code}"
+        doc =
+          file: datetime
+          inProgress: false
+          completed: false
+          summarize: false
+        persistence.insertWorklogs doc, (->
+          callback()) if code is 0
+        callback(code) if code isnt 0
+
+        deleteArchiveFile(datetime)
+    else
+      logger.error "mongoimport doesn't exist."
+      callback(code)
+
+deleteArchiveFile = (datetime) ->
+  fs.unlink "#{archiveDir}/#{datetime}.json", (err) ->
+    logger.error "delete #{archiveDir}/#{datetime}.json" if err
+
 getYesterday = ->
   now = new Date
   year = now.getFullYear()


Implemented check if a systemd unit exists function

--- src/systemd.coffee
+++ src/systemd.coffee
@@ -17,3 +17,17 @@
 	bus.getInterfaceAsync(SERVICE, MANAGER_OBJECT, MANAGER_INTERFACE)
 	.then (manager) ->
 		manager.StopUnitAsync(unit, mode)
+
+exports.exists = (unit, mode='fail') ->
+	new Promise (resolve, reject) ->
+		bus.getInterfaceAsync(SERVICE, MANAGER_OBJECT, MANAGER_INTERFACE)
+		.then (manager) ->
+			manager.ListUnitsAsync()
+		.then (units) ->
+			for obj in units
+				if unit of obj
+					resolve (true)
+			resolve (false)
+		.catch (e) ->
+			reject (e)
+


remove http:// from delete search input

remove http:// from delete search input

--- src/uapanfw.coffee
+++ src/uapanfw.coffee
@@ -212,6 +212,13 @@
     bl_type = msg.match[1]
     bl_search = msg.match[2]

+    if bl_search.toLowerCase().indexOf('https://') == 0
+      usermsg = "Blacklisting of https links not supported."
+      return msg.reply usermsg
+
+    if bl_search.toLowerCase().indexOf('http') == 0
+      bl_search = bl_search.replace(/http:\/\//i,'')
+
     logmsg = "#{modulename}: #{who} requested: " +
       "blacklist delete #{bl_type} #{bl_search}"
     robot.logger.info logmsg
@@ -226,7 +233,8 @@
         new_bl.push obj
         continue
       if bl_search and obj.val.indexOf(bl_search) == -1
-        console.log 'search: indexOf['+ bl_search +'] not found in [' + obj.val +']'
+        val = obj.val
+        console.log 'search: indexOf['+ bl_search +'] not found in ['+ val +']'
         new_bl.push obj
         continue
       if expires.isBefore() # now


Hide the play button on the inventory screen until the items have fully loaded.

--- app/views/game-menu/InventoryModal.coffee
+++ app/views/game-menu/InventoryModal.coffee
@@ -80,7 +80,9 @@

   afterRender: ->
     super()
+    @$el.find('.modal-footer button').css('visibility', 'hidden')
     return unless @supermodel.finished()
+    @$el.find('.modal-footer button').css('visibility', 'visible')

     keys = (item.get('original') for item in @items.models)
     itemMap = _.zipObject keys, @items.models


Delete dead code

Delete dead code

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
@@ -73,14 +73,6 @@
 							callback null, result
 						else
 							callback null, result
-						# else
-						# 	# get keys from the backend
-						# 	referencesSearch = window?._ide?.referencesSearchManager
-						# 	if referencesSearch
-						# 		referencesSearch.getReferenceKeys (keys) =>
-						# 			keys.forEach (key) ->
-						# 				result.push(referenceKeyToAutocompleteEntry(key))
-						# 			callback null, result

 			@editor.completers = [@suggestionManager, SnippetCompleter, ReferencesCompleter]


Initial work on validForms, but not yet tested or finished. And I'm falling asleep

--- src/wordnet.coffee
+++ src/wordnet.coffee
@@ -227,10 +227,8 @@

-  _forms: (string) ->
-    wordnet = @
-
-    [word, pos] = string.split('#')
+  _forms: (word, pos) ->
+    wordnet = @

     lword = word.toLowerCase()

@@ -245,9 +243,9 @@
       return tokenDetach(token[0] + "#" + pos)

     ## Otherwise, handle the forms recursively
-    forms = tokens.map (token) -> _forms(token)
-
-    ## Now generate all possible token sequences (collocations)
+    forms = tokens.map (token) -> _forms(token, pos)
+
+    ## Now generate all possible token sequenc,es (collocations)
     rtn = []
     index = (0 for token in tokens)

@@ -267,5 +265,21 @@
     return rtn

+    forms: (string) ->
+      [word, pos, sense] = string.split('#')
+      rtn = _forms(word, pos)
+      (element + "#" + pos for element in rtn)
+
+
+    validForms: (string) ->
+      [word, pos, sense] = string.split('#')
+
+      if ! pos
+        ['n', 'v', 'a', 'r']
+          .map (pos) -> @validForms(string + "#" + pos)
+          .reduce (previous, current) -> previous.concat(current)
+      else
+        possibleForms = forms(word + "#" + pos)
+

 module.exports = WordNet


JStackTemplate: Tests: add tests for clone and update operations on cloned stacks

--- workers/social/lib/social/models/computeproviders/stacktemplate.test.coffee
+++ workers/social/lib/social/models/computeproviders/stacktemplate.test.coffee
@@ -360,6 +360,46 @@
           async.series queue, done

+      it 'should be able to update a cloned stacktemplate', (done) ->
+
+        withConvertedUserAndStackTemplate (data) ->
+          { client, stackTemplate, stackTemplateData } = data
+
+          stackTemplate.clone client, (err, clonedTemplate) ->
+            expect(err).to.not.exist
+
+            queue = [
+
+              (next) ->
+                params = { title: 'title should be updated' }
+                clonedTemplate.update$ client, params, (err) ->
+                  expect(err).to.not.exist
+                  expect(clonedTemplate.title).to.be.equal params.title
+                  next()
+
+              (next) ->
+                params = { template: '{}', rawContent: '--' }
+                clonedTemplate.update$ client, params, (err, clonedTemplate) ->
+                  expect(err)                                .to.not.exist
+                  expect(clonedTemplate.template.content)    .to.be.equal params.template
+                  expect(clonedTemplate.template.rawContent) .to.be.equal params.rawContent
+                  expect(clonedTemplate.template.sum)        .to.be.equal 'bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f' # sum of "{}"
+                  next()
+
+              (next) ->
+                params = { config: { groupStack: yes } }
+                clonedTemplate.update$ client, params, (err, clonedTemplate) ->
+                  expect(err).to.not.exist
+                  expect(clonedTemplate.config.groupStack)   .to.be.equal yes
+                  # sum needs to stay same
+                  expect(clonedTemplate.template.sum)        .to.be.equal 'bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f'
+                  next()
+
+            ]
+
+            async.series queue, done
+
+
   describe '#samples()', ->

     it 'should return sample templates for all stack supported providers', (done) ->
@@ -381,6 +421,32 @@

           async.series queue, done

+
+  describe 'clone()', ->
+
+    describe 'when user doesnt have the permission', ->
+
+      it 'should fail to clone a stacktemplate', (done) ->
+
+        withConvertedUserAndStackTemplate ({ stackTemplate }) ->
+          expectAccessDenied stackTemplate, 'clone', done
+
+
+    describe 'when user has the permission', ->
+
+      it 'should be able to clone a stacktemplate', (done) ->
+
+        withConvertedUserAndStackTemplate (data) ->
+          { client, stackTemplate, stackTemplateData } = data
+
+          stackTemplate.clone client, (err, clonedTemplate) ->
+            expect(err)                              .to.not.exist
+            expect(clonedTemplate)                   .to.exist
+            expect(clonedTemplate.title)             .to.be.equal "#{stackTemplate.title} - clone"
+            expect(clonedTemplate.config.clonedFrom) .to.be.equal stackTemplate._id
+            expect(clonedTemplate.config.clonedSum)  .to.be.equal stackTemplate.template.sum
+            done()
+
 beforeTests()

 runTests()


Use dispose instead of off

Use dispose instead of off

--- lib/grammar-status-view.coffee
+++ lib/grammar-status-view.coffee
@@ -36,7 +36,7 @@
     @activeItemSubscription?.dispose()
     @grammarSubscription?.dispose()
     @clickSubscription?.dispose()
-    @configSubscription?.off()
+    @configSubscription?.dispose()
     @statusBarTile.destroy()

   getActiveTextEditor: ->


[Activity] featuredContent flag is removed

--- client/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
+++ client/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
@@ -79,7 +79,7 @@

     @emit "teasersLoaded"

-  listActivitiesFromCache:(cache, index, animation, isFeaturedContent)->
+  listActivitiesFromCache:(cache, index, animation)->
     @hideLazyLoader()
     return  unless cache.overview?.length > 0
     activityIds = []
@@ -105,7 +105,7 @@
           view.slideIn => @removeFromHiddenItems view
           activityIds.push activity.teaser._id

-    @checkIfLikedBefore activityIds  unless isFeaturedContent
+    @checkIfLikedBefore activityIds

     @lastItemTimeStamp = cache.from


Only have the decorations on the gutter

Only have the decorations on the gutter

--- lib/react-git-diff-view.coffee
+++ lib/react-git-diff-view.coffee
@@ -109,6 +109,6 @@

   markRange: (startRow, endRow, klass) ->
     marker = @editor.markBufferRange([[startRow, 0], [endRow, Infinity]], invalidate: 'never')
-    @editor.addDecorationForMarker(marker, type: ['gutter', 'line'], class: klass)
+    @editor.addDecorationForMarker(marker, type: 'gutter', class: klass)
     @markers ?= []
     @markers.push(marker)


respond with an @reply

respond with an @reply

--- skills/wiki.coffee
+++ skills/wiki.coffee
@@ -15,7 +15,7 @@
   @get url, (body) ->
     try
       if body[1][0]
-        self.message channel, "http://en.wikipedia.org/wiki/#{escape body[1][0]}"
+        self.message channel, "@" + from.split(' ')[0] + " here you are: http://en.wikipedia.org/wiki/#{escape body[1][0]}"
       else
         self.message channel, "nothin'"
     catch e


Minor increase of err domain handling (needs more optimization)

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -128,7 +128,8 @@
     @provider: (method) ->
         noMethod = "a #{method} is not a function"
         invalidArgs = "has to have at least 1 parameter"
-        applicator = _.partial @covering, method
+        assert bound = this.covering.bind this
+        applicator = _.partial bound, method
         assert _.isFunction(method), noMethod
         assert method.length >= 1, invalidArgs
         method.provider = Object.create {}
@@ -140,18 +141,19 @@
     # around the provider invocation procedure. This wrapping is of
     # protective nature. It also exposes some goodies for the provider.
     # Such as Socket.IO handle, session if available and the context.
-    @covering: (method, socket, context) -> (parameters..., callback) ->
+    @covering: (method, socket, context) ->
         assert _.isFunction o = Marshal.serialize
         assert _.isFunction i = Marshal.deserialize
         assert session = socket?.handshake?.session
-        guarded = @constructor.guarded? method, socket
+        assert _.isObject guarded = @guarded method, socket
         assert _.isFunction g = guarded.run.bind guarded
         s = (f) => session.save -> f.apply this, arguments
-        execute = (a...) => g => method.apply this, i(a)
-        respond = (a...) => g => s => callback.apply this, o(a)
-        respond.socket = socket; respond.context = context
-        respond.session = session; socket.session = session
-        return execute parameters..., respond
+        assert context; return (parameters..., callback) ->
+            execute = (a...) => g => method.apply this, i(a)
+            respond = (a...) => g => s => callback.apply this, o(a)
+            respond.socket = socket; respond.context = context
+            respond.session = session; socket.session = session
+            return execute parameters..., respond

     # This server side method is called on the context prior to the
     # context being compiled and flushed down to the client site. The


Implement dialogue support

Implement dialogue support

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -73,6 +73,13 @@
     # place to implementation various schemes for authorization. If
     # you wish to decline, just don't call `next` and close socket.
     screening: (context, socket, binder, next) -> next()
+
+    # A usable hook that gets asynchronously invoked once a sentence
+    # comes through an opened channel. This happens every time when
+    # a client tries to invoke a server site provider method. This
+    # is a good place to validate if an invocation is legitimate or
+    # not. If you do not invoke `next` then the call won't happen!
+    sentence: (socket, name, provider, args, next) -> next()

     # An internal, static method that is used to obtain gurading
     # domains for each of the declared server site providers. Please
@@ -192,8 +199,12 @@
             providing = value?.providing or null
             return unless _.isFunction providing
             assert _.isFunction(value), internal
-            bound = (s) => providing(socket).bind @
-            socket.on name, bound socket; next()
+            bound = providing(socket).bind this
+            socket.on name, (args..., callback) =>
+                sentence = @upstreamAsync "sentence", =>
+                    bound.call this, args..., callback
+                sentence socket, name, value, args
+            return next()

     # A hook that will be called prior to registering the service
     # implementation. Please refer to this prototype signature for


Minor syntax correction

Minor syntax correction

--- library/nucleus/content.coffee
+++ library/nucleus/content.coffee
@@ -60,7 +60,7 @@
     @associate: (negotiator) ->
         isValid = _.isFunction negotiator
         invalid = "Checker is not a valid method"
-        throw new Error(invalid) unless isValid
+        throw new Error invalid unless isValid
         (@registry ?= []).unshift negotiator

     # Associate the JSON negotiator with the broker. This method


Opein in background runtime option for application manager

--- client/landing/app/MainApp/ApplicationManager.coffee
+++ client/landing/app/MainApp/ApplicationManager.coffee
@@ -47,17 +47,17 @@

   open: do ->

-    createOrShow = (appOptions, appParams, callback = noop)->
-
-      appManager  = KD.getSingleton "appManager"
-      name        = appOptions?.name
-
-      return appManager.handleAppNotFound()  unless name
-
-      appInstance = appManager.get name
-      cb          = -> appManager.show appOptions, callback
-      if appInstance then do cb
-      else appManager.create name, appParams, cb
+    createOrShow = (appOptions = {}, appParams, callback = noop)->
+
+      name = appOptions?.name
+      return @handleAppNotFound()  unless name
+
+      appInstance = @get name
+      cb = if appParams.background or appOptions.background
+      then -> KD.utils.defer callback
+      else @show.bind this, name, appParams, callback
+
+      if appInstance then cb() else @create name, appParams, cb

     (name, options, callback)->

@@ -67,7 +67,7 @@
       options            or= {}
       appOptions           = KD.getAppOptions name
       appParams            = options.params or {}
-      defaultCallback      = -> createOrShow appOptions, appParams, callback
+      defaultCallback      = createOrShow.bind this, appOptions, appParams, callback
       kodingAppsController = KD.getSingleton("kodingAppsController")

       # If app has a preCondition then first check condition in it
@@ -212,12 +212,10 @@
         KD.getSingleton("kodingAppsController").putAppResources appInstance, callback
       callback? appInstance

-  show:(appOptions, callback)->
-
-    appInstance = @get appOptions.name
-
-    if appOptions.background
-      return @utils.defer -> callback? appInstance
+  show:(name, appParams, callback)->
+
+    appOptions  = KD.getAppOptions name
+    appInstance = @get name

     appView     = appInstance.getView?()
     return unless appView


Fix bug when migrating old task data

Fix bug when migrating old task data

--- views/components/main/parts/task-panel.cjsx
+++ views/components/main/parts/task-panel.cjsx
@@ -128,9 +128,9 @@
   flag = false
   for id, q of questRecord
     continue unless q.active and q[e]?
-    continue if questGoals[id][e].shipType? and options.shipType not in questGoals[id][e].shipType
-    continue if questGoals[id][e].mission? and options.mission not in questGoals[id][e].mission
-    continue if questGoals[id][e].maparea? and options.maparea not in questGoals[id][e].maparea
+    continue if questGoals[id][e]?.shipType? and options.shipType not in questGoals[id][e].shipType
+    continue if questGoals[id][e]?.mission? and options.mission not in questGoals[id][e].mission
+    continue if questGoals[id][e]?.maparea? and options.maparea not in questGoals[id][e].maparea
     before = q[e].count
     q[e].count = Math.min(q[e].required, q[e].count + delta)
     q.count += q[e].count - before


fix weird merge conflict

fix weird merge conflict

--- core/app/backbone/collections/feed_activities.coffee
+++ core/app/backbone/collections/feed_activities.coffee
@@ -1,8 +1,6 @@
 class window.GlobalFeedActivities extends Backbone.Factlink.Collection
   _.extend @prototype, AutoloadCollectionOnTimestamp
   model: Activity
-
-  url: -> '/api/beta/feed'

   initialize: (models, options={}) ->
     @_count = options.count || 20


Remove unnecessary do nesting; there are no private variables in this scope.

--- core/app/backbone/factlink/tooltip_marionette.coffee
+++ core/app/backbone/factlink/tooltip_marionette.coffee
@@ -1,43 +1,38 @@
 Backbone.Factlink ||= {}

-do ->
-  #options:
-  #  parentView: marionette view
-  #  tooltipViewFactory: -> view
-  #  selector: selector identifying what can be hovered over.
-  #  $offsetParent: a dom node within which to position the
-  #    tooltip with respect to the target (hovered) node. By
-  #    default, uses parentView.$el.
+#options:
+#  parentView: marionette view
+#  tooltipViewFactory: -> view
+#  selector: selector identifying what can be hovered over.
+#  $offsetParent: a dom node within which to position the
+#    tooltip with respect to the target (hovered) node. By
+#    default, uses parentView.$el.

-  Backbone.Factlink.Tooltip = (options) ->
-    positionedRegion = null
+Backbone.Factlink.Tooltip = (options) ->
+  positionedRegion =
+    new Backbone.Factlink.PositionedRegion options
+
+  maker = ($el, $target) ->
+    popoverOptions = _.extend {}, options,
+      contentView: options.tooltipViewFactory()
+
+    positionedRegion.bindToElement $target,
+      options.$offsetParent || $el
+    positionedRegion.show new PopoverView popoverOptions
+    positionedRegion.updatePosition()
+    positionedRegion.$el
+
+  remover = ->
+    positionedRegion.resetFade()

-    maker = ($el, $target) ->
-      throw "Cannot open duplicate tooltip" if positionedRegion?
-      regionOptions = _.extend {}, options,
-        contentView: options.tooltipViewFactory()
+  closeHandler = Backbone.Factlink.TooltipJQ
+    $el: options.parentView.$el
+    selector: options.selector
+    makeTooltip: maker
+    removeTooltip: remover

-      positionedRegion =
-        new Backbone.Factlink.PositionedRegion regionOptions
-      positionedRegion.bindToElement $target,
-        options.$offsetParent || $el
-      positionedRegion.show new PopoverView regionOptions
-      positionedRegion.updatePosition()
-      positionedRegion.$el
-
-    remover = ->
-      positionedRegion.resetFade()
-      positionedRegion = null
-
-
-    closeHandler = Backbone.Factlink.TooltipJQ
-      $el: options.parentView.$el
-      selector: options.selector
-      makeTooltip: maker
-      removeTooltip: remover
-
-    options.parentView.on 'close', closeHandler.close
+  options.parentView.on 'close', closeHandler.close


Fix usage of reserved word

Fix usage of reserved word

'private' is a reserved as of coffee-script 1.3.1

--- src/content.coffee
+++ src/content.coffee
@@ -86,11 +86,11 @@
 # Class ContentTree
 # not using Class since we need a clean prototype
 ContentTree = (filename) ->
-  private = {directories: []}
+  groups = {directories: []}
   for plugin in contentPlugins
-    private[plugin.treeName] = []
+    groups[plugin.treeName] = []
   Object.defineProperty @, '_',
-    get: -> private
+    get: -> groups
   Object.defineProperty @, 'filename',
     get: -> filename
   Object.defineProperty @, 'index',


split creation of run context prototype

split creation of run context prototype

--- src/context.coffee
+++ src/context.coffee
@@ -153,120 +153,123 @@
     ctx.add_component nested_context.component_list
     nested_context.apply_to fn, args

+  # TODO this is an awful name
+  create_context_run_context = ->
+    asyncs = new Bacon.Bus
+
+    changes: new Bacon.Bus
+    pending: asyncs.scan 0, (a, b) -> a + b
+    current_options: {}
+    component_list: React.ComponentList()
+
+    options: -> @current_options
+
+    apply_to: (fn, args) ->
+      previous_context = @scope_context.current_context
+      @scope_context.current_context = @
+      try
+        fn.apply @, args
+      finally
+        @scope_context.current_context = previous_context
+
+    in_running_context: (fn, args) ->
+      throw new Error 'no active running context. did you call an async function without keeping the context?' unless running_context_binding?
+      running_context_binding.apply_to fn, args
+
+    # returns a function that calls its argument in the current context
+    capture_context: ->
+      context = @
+      running_context = running_context_binding
+      (fn, args) ->
+        previous_running_context_binding = running_context_binding
+        running_context_binding = running_context
+        try
+          context.apply_to fn, args
+        finally
+          running_context_binding = previous_running_context_binding
+
+    # wraps a function so that it is called in the current context
+    keeping_context: (fn) ->
+      restoring_context = @capture_context()
+      ->
+        restoring_context fn, arguments
+
+    add_renderable: (renderable) ->
+      @add_component component_for_renderable renderable
+      ignore
+
+    add_component: (component) ->
+      @component_list.add_component component
+
+    add_rendered: (rendered) ->
+      @add_renderable _lead_render: -> rendered
+
+    render: render
+
+    empty: -> @component_list.empty()
+
+    div: (contents) ->
+      $div = $('<div/>')
+      if contents?
+        if _.isFunction contents
+          return nested_item @, contents
+        else
+          $div.append contents
+      @add_rendered $div
+      $div
+
+    # makes o renderable using the given function or renderable
+    renderable: (o, fn) ->
+      if is_component fn
+        o._lead_render = fn
+      else if fn._lead_render?
+        o._lead_render = fn._lead_render
+      else
+        nested_context = @create_nested_context
+          component_list: add_component: -> throw new Error 'Output functions not allowed inside a renderable'
+        o._lead_render = -> nested_context.apply_to fn
+      o
+
+    create_nested_context: (overrides) ->
+      nested_context = _.extend create_new_run_context(@), overrides
+
+    detached: (fn, args) ->
+      nested_context = create_nested_renderable_context @
+      nested_context.apply_to fn, args
+      nested_context.component_list
+
+    value: (value) -> _lead_context_fn_value: value
+
+    async: (fn) ->
+      start_time = new Date
+      promise = nested_item @, fn
+
+      @promise_status promise, start_time
+
+      asyncs.push 1
+      promise.finally =>
+        asyncs.push -1
+        @changes.push true
+      promise
+    scoped_eval: scoped_eval
+
   create_run_context = (extra_contexts) ->
-    asyncs = new Bacon.Bus
-
-    run_context_prototype = _.extend {}, extra_contexts...,
-      changes: new Bacon.Bus
-      pending: asyncs.scan 0, (a, b) -> a + b
-      current_options: {}
-      component_list: React.ComponentList()
-
-      options: -> @current_options
-
-      apply_to: (fn, args) ->
-        previous_context = @scope_context.current_context
-        @scope_context.current_context = @
-        try
-          fn.apply @, args
-        finally
-          @scope_context.current_context = previous_context
-
-      in_running_context: (fn, args) ->
-        throw new Error 'no active running context. did you call an async function without keeping the context?' unless running_context_binding?
-        running_context_binding.apply_to fn, args
-
-      # returns a function that calls its argument in the current context
-      capture_context: ->
-        context = @
-        running_context = running_context_binding
-        (fn, args) ->
-          previous_running_context_binding = running_context_binding
-          running_context_binding = running_context
-          try
-            context.apply_to fn, args
-          finally
-            running_context_binding = previous_running_context_binding
-
-      # wraps a function so that it is called in the current context
-      keeping_context: (fn) ->
-        restoring_context = @capture_context()
-        ->
-          restoring_context fn, arguments
-
-      add_renderable: (renderable) ->
-        @add_component component_for_renderable renderable
-        ignore
-
-      add_component: (component) ->
-        @component_list.add_component component
-
-      add_rendered: (rendered) ->
-        @add_renderable _lead_render: -> rendered
-
-      render: render
-
-      empty: -> @component_list.empty()
-
-      div: (contents) ->
-        $div = $('<div/>')
-        if contents?
-          if _.isFunction contents
-            return nested_item @, contents
-          else
-            $div.append contents
-        @add_rendered $div
-        $div
-
-      # makes o renderable using the given function or renderable
-      renderable: (o, fn) ->
-        if is_component fn
-          o._lead_render = fn
-        else if fn._lead_render?
-          o._lead_render = fn._lead_render
-        else
-          nested_context = @create_nested_context
-            component_list: add_component: -> throw new Error 'Output functions not allowed inside a renderable'
-          o._lead_render = -> nested_context.apply_to fn
-        o
-
-      create_nested_context: (overrides) ->
-        nested_context = _.extend create_new_run_context(run_context_prototype, @), overrides
-
-      detached: (fn, args) ->
-        nested_context = create_nested_renderable_context @
-        nested_context.apply_to fn, args
-        nested_context.component_list
-
-      value: (value) -> _lead_context_fn_value: value
-
-      async: (fn) ->
-        start_time = new Date
-        promise = nested_item @, fn
-
-        @promise_status promise, start_time
-
-        asyncs.push 1
-        promise.finally =>
-          asyncs.push -1
-          @changes.push true
-        promise
-      scoped_eval: scoped_eval
-
+    run_context_prototype = _.extend {}, extra_contexts..., create_context_run_context()
+    run_context_prototype.run_context_prototype = run_context_prototype
     scope_context = {}
     run_context_prototype.fns = bind_context_fns scope_context, run_context_prototype.imported_context_fns
     run_context_prototype.scope_context = scope_context

-    scope_context.current_context = create_new_run_context run_context_prototype, run_context_prototype
-
-  create_new_run_context = (run_context_prototype, parent) ->
+    scope_context.current_context = create_new_run_context run_context_prototype
+
+  create_new_run_context = (parent) ->
     new_context = Object.create parent

     fns_and_vars = bind_context_fns new_context, new_context.context_fns
     _.each new_context.vars, (vars, name) -> _.extend (fns_and_vars[name] ?= {}), vars
     _.extend fns_and_vars, fns_and_vars.builtins
     _.each fns_and_vars, (mod, name) ->
-      if run_context_prototype[name]?
+      if parent.run_context_prototype[name]?
         console.warn mod._lead_context_name, 'would overwrite core'
       else
         new_context[name] = mod


use example for auto docs of graphite functions

--- src/context.coffee
+++ src/context.coffee
@@ -22,7 +22,7 @@
       lead_string = core.to_string object
       if _.isFunction object
         @cli.text "#{lead_string} is a Graphite function"
-        run_before @input_cell, "docs #{object.values[0]}"
+        @cli.example "docs #{object.values[0]}"
       else
         @cli.text "What do you want to do with #{lead_string}?"
         for f in ['data', 'graph', 'img', 'url']


add restart command

add restart command

--- src/decking.coffee
+++ src/decking.coffee
@@ -57,11 +57,12 @@

       Commands:
         build   build an image or pass 'all' to build all
-        create  create a cluster
-        start   start a cluster of containers
-        stop    stop a cluster
-        status  check the status of a cluster's containers
-        attach  attach to all running containers in a cluster
+        create   create a cluster
+        start    start a cluster of containers
+        stop     stop a cluster
+        restart  restart a cluster
+        status   check the status of a cluster's containers
+        attach   attach to all running containers in a cluster
       """

       log help
@@ -84,6 +85,7 @@
     create: (done) -> @_run "create", done
     start: (done) -> @_run "start", done
     stop: (done) -> @_run "stop", done
+    restart: (done) -> @_run "restart", done
     status: (done) -> @_run "status", done
     attach: (done) -> @_run "attach", done

@@ -128,6 +130,26 @@
         else
           logAction name, "skipping (already stopped)"
           callback null
+
+    resolveOrder @config, cluster, (list) ->
+
+      validateContainerPresence list, (err) ->
+        return done err if err
+
+        async.eachSeries list, iterator, done
+
+  restart: (cluster, done) ->
+    iterator = (details, callback) ->
+      name = details.name
+      container = docker.getContainer name
+      isRunning container, (err, running) ->
+        if running
+          logAction name, "restarting..."
+          container.stop (err) ->
+            container.start callback
+        else
+          logAction name, "starting..."
+          container.start callback

     resolveOrder @config, cluster, (list) ->


Element#removeClass fixed. Fixes Issue #e

--- src/element.coffee
+++ src/element.coffee
@@ -120,7 +120,7 @@
     i = arr.indexOf className
     if i != -1
       arr.splice i, 1
-      @setClasses
+      @setClasses arr

   getValue: () ->
     @_.value


:racehorse: Return from emit so we don’t create a garbage array!

--- src/emitter.coffee
+++ src/emitter.coffee
@@ -91,3 +91,4 @@
   emit: (eventName, value) ->
     if handlers = @handlersByEventName?[eventName]
       handler(value) for handler in handlers
+    return


Log stack traces

Log stack traces

--- src/enfield.coffee
+++ src/enfield.coffee
@@ -62,16 +62,21 @@
         conf.get(options)
           .then (config) ->
             printConfiguration config
+            log.silly "enfield", "Full configuration: %j", config

             if command is 'build'
               exports.build(config)
                 .fail (err) ->
                   log.error "enfield", "Generation error: #{err.message}"
+                  if err.stack
+                    log.verbose "enfield", "Stack trace: %s", err.stack
                   process.exit -1
             else
               exports.serve(config)
                 .fail (err) ->
                   log.error "enfield", "Could not start server: #{err.message}"
+                  if err.stack
+                    log.verbose "enfield", "Stack trace: %s", err.stack
                   process.exit -1
           .fail (err) ->
             log.error "enfield", "Could not load configuration: #{err.message}"


Revert "Don't use deprecated [l]statSyncNoException methods in Electron >= 3"

This reverts commit 7c6bd36d2359cac7b5a58871f09e497510867f92.

--- src/fs-plus.coffee
+++ src/fs-plus.coffee
@@ -552,30 +552,8 @@
   # Returns `true` if case sensitive, `false` otherwise.
   isCaseSensitive: -> not fsPlus.isCaseInsensitive()

-  # Public: Calls `fs.statSync`, catching all exceptions raised. This
-  # method calls `fs.statSyncNoException` when provided by the underlying
-  # `fs` module (Electron < 3.0).
-  #
-  # Returns `fs.Stats` if the file exists, `undefined` otherwise.
-  statSyncNoException: (args...) ->
-    statSyncNoException(args...)
-
-  # Public: Calls `fs.lstatSync`, catching all exceptions raised.  This
-  # method calls `fs.statSyncNoException` when provided by the underlying
-  # `fs` module (Electron < 3.0).
-  #
-  # Returns `fs.Stats` if the file exists, `undefined` otherwise.
-  lstatSyncNoException: (args...) ->
-    lstatSyncNoException(args...)
-
-# Built-in [l]statSyncNoException methods are only provided in
-# Electron releases before 3.0.
-isElectron3OrHigher =
-  process.versions.electron &&
-  parseInt(process.versions.electron.split('.')[0]) >= 3
-
 statSyncNoException = (args...) ->
-  if fs.statSyncNoException and !isElectron3OrHigher
+  if fs.statSyncNoException
     fs.statSyncNoException(args...)
   else
     try
@@ -584,7 +562,7 @@
       false

 lstatSyncNoException = (args...) ->
-  if fs.lstatSyncNoException and !isElectron3OrHigher
+  if fs.lstatSyncNoException
     fs.lstatSyncNoException(args...)
   else
     try


Add ".tif" as a recognised TIFF extension

--- src/fs-plus.coffee
+++ src/fs-plus.coffee
@@ -553,6 +553,7 @@
   '.jpeg': true
   '.jpg':  true
   '.png':  true
+  '.tif':  true
   '.tiff': true
   '.webp': true


i meant _stage_ right to left

i meant _stage_ right to left

--- src/grafana.coffee
+++ src/grafana.coffee
@@ -3,7 +3,7 @@
 #
 #   Examples:
 #   - `hubot graf db graphite-carbon-metrics` - Get all panels in the dashboard
-#   - `hubot graf db graphite-carbon-metrics:3` - Get only the third panel, from right to left, of a particular dashboard
+#   - `hubot graf db graphite-carbon-metrics:3` - Get only the third panel, from left to right, of a particular dashboard
 #   - `hubot graf db graphite-carbon-metrics:panel-8` - Get only the panel of a particular dashboard with the ID of 8
 #   - `hubot graf db graphite-carbon-metrics:cpu` - Get only the panels containing "cpu" (case insensitive) in the title
 #   - `hubot graf db graphite-carbon-metrics now-12hr` - Get a dashboard with a window of 12 hours ago to now


Fix check for modifier-only keyboard events

There was previously a check of the BrowserModifiers set, but it wasn't
getting populated correctly. This renames the set to
KeyboardEventModifiers and correctly populates it, eliminating the need
for a later check.

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -4,8 +4,8 @@
 AtomModifiers = new Set
 AtomModifiers.add(modifier) for modifier in ['ctrl', 'alt', 'shift', 'cmd']

-BrowserModifiers = new Set
-AtomModifiers.add(modifier) for modifier in ['Ctrl', 'Alt', 'Shift', 'Meta']
+KeyboardEventModifiers = new Set
+KeyboardEventModifiers.add(modifier) for modifier in ['Control', 'Alt', 'Shift', 'Meta']

 SpecificityCache = {}

@@ -19,7 +19,7 @@
   normalizedKeystrokes.join(' ')

 exports.keystrokeForKeyboardEvent = (event) ->
-  unless BrowserModifiers.has(event.keyIdentifier)
+  unless KeyboardEventModifiers.has(event.keyIdentifier)
     if event.keyIdentifier.indexOf('U+') is 0
       hexCharCode = event.keyIdentifier[2..]
       charCode = parseInt(hexCharCode, 16)
@@ -38,10 +38,6 @@
     key = key.toUpperCase() if /^[a-z]$/.test(key)
   else
     key = key.toLowerCase() if /^[A-Z]$/.test(key)
-
-  # If only a modifier was pressed, null out the key
-  key = null if key in ["meta", "shift", "control", "alt"]
-
   keystroke.push 'cmd' if event.metaKey
   keystroke.push(key) if key?
   keystroke.join('-')


refs #10 Also initialize likes on music library index page.

--- app/assets/javascripts/voluntary_music_metadata_enrichment/library/index.js.coffee
+++ app/assets/javascripts/voluntary_music_metadata_enrichment/library/index.js.coffee
@@ -13,7 +13,8 @@

   $(document.body).on "ajax:beforeSend", ".destroy_music_library_artist_link", ->
     $(this).find('.ajax_spinner').show()
-
+
   new window.VoluntaryMusicMetadataEnrichment.Library.YearsInReview.IndexView(true)
   new window.VoluntaryMusicMetadataEnrichment.Library.YearInReviewReleases.IndexView()
-  new window.VoluntaryMusicMetadataEnrichment.Library.YearInReviewTracks.NewView()+  new window.VoluntaryMusicMetadataEnrichment.Library.YearInReviewTracks.NewView()
+  new window.Likes.List()


clean warning

clean warning

--- views/akashic-senka-server-table.cjsx
+++ views/akashic-senka-server-table.cjsx
@@ -72,7 +72,7 @@

                   {
                     for tab, index in @props.tableTab
-                      {tab}
+                      {tab}
                   }


Fix bad spec: children is a function and it should actually assert the collection view not to be empty

--- spec/body-spec.coffee
+++ spec/body-spec.coffee
@@ -20,4 +20,4 @@
     expect(showView().$el).toHaveClass 'body'

   it 'has many body rows', ->
-    expect(showView().$el.children).not.toBeEmpty()
+    expect(showView().$el).not.toBeEmpty()


Factoring in margin & padding for mouse offset

--- src/js/Game.coffee
+++ src/js/Game.coffee
@@ -198,8 +198,15 @@
     @renderer.drawDraggedBlocks @state.stack

     [canvasX, canvasY] = @renderer.renderingCoordinatesForBlock bX, bY, i
-    @state.mouseOffsetY = @state.downY - canvasY - @renderer.settings.blockSizeHalf
-    @state.mouseOffsetX = @state.downX - canvasX
+    # Using bitwise-or 0 to convert Strings to ints
+    marginTop   = @mainCanvas.css(  'margin-top').replace('px','') | 0
+    marginLeft  = @mainCanvas.css( 'margin-left').replace('px','') | 0
+    paddingTop  = @mainCanvas.css( 'padding-top').replace('px','') | 0
+    paddingLeft = @mainCanvas.css('padding-left').replace('px','') | 0
+    @state.mouseOffsetY = @state.downY - canvasY + paddingTop  + marginTop - @renderer.settings.blockSizeHalf
+    @state.mouseOffsetX = @state.downX - canvasX + paddingLeft + marginLeft
+
+    console.log @state.mouseOffsetX

     @state.type = 'dragging'
     @renderer.drawMap yes # Redraw the map to update the hitmap


Await script execution for dotstorm tests

--- test/dotstorm_socketpipe.spec.coffee
+++ test/dotstorm_socketpipe.spec.coffee
@@ -28,8 +28,11 @@

   it "visits the front page", (done) ->
     @browser.visit(config.apps.dotstorm.url + "/")
-      .then ->
-        done()
+      .then =>
+        await =>
+          if @browser.querySelector("#id_join")?
+            done()
+            return true
       .fail (err) ->
         done(new Error(err))


group bundle: rough UI for bundle editor

--- client/landing/app/Applications/Groups.kdapplication/Views/groupsbundleview.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/groupsbundleview.coffee
@@ -3,7 +3,7 @@
   constructor:(options, data)->
     super

-    group = @getData()
+    {group} = @getData()

     @createButton = new KDButtonView
       style     : "clean-gray"
@@ -23,13 +23,69 @@

 class GroupsBundleEditView extends JView

+  computeUnitMap =
+    vms   : 1
+    cpu   : 1
+    ram   : 0.25
+    disk  : 0.5
+
+  computeUnitVisibility =
+    users : yes
+    vms   : yes
+    cpu   : no
+    ram   : no
+    disk  : no
+
   constructor: (options, data) ->
     super

-    group = @getData()
+    {group, bundle} = @getData()
+
+    {JLimit} = KD.remote.api
+
+    computeLimit = new JLimit { usage: 0, quota: 0, unit: 'VM', title: 'vms' }
+
+    bundle.fetchLimits (err, limits) =>
+      return error err      if err?
+      limits.splice 1, 0, computeLimit
+      @renderLimits limits  if limits?
+
+    @usersLabel = new KDLabelView
+      title       : 'Users'
+
+    @usersSlider = new KDInputView
+      label       : @usersLabel
+      type        : 'range'
+      change      : (event) =>
+        userData = @limitsData.users
+        userData.quota = @usersSlider.getValue()
+        userData.emit 'update'
+      attributes  :
+        min       : 0
+        max       : 50000
+
+    @computeLabel = new KDLabelView
+      title       : 'Computing resources'
+
+    @computeSlider = new KDInputView
+      label       : @computeLabel
+      type        : 'range'
+      change      : (event) =>
+        computeValue = @computeSlider.getValue()
+        ['cpu','ram','disk','vms'].forEach (resource) =>
+          resourceData = @limitsData[resource]
+          resourceData.quota = computeValue * computeUnitMap[resource]
+          resourceData.emit 'update'
+      attributes  :
+        min       : 0
+        max       : 50000
+
+    @limits = new KDView
+      tagName   : 'span'
+      cssClass  : 'group-bundle-limits clearfix'

     @destroyButton = new KDButtonView
-      style     : "clean-gray"
+      style     : "modal-clean-red"
       title     : "Destroy bundle"
       callback  : =>
         group.destroyBundle (err, bundle) =>
@@ -37,14 +93,83 @@

           @emit 'BundleDestroyed', bundle

-  pistachio:->
+    @saveButton = new KDButtonView
+      style     : "modal-clean-green"
+      title     : "Save bundle"
+      callback  : =>
+        overagePolicy = @overagePolicy.getValue()
+        group.updateBundle { overagePolicy }
+
+    @overageLabel = new KDLabelView
+      title     : 'Overage policy'
+
+    @overagePolicy = new KDSelectBox
+      label       : @overageLabel
+      name        : "overagePolicy"
+      defaultValue: bundle.overagePolicy ? "not allowed"
+      selectOptions : [
+        { value : "not allowed",    title : "Not allowed" }
+        { value : "by permission",  title : "Allowed only with admin approval" }
+        { value : "allowed",        title : "Allowed for all members" }
+      ]
+
+  renderLimits: (limits) ->
+    @limits.destroySubViews()
+    @limitsData = {}
+    for limit in limits
+      @limitsData[limit.title] = limit
+      cssClass = 'group-bundle-limit'
+      cssClass += ' hidden'  unless computeUnitVisibility[limit.title]
+      limitView = new GroupsBundleLimitView {cssClass}, limit
+      @addSubView limitView
+
+  pistachio: ->
     """

Bundle details

There will be some cool details here.

-
For now you can only destroy it.

-    {{> @destroyButton}}
+

+
{{> @usersLabel}} {{> @usersSlider}}

+
{{> @computeLabel}}  {{> @computeSlider}}

+
{{> @overageLabel}} {div#overage{> @overagePolicy}}

+

+    {{> @limits}}
+
{{> @saveButton}} {{> @destroyButton}}

     """

+# class GroupsBundleLimitWrapper extends KDObject
+#   constructor: (@limit, @usersLimit)->
+#     super()
+#     @calculate()
+#     @limit.on 'update', @bound 'calculate'
+#     @usersLimit.on 'update', @bound 'calculate'
+
+#   calculate:->
+#     {@unit, @title, @quota, @usage} = @limit
+#     @perUserQuota = @quota / @usersLimit.quota
+#     console.log this
+#     @emit 'update'
+
+class GroupsBundleLimitPerUserView extends JView
+
+  pistachio: ->
+    """
+    {h2{#(title)}}
+    {h3{#(usage)}}
+
/

+
{.per-user{#(perUserQuota)}} - {.total{#(quota)}}

+    {h4{#(unit)}}
+    """
+
+class GroupsBundleLimitView extends JView
+
+  pistachio: ->
+    """
+    {h2{#(title)}}
+    {h3{#(usage)}}
+
/

+    {h3{#(quota)}}
+    {h4{#(unit)}}
+    """

 class GroupsBundleView extends KDView

@@ -62,10 +187,10 @@
       return error err  if err?

       unless bundle?
-        @createBundleView = new GroupsBundleCreateView {}, group
+        @createBundleView = new GroupsBundleCreateView {}, { group }
         @createBundleView.once 'BundleCreated', @bound 'resetBundleView'
         @addSubView @createBundleView
       else
-        @editBundleView = new GroupsBundleEditView {}, group
+        @editBundleView = new GroupsBundleEditView {}, { group, bundle }
         @editBundleView.once 'BundleDestroyed', @bound 'resetBundleView'
         @addSubView @editBundleView


fix (Modal tests) add id to the preloaded web view

--- testApp/app/controllers/modal.coffee
+++ testApp/app/controllers/modal.coffee
@@ -124,8 +124,8 @@

     preloadedView = new steroids.views.WebView
       location: "/views/modal/preload.html"
+      id: "preloadedModal"
     preloadedView.preload()
-

   @testShowPreloadedKeepLoading: () ->


Notification: Remove notification tray lines from notification controller

--- client/Main/notificationcontroller.coffee
+++ client/Main/notificationcontroller.coffee
@@ -28,7 +28,6 @@
     @notificationChannel.on 'message', (notification)=>
       @emit "NotificationHasArrived", notification
       if notification.contents
-        @prepareNotification notification

         unless notification.context
           @emit notification.event, notification.contents
@@ -116,137 +115,3 @@
       @utils.wait 10000, =>
         Cookies.expire 'clientId'
         location.reload yes
-
-  fetchActor: (contents) ->
-    new Promise (resolve, reject) =>
-      {actorId} = contents
-      KD.remote.api.JAccount.one _id: actorId, (err, actor) =>
-        return reject err  if err
-        return reject {message: "actor not found"}  unless actor
-        resolve {actor, contents}
-
-
-  fetchTarget: (data)->
-    new Promise (resolve, reject) =>
-      helper = (err, target) ->
-        return reject err  if err
-        data.target = target
-        resolve data
-
-      {targetId, type} = data.contents
-      switch type
-        when "comment", "like"
-          KD.remote.api.SocialMessage.fetch id: targetId, helper
-        when "follow"
-          KD.remote.api.JAccount.one socialApiId: targetId, helper
-        when "join", "leave"
-          KD.remote.api.JGroup.one socialApiChannelId: targetId, helper
-        else
-          resolve data
-
-
-  prepareMessage: (data) ->
-    options = {}
-    options.actorAvatar = new AvatarView
-      size      :
-        width   : 35
-        height  : 35
-      , data.actor
-
-    @prepareTitle data, options
-
-
-  prepareTitle: (data, options) ->
-    new Promise (resolve, reject) ->
-      {actor, contents, target} = data
-      actorName = KD.utils.getFullnameFromAccount actor
-      setTitle = (title) ->
-        options.title = title
-        resolve options
-
-      switch contents.type
-        when "comment"
-          isMine = target.accountOldId is KD.whoami()?.getId()
-          subject = if target.type is "privateMessage" then "private message" else "status"
-          if isMine then setTitle "#{actorName} commented on your #{subject}"
-          else
-            KD.remote.api.JAccount.one _id: target.accountOldId, (err, origin)->
-              return reject err  if err
-              return reject {message: "message origin not found"}  unless origin
-
-              if origin.getId() is actor.getId()
-                ownerName = "their own"
-              else
-                ownerName = KD.utils.getFullnameFromAccount actor
-                ownerName = "#{ownerName}'s"
-              setTitle "#{actorName} commented on #{ownerName} #{subject}"
-        when "like"
-          setTitle "#{actorName} liked your status."
-        when "follow"
-          setTitle "#{actorName} started following you."
-        when "join"
-          setTitle "#{actorName} has joined #{target.title}."
-        when "leave"
-          setTitle "#{actorName} has left #{target.title}."
-        when "mention"
-          setTitle "#{actorName} mentioned you in a post."
-
-      # when "newMessage"
-      #   @emit "NewMessageArrived"
-      #   "#{actorName} sent you a #{subjectMap()[subject.constructorName]}."
-      # when "groupInvited"
-      #   "#{actorName} has invited you to #{subjectObj.title}."
-
-
-  prepareClick: (data, options) ->
-    {target, contents} = data
-    if target
-      options.click = ->
-        view = this
-        if contents.type in ["comment", "like"]
-          # TODO group slug must be prepended after groups are implemented
-          # groupSlug = if target.group is "koding" then "" else "/#{target.group}"
-          KD.getSingleton('router').handleRoute "/Activity/Post/#{target.message.slug}", state:target
-          view.destroy()
-        else if contents.type in ["join", "leave"]
-          KD.getSingleton('router').handleRoute "/#{target.slug}"
-          view.destroy()
-
-
-  # NOTIFICATION SAMPLES
-
-  # 1 - < actor fullname > commented on your < activity type >.
-  # 2 - < actor fullname > also commented on the < activity type > that you commented.
-  # 3 - < actor fullname > liked your < activity type >.
-  # 4 - < actor fullname > sent you a private message.
-  # 5 - < actor fullname > replied to your private message.
-  # 6 - < actor fullname > also replied to your private message.
-  # 7 - < actor fullname > has invited you to < group title >.
-  # 8 - < actor fullname > has joined < group title >.
-  # 9 - < actor fullname > has left < group title >.
-
-  prepareNotification: (notification)->
-    {contents} = notification
-    @fetchActor(contents).then (data) =>
-      @fetchTarget(data).then (data) =>
-        @prepareMessage(data).then (options) =>
-          @prepareClick data, options
-          @notify options
-        .catch (err) ->
-          warn err  if err
-
-
-  notify:(options  = {})->
-
-    options.title       or= 'notification arrived'
-
-    notification = new KDNotificationView
-      type     : 'tray'
-      cssClass : "mini realtime #{options.type}"
-      duration : 10000
-      title    : "#{options.title}"
-      content  : options.content  or null
-
-    if options.actorAvatar then notification.addSubView options.actorAvatar
-
-    notification.once 'click', options.click


workspace: delete related files

workspace: delete related files

--- client/Main/workspacesettingspopup.coffee
+++ client/Main/workspacesettingspopup.coffee
@@ -31,14 +31,15 @@
         KD.remote.api.JWorkspace.deleteById workspace.id, (err)=>

           return  if KD.showError err
+
+          KD.getSingleton('mainController').emit "WorkspaceWasDeleted", workspace.data.machineUId, workspace.data.rootPath

           navItem.destroy()
           @destroy()

           KD.singletons
             .router.handleRoute "/IDE/#{workspace.machineLabel}/my-workspace"
-
-
+
     @addSubView new KDCustomHTMLView
       cssClass : 'modal-arrow'
       position : top : 34


app/shortcuts: comments :dolphin:

app/shortcuts: comments :dolphin:

--- client/app/lib/shortcutscontroller.coffee
+++ client/app/lib/shortcutscontroller.coffee
@@ -20,11 +20,11 @@

   # Manages keyboard shortcuts.
   #
-  # Wraps over a _shortcuts_ instance by default (if not passed explicitly
+  # Wraps over a shortcuts instance by default (if not passed explicitly
   # within opts.shortcuts), and makes sure we are listening and dispatching
   # correct keyboard events depending on an application's config.
   #
-  # This also exposes convenience proxy methods to the underlying _keyconfig_
+  # This also exposes convenience proxy methods to the underlying keyconfig
   # instance, and persists the state of a keyconfig#Collection to the app storage.
   #
   constructor: (options={}, data) ->
@@ -60,8 +60,7 @@
   # Party starts here.
   #
   # This adds the necessary event listeners, and fetches changes.
-  # Make sure to call this after _appStorageController_ & _appManager_
-  # singletons are set.
+  # Make sure to call this after appStorageController & appManager singletons are set.
   #
   addEventListeners: ->

@@ -90,11 +89,11 @@
       @_flushBuffer()
       return

-    # take over buffer
+    # Take over buffer.
     buffer = _.clone @_buffer, yes
     @_flushBuffer()

-    # transform it, so we can extend the remote object
+    # Transform it, so we can extend the remote object.
     pack =
       _
         .reduce buffer, (sum, collection, key) ->
@@ -105,10 +104,10 @@
           , sum
         , {}

-    # so we extend the remote object
+    # So we extend the remote object.
     @_store._storage.update $set: pack, =>

-      # and make sure everybody is aware of the changes we've just made
+      # And make sure everybody is aware of the changes we've just made.
       _.each buffer, (objs, collectionName) =>
         collection = @get collectionName
         _.each objs, (value) =>
@@ -133,8 +132,7 @@

     @_store._storage.update $unset: pack, =>

-      # remove change listeners, so we won't try to persist following changes.
-      #
+      # Remove change listeners, so we won't try to persist following changes.
       # XXX: use component-bind, bound or whatever instead
       @shortcuts.removeAllListeners 'change'

@@ -160,10 +158,10 @@

   # Buffers up changed models.
   #
-  # This is necessary for not to exhaust given resources, since
-  # we are listening changes in _keyconfig#Model_ level.
-  #
-  # That is to say, each _keyconfig#Model_ change dispatches this.
+  # This is necessary for not to exhaust given resources, since we are listening
+  # for changes in keyconfig#Model level.
+  #
+  # That is, each keyconfig#Model change dispatches this.
   #
   _handleShortcutsChange: (collection, model) ->

@@ -176,8 +174,8 @@
     else
       obj.binding = null

-    # even if enabled is _true_ by default for this model, we always save it on
-    # appstorage. defaults may change, but user overrides should stay.
+    # Even if enabled is true by default for this model, we always save it on
+    # appstorage. Defaults may change, but user overrides should stay.
     obj.enabled = if model.options?.enabled is no then no else yes

     idx = _.findIndex queue, name: model.name
@@ -221,8 +219,8 @@
     return

-  # Invalidates previous app's keyboard events and adds new
-  # listeners for the current one.
+  # Invalidates previous app's keyboard events and adds new listeners
+  # for the current one.
   #
   _handleFrontAppChange: (app, prevApp) ->

@@ -240,7 +238,7 @@
         @shortcuts.on "key:#{key}", app.bound 'handleShortcut'

-  # Proxy to _shortcuts#get_.
+  # Proxy to shortcuts#get.
   #
   # See: http://github.com/koding/shortcuts#api
   #
@@ -249,16 +247,15 @@
     @shortcuts.get.apply @shortcuts, Array::slice.call arguments

-  # Updates a _keyconfig#Model_.
-  #
-  # This api is pretty-much same with _shortcuts#update_.
-  #
-  # One important difference is _silent_ argument is handled internally,
-  # and not exposed. That's because passing _silent_ to _shortcuts#update_
-  # as true makes sure _shortcuts_ doesn't add any keyboard event listeners,
-  # yet it still updates the underlying _keyconfig_ instance. This is how we
+  # Updates a keyconfig#Model.
+  #
+  # This api is pretty-much same with shortcuts#update.
+  #
+  # One important difference is silent argument is handled internally,
+  # and not exposed. That's because passing silent to shortcuts#update
+  # as true makes sure shortcuts doesn't add any keyboard event listeners,
+  # yet it still updates the underlying keyconfig instance. This is how we
   # deal with shortcuts that should be handled manually.
-  #
   #
   update: (collectionName, modelName, value) ->

@@ -273,18 +270,18 @@

     throw "#{modelName} not found"  unless model

-    # _lodash#pick_ returns a shallow copy; make sure we don't override the given value
+    # lodash#pick returns a shallow copy; make sure we don't override the given value.
     overrides = _.clone overrides, yes

-    # _isNull_ test is necessary here to make sure we don't update other
-    # platform bindings accidentally
+    # isNull test is necessary here to make sure we don't update other
+    # platform bindings accidentally.
     if _.isArray(overrides.binding) or _.isNull(overrides.binding)
       overrides.binding = klass._replacePlatformBinding model, overrides.binding
     else
       delete overrides.binding

-    # _options.enabled_ must be precisely set to _false_ in order to disable a shortcut;
-    # any other falsey setting will always be converted to _true_
+    # options.enabled must be precisely set to false in order to disable a shortcut;
+    # any other falsey setting will always be converted to true.
     if _.isObject overrides.options
       whitelistedOptions = _.pick overrides.options, 'enabled'
       unless _.isEmpty whitelistedOptions
@@ -293,7 +290,7 @@
     silent = klass._isCustomShortcut model
     res    = @shortcuts.update collectionName, modelName, overrides, silent

-    # we manually trigger change for custom shortcuts
+    # We manually dispatch change for custom shortcuts.
     if silent then @_handleShortcutsChange @shortcuts.get(collectionName), res

     return res
@@ -305,10 +302,10 @@
   # Note that:
   #
   # * Returning value only includes bindings for the current platform.
-  # * _extends_ field is used to merge a collection into the specified one.
+  # * extends field is used to merge a collection into the specified one.
   # That's because some shortcuts are logically in the same group and should
   # be displayed along; but in fact they are not and should be separated
-  # to avoid collisions.
+  # to avoid collisions. (XXX: extends stuff is obsolete)
   #
   toJSON: (predicate) ->

@@ -343,7 +340,7 @@
     new Collection @toJSON predicate

-  # Convenience method that returns a _keyconfig#Collection's_ json representation
+  # Convenience method that returns a keyconfig#Collection's json representation
   # for the current platform. Optionally takes a filter predicate.
   #
   getJSON: (name, predicate) ->
@@ -392,19 +389,19 @@

   # Returns the app storage key for the current platform.
   #
-  # This will either be *shortcuts-mac* or *shortcuts-win*.
+  # This will either be shortcuts-mac or shortcuts-win.
   #
   @getPlatformStorageKey: ->

     "#{STORAGE_NAME}-#{globals.keymapType}"

-  # Returns _binding_ getter method name of a _keyconfig#Model_ for the current platform.
+  # Returns binding getter method name of a keyconfig#Model for the current platform.
   #
   # This is memoized, so subsequent calls will return the cached result;
-  # it still must be defined at static level since _globals_ must be ready.
-  #
-  # This will either be *getMacKeys* or *getWinKeys*.
+  # it still must be defined at static level since globals must be ready.
+  #
+  # This will either be getMacKeys or getWinKeys.
   #
   # See: http://github.com/koding/keyconfig#api
   #
@@ -414,7 +411,7 @@
     return "get#{kmt.charAt(0).toUpperCase()}#{kmt.slice 1, 3}Keys"

-  # Given a _keyconfig#Model_, returns its binding for the current platform.
+  # Given a keyconfig#Model, returns its binding for the current platform.
   #
   @_getPlatformBinding: (model) ->

@@ -423,10 +420,10 @@
   getPlatformBinding: klass._getPlatformBinding

-  # Returns _binding_ array index of a _keyconfig#Model_ for the current platform.
+  # Returns binding array index of a keyconfig#Model for the current platform.
   #
   # This is memoized, so subsequent calls will return the cached result;
-  # it still must be defined at static level since _globals_ must be ready.
+  # it still must be defined at static level since globals must be ready.
   #
   # This will either be 1 for mac or 0 for windows/linux.
   #
@@ -450,7 +447,7 @@
     return binding

-  # Returns *true* if given _keyconfig#Model_ is a custom shortcut.
+  # Returns true if given keyconfig#Model is a custom shortcut.
   #
   @_isCustomShortcut: (model) ->


Documentation

Documentation

--- src/logging.coffee
+++ src/logging.coffee
@@ -1,8 +1,8 @@
 ###*
-Gives you some convenience methods for outputting data while developing.
-
 @name Logging
 @namespace
+
+Gives you some convenience methods for outputting data while developing.

   log "Testing123"


Fix an error on importing if there was a user by the same username on the server but different email

--- packages/rocketchat-importer-slack/server.coffee
+++ packages/rocketchat-importer-slack/server.coffee
@@ -112,6 +112,9 @@
 				do (user) =>
 					Meteor.runAsUser startedByUserId, () =>
 						existantUser = RocketChat.models.Users.findOneByEmailAddress user.profile.email
+						if not existantUser
+							existantUser = RocketChat.models.Users.findOneByUsername user.name
+
 						if existantUser
 							user.rocketId = existantUser._id
 							@userTags.push


use previous value

use previous value

--- src/obserer.coffee
+++ src/obserer.coffee
@@ -33,7 +33,8 @@
     type = adapter.get_attr(from, 'type')
     # FIXME maybe save all needed attributes in hash ????
     handler = (e) ->
-      c e.type
+      c e.type # TODO into log
+
       result = {text: null, attribute: null}
       if e.type == "input" || e.type == "childList" || e.type == "change" || e.type == "DOMNodeInserted"
         result['text'] = adapter.text(from)
@@ -43,22 +44,8 @@
         new_attr  = adapter.get_attr(from, attr_name)

         result['text'] = new_attr
-
-        #        # when old < new, then we add value, send diff
-        #        # when old = new, then swap value, send new
-        #        # when old > new, then we remove value, send new
-        #        new_value = null
-        #        old_attr_length = old_attr.length
-        #        new_attr_length = new_attr.length
-        #
-        #        if old_attr_length < new_attr_length
-        #
-        #        else if old_attr_length == new_attr_length
-        #
-        #        else # old > new
-
-
         result['attribute'] = attr_name
+        result['previous'] = old_attr

       if e.type == "DOMAttrModified" # for ie 9...
         attr_name = e.originalEvent.attrName
@@ -66,7 +53,7 @@
         new_attr  = adapter.get_attr(from, attr_name)
         result['text'] = new_attr
         result['attribute'] = attr_name
-
+        result['previous'] = old_attr

       clb(result)

@@ -92,9 +79,14 @@
           characterDataOldValue: true
           subtree: false # FIXME subtree: true

-        elems = adapter.get(@from_element) # fixme : all

-        observer.observe(elems, cnf)
+        if Sirius.Utils.is_string(from)
+          elements = adapter.get(from) # fixme : all
+          observer.observe(elements, cnf)
+        else
+          observer.observe(from, cnf)
+
+

       else # when null, need register event with routes
         adapter.bind(document, @from_element, 'DOMNodeInserted', handler)


sync: address an edge involving deleting a file and saving it, which sometimes results in an error

--- src/smc-util/syncstring.coffee
+++ src/smc-util/syncstring.coffee
@@ -1756,6 +1756,11 @@
             cb?('readonly')
             return

+        if @_deleted
+            # nothing to do -- no need to attempt to save if file is already deleted
+            cb?()
+            return
+
         @_save_to_disk()
         if not @_syncstring_table?
             cb("@_syncstring_table must be defined")
@@ -1763,6 +1768,10 @@
         if cb?
             #dbg("waiting for save.state to change from '#{@_syncstring_table.get_one().getIn(['save','state'])}' to 'done'")
             f = (cb) =>
+                if @_deleted
+                    # if deleted, then save doesn't need to finish and is done successfully.
+                    cb()
+                    return
                 if not @_syncstring_table?
                     cb(true)
                     return


Add dialogue for removing all upgrades

Add dialogue for removing all upgrades

--- src/smc-webapp/r_upgrades.cjsx
+++ src/smc-webapp/r_upgrades.cjsx
@@ -1,7 +1,7 @@
 {React, rclass, rtypes}  = require('./smc-react')
 {Loading, r_join, Space, Footer} = require('./r_misc')
 misc = require('smc-util/misc')
-{Button, Row, Col, Well, Panel, ProgressBar} = require('react-bootstrap')
+{Button, ButtonToolbar, Row, Col, Well, Panel, ProgressBar} = require('react-bootstrap')
 {HelpEmailLink, SiteName, PolicyPricingPageUrl} = require('./customize')

 {PROJECT_UPGRADES} = require('smc-util/schema')
@@ -15,6 +15,9 @@
         stripe_customer : rtypes.object

     displayName : "UpgradesPage"
+
+    getInitialState: ->
+        expand_reset_all_projects : false

     render_no_upgrades: ->
         {SubscriptionGrid, ExplainResources, ExplainPlan, FAQ} = require('./billing')
@@ -155,11 +158,35 @@
             i += 1
             @render_upgraded_project(project_id, upgrades, i%2==0)

+    confirm_reset: (e) ->
+        console.log "TODO: Actually remove all upgrades"
+        @setState(expand_reset_all_projects:false)
+
+    render_header: ->
+

+            <Row>
+                <Col sm=12 style={display:'flex'} >
+
Upgrades you have applied to projects

+                    <Button onClick={=>@setState(expand_reset_all_projects:true)} disabled={@state.expand_reset_all_projects}>
+                        Reset all project upgrades...
+                    </Button>
+                </Col>
+            </Row>
+            {<Row>
+                <Col sm=12>
+                    <ResetProjectsConfirmation
+                        on_confirm = {@confirm_reset}
+                        on_cancel  = {=>@setState(expand_reset_all_projects:false)}
+                    />
+                </Col>
+            </Row> if @state.expand_reset_all_projects}
+

+
     render_upgraded_projects: ->
         upgraded_projects = @props.redux.getStore('projects').get_projects_upgraded_by()
         if not misc.len(upgraded_projects)
             return
-        <Panel header={
Upgrades you have applied to projects
}>
+        <Panel header={@render_header()}>
             <Row key='header'>
                 <Col sm=4>
                     Project
@@ -182,4 +209,18 @@
                 {@render_upgrades()}
                 {@render_upgraded_projects()}

-
+

+
+ResetProjectsConfirmation = ({on_confirm, on_cancel}) ->
+    <Well style={marginBottom:'0px', marginTop:'10px', textAlign:'center'}>
+        Are you sure you want to remove all your upgrades from all projects?

+        You will have all your upgrades available to use.
+        <ButtonToolbar>
+            <Button bsStyle='warning' onClick={on_confirm}>
+                Yes, remove all upgrades.
+            </Button>
+            <Button onClick={on_cancel}>
+                Cancel
+            </Button>
+        </ButtonToolbar>
+    </Well>


Fix specs.

Fix specs.

--- src/spec/elasticio.spec.coffee
+++ src/spec/elasticio.spec.coffee
@@ -12,7 +12,7 @@
       sphereProjectKey: 'here'
     msg = ''
     elasticio.process msg, cfg, (error, message) ->
-      expect(error).toBe 'No data found in elastic.io msg.'
+      expect(error).toBe '[SphereStockImport] No data found in elastic.io msg.'
       done()

   describe 'XML file', ->
@@ -43,7 +43,7 @@
       spyOn(ElasticIo, 'returnSuccess').andCallThrough()
       elasticio.process msg, cfg, (error, message) ->
         expect(error).toBe null
-        if message is 'elastic.io messages sent.'
+        if message is '[SphereStockImport] elastic.io messages sent.'
           expect(ElasticIo.returnSuccess.callCount).toBe 3
           expectedMessage =
             body:
@@ -79,7 +79,7 @@
       spyOn(ElasticIo, 'returnSuccess').andCallThrough()
       elasticio.process msg, cfg, (error, message) ->
         expect(error).toBe null
-        if message is 'elastic.io messages sent.'
+        if message is '[SphereStockImport] elastic.io messages sent.'
           expect(error).toBe null
           expectedMessage =
             body:


OHM-117: Added tests

OHM-117: Added tests

--- test/unit/tasksTest.coffee
+++ test/unit/tasksTest.coffee
@@ -266,3 +266,41 @@
                 done()

           setTimeout validateTask, 1000
+
+    it 'should not process a paused task', (done) ->
+      Task.update { _id: task1._id }, { status: 'Paused' }, (err) ->
+        return done err if err
+
+        server = testUtils.createMockServer 200, "Mock response", 7786, ->
+          tasks.findAndProcessAQueuedTask()
+          validateTask = ->
+            Task.findOne _id: task1._id, (err, task) ->
+              # Task should be untouched
+              task.status.should.be.equal 'Paused'
+              task.remainingTransactions.should.be.equal 3
+              task.transactions[0].tstatus.should.be.equal 'Queued'
+              task.transactions[1].tstatus.should.be.equal 'Queued'
+              task.transactions[2].tstatus.should.be.equal 'Queued'
+              server.close ->
+                done()
+
+          setTimeout validateTask, 1000
+
+    it 'should not process a cancelled task', (done) ->
+      Task.update { _id: task1._id }, { status: 'Cancelled' }, (err) ->
+        return done err if err
+
+        server = testUtils.createMockServer 200, "Mock response", 7786, ->
+          tasks.findAndProcessAQueuedTask()
+          validateTask = ->
+            Task.findOne _id: task1._id, (err, task) ->
+              # Task should be untouched
+              task.status.should.be.equal 'Cancelled'
+              task.remainingTransactions.should.be.equal 3
+              task.transactions[0].tstatus.should.be.equal 'Queued'
+              task.transactions[1].tstatus.should.be.equal 'Queued'
+              task.transactions[2].tstatus.should.be.equal 'Queued'
+              server.close ->
+                done()
+
+          setTimeout validateTask, 1000


More relaxed check on date

More relaxed check on date

--- test/unit/util.spec.coffee
+++ test/unit/util.spec.coffee
@@ -5,7 +5,7 @@

   describe 'getDate', ->
     it 'should return current date by default', ->
-      expect(util.getDate().getTime()).toBeCloseTo(new Date().getTime(), 5)
+      expect(util.getDate().getTime()).toBeCloseTo(new Date().getTime(), 10)

   describe 'getISODateString', ->
     it 'should return date formatted as YYYYMMDDTHHnnssZ', ->