missing handleError helper on vm.coffee

missing handleError helper on vm.coffee

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -10,12 +10,13 @@

   JRecurlySubscription = require './recurly/subscription'
   JPermissionSet       = require './group/permissionset'
-
   @share()

   @trait __dirname, '../traits/protected'

   @bound = require 'koding-bound'
+
+  handleError = (err)-> console.error err  if err

   @set
     softDelete          : yes
@@ -394,8 +395,6 @@

   do ->

-    handleError = (err)-> console.error err  if err
-
     JGroup  = require './group'
     JUser   = require './user'


fix a typo

fix a typo

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -562,7 +562,7 @@
             webHome   : user.username
             groups    : wrapGroup group
           }
-        else unless group is 'koding'
+        else unless group.slug is 'koding'
           member.checkPermission group, 'sudoer', (err, hasPermission)->
             if err then handleError err
             else


Activity: old styles included temp

Activity: old styles included temp

--- client/includes.coffee
+++ client/includes.coffee
@@ -664,7 +664,7 @@
   # "stylus/app.editor.styl",
   # "stylus/app.finder.styl",
   # "stylus/app.aceeditor.styl",
-  # "stylus/app.activity.styl",
+  "stylus/app.activity.styl",
   # "stylus/app.contextmenu.styl",
   # "stylus/app.environments.styl",
   # "stylus/app.chat.styl",


:bug: Fix visibility changes not properly watched when a tab become active

--- lib/minimap-element.coffee
+++ lib/minimap-element.coffee
@@ -384,10 +384,11 @@

   # Internal: Polling callback used to detect visibility and size changes.
   pollDOM: ->
+    visibilityChanged = @checkForVisibilityChange()
     if @isVisible()
       @requestForcedUpdate() unless @wasVisible

-      @measureHeightAndWidth(false)
+      @measureHeightAndWidth(visibilityChanged, false)

   # Internal: A method that checks for visibility changes in the
   # {MinimapElement}. The method returns `true` when the visibility changed
@@ -412,9 +413,8 @@
   #
   # forceUpdate - A {Boolean} that forces the update even when no changes were
   #               detected.
-  measureHeightAndWidth: (forceUpdate=true) ->
+  measureHeightAndWidth: (visibilityChanged, forceUpdate=true) ->
     wasResized = @width isnt @clientWidth or @height isnt @clientHeight
-    visibilityChanged = @checkForVisibilityChange()

     @height = @clientHeight
     @width = @clientWidth


:bug: FIX leftover

:bug: FIX leftover

--- lib/operation-stack.coffee
+++ lib/operation-stack.coffee
@@ -20,10 +20,9 @@
   push: (op) ->
     return unless op?
     if @isEmpty() and settings.debug()
+      if settings.debugOutput() is 'console'
+        console.clear()
       debug "#=== Start at #{new Date().toISOString()}"
-      if settings.debugOutput() is 'console'
-        null
-        # console.clear()

     @withLock =>
       # Motions in visual mode perform their selections.
@@ -75,7 +74,6 @@
   process: ->
     return if @isEmpty()
     debug "-> @process(): start"
-    @inspect()

     unless @peekTop().isComplete()
       if @vimState.isNormalMode() and @peekTop().isOperator?()


:bug: fix

:bug: fix

--- lib/operation-stack.coffee
+++ lib/operation-stack.coffee
@@ -95,7 +95,7 @@
       @vimState.resetNormalMode()
     @finish()

-  ensureAllSelectionsAreEmpty: ->
+  ensureAllSelectionsAreEmpty: (operation) ->
     unless @editor.getLastSelection().isEmpty()
       if settings.get('throwErrorOnNonEmptySelectionInNormalMode')
         throw new Error("Selection is not empty in normal-mode: #{operation.toString()}")
@@ -111,7 +111,7 @@
     @record(operation) if operation?.isRecordable()
     @vimState.emitter.emit 'did-finish-operation'
     if @vimState.isMode('normal')
-      @ensureAllSelectionsAreEmpty()
+      @ensureAllSelectionsAreEmpty(operation)
       @ensureAllCursorsAreNotAtEndOfLine()

     @vimState.updateCursorsVisibility()


:lipstick: Inline runCommand args

:lipstick: Inline runCommand args

--- lib/package-manager.coffee
+++ lib/package-manager.coffee
@@ -19,8 +19,7 @@
     new BufferedNodeProcess({command, args, stdout, stderr, exit})

   loadAvailable: (callback) ->
-    args = ['available', '--json']
-    exit = (code, stdout, stderr) =>
+    @runCommand ['available', '--json'], (code, stdout, stderr) =>
       if code is 0
         try
           @packages = JSON.parse(stdout) ? []
@@ -34,8 +33,6 @@
         error.stdout = stdout
         error.stderr = stderr
         callback(error)
-
-    @runCommand(args, exit)

   getAvailable: ->
     if @packages?
@@ -80,8 +77,7 @@
     {name, theme} = pack
     atom.packages.deactivatePackage(name) if atom.packages.isPackageActive(name)

-    args = ['uninstall', name]
-    exit = (code, stdout, stderr) =>
+    @runCommand ['uninstall', name], (code, stdout, stderr) =>
       if code is 0
         atom.packages.unloadPackage(name) if atom.packages.isPackageLoaded(name)
         callback?()
@@ -98,5 +94,3 @@
         else
           @emit 'package-uninstall-failed', pack, error
         callback(error)
-
-    @runCommand(args, exit)


launch browser when webserver starts

launch browser when webserver starts

--- lib/peon-gui-server.coffee
+++ lib/peon-gui-server.coffee
@@ -25,6 +25,7 @@
   ###
   run: () ->
     ps = require('portscanner')
+    exec = require('child_process').exec
     that = @
     workerPort = @worker.getSocket()
     ps.findAPortNotInUse(startPort, endPort, 'localhost', (err, port) ->
@@ -35,6 +36,9 @@
       grunt.log.writeln "GUI running on localhost:#{port}"
       url = "http://localhost:#{port}/?socket=#{workerPort}"
       grunt.log.writeln "Manage this project on #{url}"
+      exec("open #{url}", (err, stdout, stderr)->
+
+      )
     )

 module.exports = PeonGUIServer


Wrapping up temporal queries with hard limit set by internal clock

--- code/models/DataModel.coffee
+++ code/models/DataModel.coffee
@@ -1,4 +1,5 @@
 mongoose = require 'mongoose'
+clock    = require './SensorsClock'

 dataSchema = new mongoose.Schema
   _id:       String
@@ -8,5 +9,9 @@
   address:   String

 dataSchema.set 'collection', 'envdata'
+EnvData = mongoose.model 'envdata', dataSchema

-module.exports = mongoose.model 'envdata', dataSchema
+module.exports =
+  find: (query, rest...) ->
+    newQuery = $and: [ query, timestamp: $lt: clock.getTime() ]
+    EnvData.find newQuery, rest...


Cleared form after changing password

Cleared form after changing password

--- client/settings.coffee
+++ client/settings.coffee
@@ -27,8 +27,13 @@

     # Update password
     Accounts.changePassword currentPass, newPass, (error) ->
-      Notify.meteorError error, true if error
-      Notify.success "Password changed sucessfully" unless error
+      if error
+        Notify.meteorError error, true
+      else
+        Notify.success "Password changed sucessfully"
+        $('#current-password').val('')
+        $('#new-password').val('')
+        $('#new-password-confirm').val('')

     return # make sure CoffeeScript does not return anything


chore(js-init): only load internal modules when authenticated

--- app/assets/javascripts/app.coffee
+++ app/assets/javascripts/app.coffee
@@ -6,15 +6,13 @@
   $('.btn.btn-loading').click ->
     $(@).button('loading')

-document.addEventListener 'turbolinks:load', ->
-  App.init()
-
   Selectize = new App.Selectize()
   Selectize.init()

   Moment = new App.Moment()
   Moment.init()

+App.initInternal = ->
   Accounting = new App.Accounting()
   Accounting.init()

@@ -32,6 +30,10 @@
   Timer.init()

   new App.Notifications()
+
+document.addEventListener 'turbolinks:load', ->
+  App.init()
+  App.initInternal() if AuthToken

 $(document).on 'click', '[data-geolocation]', (ev) ->
   ev.preventDefault()


simplify addTrailingTabstop()

simplify addTrailingTabstop()

--- lib/atomizr.coffee
+++ lib/atomizr.coffee
@@ -389,20 +389,7 @@
       # nothing to do here
       return input

-    re  = /\${?(\d+)/g
-    tabStops = []
-
-    while (m = re.exec(input)) != null
-      tabStops.push m[1]
-
-    # no tab-stops
-    unless tabStops.length
-      return "#{input}$1"
-
-    tabStops = tabStops.sort()
-    highest = parseInt(tabStops[tabStops.length - 1]) + 1
-
-    return "#{input}$#{highest}"
+    return "#{input}$0"

   removeTrailingTabstops: (input) ->
     if input.match(/\$\d+$/g) is null or atom.config.get('atomizr.removeTrailingTabstops') is false


Allow tagging specific logs

Allow tagging specific logs

--- components/Log.coffee
+++ components/Log.coffee
@@ -48,9 +48,12 @@
   entire stream is complete."

   constructor: ->
+    @tag = null
+
     @inPorts =
       in: new noflo.Port
       options: new noflo.Port
+      tag: new noflo.Port
     @outPorts =
       out: new noflo.Port

@@ -59,10 +62,16 @@
         for own key, value of opts
           options[key] = value

+    @inPorts.tag.on "data", (@tag) =>
+
     @inPorts.in.on "connect", =>
       count++
       @cache = []
       @groups = []
+
+      if @tag?
+        @cache[@tag] ?= []
+        @groups.push @tag

     @inPorts.in.on "begingroup", (group) =>
       here = @locate()
@@ -82,6 +91,8 @@
     @inPorts.in.on "disconnect", =>
       @outPorts.out.disconnect() if @outPorts.out.isAttached()

+      @groups.pop() if @tag?
+
       log.push @cache
       count--
       flush() if count is 0


search no longer show column and line header correctly aligned

--- lib/provider/search.coffee
+++ lib/provider/search.coffee
@@ -4,6 +4,7 @@

 ProviderBase = require './provider-base'
 settings = require '../settings'
+{padStringLeft} = require '../utils'

 runCommand = (options) ->
   new BufferedProcess(options).onWillThrowError ({error, handle}) ->
@@ -55,7 +56,17 @@
     else
       search = @search.bind(null, _.escapeRegExp(@options.word))
       Promise.all(@options.projects.map(search)).then (values) =>
-        @items = _.flatten(values)
+        items = _.flatten(values)
+        @injectMaxRow(items)
+        @items = items
+
+  injectMaxRow: (items) ->
+    # Inject textWidthForLastRow field to each item just for make row header aligned.
+    items = items.filter((item) -> not item.skip) # normal item only
+    maxRow = Math.max((items.map (item) -> item.point.row)...)
+    textWidthForLastRow = String(maxRow + 1).length
+    for item in items
+      item.textWidthForLastRow = textWidthForLastRow

   search: (pattern, project) ->
     items = []
@@ -102,7 +113,7 @@
         true

   getRowHeaderForItem: (item) ->
-    "    #{item.point.row + 1}:#{item.point.column + 1}:"
+    "    " + padStringLeft(String(item.point.row + 1), item.textWidthForLastRow) + ":"

   viewForItem: (item) ->
     if item.header?


Fixed dataset test

Fixed dataset test

--- test/integration/dataset.coffee
+++ test/integration/dataset.coffee
@@ -33,7 +33,7 @@
       browser.isVisible 'css selector', '#toolbar', (err, visible) =>
         visible.should.be.true
         wd40.getText '#toolbar', (err, text) =>
-          @toolbarText = text
+          @toolbarText = text.replace /\s+/g, ' '
           done()

     it '...the tool that made this dataset', ->
@@ -43,6 +43,7 @@
       @toolbarText.should.include 'View in a table'

     it '...the spreadsheet download tool', ->
+      console.log @toolbarText
       @toolbarText.should.include 'Download as spreadsheet'

     it '...(only once)', ->


added support for triggerfeedback

added support for triggerfeedback

--- src/leanengage.coffee
+++ src/leanengage.coffee
@@ -27,6 +27,9 @@
       trackEvent: (namespace, event, data) ->
         @$window.leanengage 'trackEvent', "#{namespace}/#{event}", data

+      triggerFeedback: (name) ->
+        @$window.leanengage 'triggerFeedback', name
+
     # Public API for configuration
     @setAppId = (id) ->
       appId = id


bubbles: flow lines properly updated when selecting purpose

--- app/charts/bubbles.coffee
+++ app/charts/bubbles.coffee
@@ -41,6 +41,7 @@

   state = null
   stopAnimation = null
+  selectedNode = null

   mapProj = winkelTripel()
@@ -442,7 +443,6 @@
     flows = svg.selectAll("g.flows")

-    selectedNode = null

     bubble = svg.selectAll("g.bubble")
@@ -511,21 +511,33 @@
     update()

+
+    if selectedNode?
+      if nodes.nodeById(d3.select(selectedNode).datum()[conf.nodeIdAttr])?
+        showFlowsOf(selectedNode)
+
+      else
+        # selected node does not have any flows
+        flows.selectAll("line").remove()
+
+
     clearNodeSelection = ->
-      if selectedNode != null
+      if selectedNode?
         d3.select(selectedNode).selectAll("circle").classed("selected", false)
         flows.selectAll("line").remove()
         selectedNode = null

-    $(document).keyup (e) -> if e.keyCode == 27 then clearNodeSelection()
-
-    $('g.bubble').tipsy
-      gravity: 'w'
-      html: true
-      opacity: 0.9
-      trigger: "manual"
-      title: ->
-        bubbleTooltip(d3.select(this).data()[0])
+
+    unless isUpdate
+
+      $(document).keyup (e) -> if e.keyCode == 27 then clearNodeSelection()
+
+      $('g.bubble').tipsy
+        gravity: 'w'
+        html: true
+        opacity: 0.9
+        trigger: "manual"
+        title: -> bubbleTooltip(d3.select(this).data()[0])

     ###
@@ -603,7 +615,8 @@

   showFlowsOf = (bbl) ->
     flows = svg.selectAll("g.flows")
-    d = d3.select(bbl).datum() #data()?[0]
+    d = d3.select(bbl).datum()
+
     ###
     ffwscale = d3.scale.linear()
       .range([0, rscale.range()[1]/2])
@@ -611,11 +624,13 @@
     ###

     if (d.outlinks?)
-      flows.selectAll("line.out")
-          .data(d.outlinks) #.filter (d) -> flowLineValue(d) > 0)
-        .enter().append("svg:line")
+      outflow = flows.selectAll("line.out")
+          .data(d.outlinks, (d) -> d.target[conf.nodeIdAttr]) #.filter (d) -> flowLineValue(d) > 0)
+
+      outflow.exit().remove()
+
+      outflow.enter().append("svg:line")
           .attr("class", "out")
-          #.attr("stroke", outboundColor)
           .attr("opacity", 0)
           .transition()
             .duration(300)
@@ -623,12 +638,13 @@

     if (d.inlinks?)
-      flows.selectAll("line.in")
-          .data(d.inlinks) #.filter (d) -> flowLineValue(d) > 0)
-        .enter().append("svg:line")
+      inflow = flows.selectAll("line.in")
+          .data(d.inlinks, (d) -> d.source[conf.nodeIdAttr]) #.filter (d) -> flowLineValue(d) > 0)
+
+      inflow.exit().remove()
+
+      inflow.enter().append("svg:line")
           .attr("class", "in")
-          #.attr("stroke", inboundColor)
-          #.attr("opacity",0.5)
           .attr("opacity", 0)
           .transition()
             .duration(300)
@@ -660,13 +676,15 @@
         $(this).tipsy("hide")
         tseriesPanel.remove("flow" + i)

-
-    $('line').tipsy
+    $('g.flows line').tipsy
       gravity: 'w'
       opacity: 0.9
       html: true
       trigger: "manual"
-      title: -> flowTooltip(d3.select(this).data()[0])
+      title: -> flowTooltip(d3.select(this).datum())
+
+
+


ComputeController: methods commented, grouped and reordered based on their tasks

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -19,15 +19,12 @@
 module.exports = class ComputeController extends KDController

   @providers = globals.config.providers
-
-  @timeout = globals.config.COMPUTECONTROLLER_TIMEOUT
-
-  @Error = {
-    'TimeoutError', 'KiteError',
-    Pending: '107', NotVerified: '500'
+  @timeout   = globals.config.COMPUTECONTROLLER_TIMEOUT
+  @Error     = {
+    'TimeoutError', 'KiteError', Pending: '107', NotVerified: '500'
   }

-  constructor:->
+  constructor: ->

     super

@@ -59,6 +56,10 @@
         @emit 'ready'

         @info machine for machine in @machines
+
+
+  # ComputeController internal helpers
+  #

   getKloud: ->

@@ -69,116 +70,6 @@
       username     : globals.config.kites.kontrol.username

-  fetchStacks: do (queue=[])->
-
-    (callback = kd.noop)-> kd.singletons.mainController.ready =>
-
-      if @stacks.length > 0
-        callback null, @stacks
-        kd.info "Stacks returned from cache."
-        return
-
-      return  if (queue.push callback) > 1
-
-      remote.api.JComputeStack.some {}, (err, stacks = [])=>
-
-        if err?
-          cb err  for cb in queue
-          queue = []
-          return
-
-        remote.api.JMachine.some {}, (err, _machines = [])=>
-
-          if err?
-            cb err  for cb in queue
-            queue = []
-            return
-
-          machines = []
-          for machine in _machines
-            machines.push new Machine { machine }
-
-          @stacks   = stacks
-          @machines = machines
-
-          @stateChecker?.machines = machines
-          @stateChecker?.start()
-
-          globals.userMachines = machines
-          @emit "MachineDataUpdated"
-
-          cb null, stacks  for cb in queue
-          queue = []
-
-
-
-  fetchMachines: do (queue=[])->
-
-    (callback = kd.noop)-> kd.singletons.mainController.ready =>
-
-      if @machines.length > 0
-        callback null, @machines
-        kd.info "Machines returned from cache."
-        return
-
-      return  if (queue.push callback) > 1
-
-      @fetchStacks (err)=>
-
-        if err?
-          cb err  for cb in queue
-          queue = []
-          return
-
-        cb null, @machines  for cb in queue
-        queue = []
-
-
-  fetchMachine: (idOrUid, callback = kd.noop)->
-
-    remote.api.JMachine.one idOrUid, (err, machine)->
-      if showError err then callback err
-      else if machine? then callback null, new Machine { machine }
-
-
-  queryMachines: (query = {}, callback = kd.noop)->
-
-    remote.api.JMachine.some query, (err, machines)=>
-      if showError err then callback err
-      else callback null, (new Machine { machine } for machine in machines)
-
-
-
-  fetchAvailable: (options, callback)->
-    remote.api.ComputeProvider.fetchAvailable options, callback
-
-  fetchUsage: (options, callback)->
-    remote.api.ComputeProvider.fetchUsage options, callback
-
-  setAlwaysOn: (machine, state, callback = kd.noop)->
-
-    options =
-      machineId : machine._id
-      provider  : machine.provider
-      alwaysOn  : state
-
-    remote.api.ComputeProvider.update options, (err)=>
-      @triggerReviveFor machine._id  unless err?
-      callback err
-
-
-  create: (options, callback)->
-    remote.api.ComputeProvider.create options, (err, machine)=>
-      @reset yes  unless err?
-      callback err, machine
-
-  createDefaultStack: ->
-    return  unless isLoggedIn()
-    remote.api.ComputeProvider.createGroupStack (err, res)=>
-      return  if showError err
-      @reset yes
-
-
   reset: (render = no)->

     @stacks   = []
@@ -189,6 +80,7 @@
     if render then @fetchMachines =>
       @info machine for machine in @machines
       @emit "RenderMachines", @machines
+

   _clearTrialCounts: (machine)->
     @_trials[machine.uid] = {}
@@ -246,250 +138,121 @@
       return err

-  destroy: (machine, force)->
-
-    destroy = (machine)=>
-
-      @eventListener.triggerState machine,
-        status      : Machine.State.Terminating
-        percentage  : 0
-
-      machine.getBaseKite( createIfNotExists = no ).disconnect()
-
-      call = @getKloud().destroy { machineId: machine._id }
-
-      .then (res)=>
-
-        @_force = no
-
-        kd.log "destroy res:", res
-        @emit "MachineBeingDestroyed", machine
-        @_clearTrialCounts machine
-        @eventListener.addListener 'destroy', machine._id
-
-      .timeout globals.COMPUTECONTROLLER_TIMEOUT
-
-      .catch (err)=>
-
-        (@errorHandler call, 'destroy', machine) err
-
-    return destroy machine  if force or @_force
-
-    ComputeController_UI.askFor 'destroy', {machine, force}, =>
-      destroy machine
-
-
-  reinit: (machine)->
-
-    ComputeController_UI.askFor 'reinit', {machine, force: @_force}, =>
-
-      @eventListener.triggerState machine,
-        status      : Machine.State.Terminating
-        percentage  : 0
-
-      machine.getBaseKite( createIfNotExists = no ).disconnect()
-
-      call = @getKloud().reinit { machineId: machine._id }
-
-      .then (res)=>
-
-        @_force = no
-
-        kd.log "reinit res:", res
-        @emit "MachineBeingDestroyed", machine
-        @_clearTrialCounts machine
-        @eventListener.addListener 'reinit', machine._id
-
-      .timeout globals.COMPUTECONTROLLER_TIMEOUT
-
-      .catch (err)=>
-
-        (@errorHandler call, 'reinit', machine) err
-
-
-  resize: (machine, resizeTo = 10)->
-
-    ComputeController_UI.askFor 'resize', {
-      machine, force: @_force, resizeTo
-    }, =>
-
-      options =
-        machineId : machine._id
-        provider  : machine.provider
-        resize    : resizeTo
-
-      remote.api.ComputeProvider.update options, (err)=>
-
-        return  if showError err
-
-        @eventListener.triggerState machine,
-          status      : Machine.State.Pending
-          percentage  : 0
-
-        machine.getBaseKite( createIfNotExists = no ).disconnect()
-
-        call = @getKloud().resize { machineId: machine._id }
-
-        .then (res)=>
-
-          @_force = no
-
-          kd.log "resize res:", res
-          @_clearTrialCounts machine
-          @eventListener.addListener 'resize', machine._id
-
-        .timeout globals.COMPUTECONTROLLER_TIMEOUT
-
-        .catch (err)=>
-
-          (@errorHandler call, 'resize', machine) err
-
-
-
-  build: (machine)->
-
-    @eventListener.triggerState machine,
-      status      : Machine.State.Building
-      percentage  : 0
-
-    machine.getBaseKite( createIfNotExists = no ).disconnect()
-
-    call = @getKloud().build { machineId: machine._id }
-
-    .then (res)=>
-
-      kd.log "build res:", res
-      @_clearTrialCounts machine
-      @eventListener.addListener 'build', machine._id
-
-    .timeout globals.COMPUTECONTROLLER_TIMEOUT
-
-    .catch (err)=>
-
-      (@errorHandler call, 'build', machine) err
-
-
-
-  start: (machine)->
-
-    @eventListener.triggerState machine,
-      status      : Machine.State.Starting
-      percentage  : 0
-
-    machine.getBaseKite( createIfNotExists = no ).isDisconnected = no
-
-    call = @getKloud().start { machineId: machine._id }
-
-    .then (res)=>
-
-      kd.log "start res:", res
-      @_clearTrialCounts machine
-      @eventListener.addListener 'start', machine._id
-
-    .timeout globals.COMPUTECONTROLLER_TIMEOUT
-
-    .catch (err)=>
-
-      (@errorHandler call, 'start', machine) err
-
-
-
-  stop: (machine)->
-
-    @eventListener.triggerState machine,
-      status      : Machine.State.Stopping
-      percentage  : 0
-      silent      : yes
-
-    machine.getBaseKite( createIfNotExists = no ).disconnect()
-
-    call = @getKloud().stop { machineId: machine._id }
-
-    .then (res)=>
-
-      kd.log "stop res:", res
-      @_clearTrialCounts machine
-      @eventListener.addListener 'stop', machine._id
-
-    .timeout globals.COMPUTECONTROLLER_TIMEOUT
-
-    .catch (err)=>
-
-      (@errorHandler call, 'stop', machine) err
-
-
-
-
-
-  info: (machine)->
-
-    if @followUpcomingEvents machine, yes
-      return Promise.resolve()
-
-    @eventListener.triggerState machine,
-      status      : machine.status.state
-      percentage  : 0
-
-    machineId = machine._id
-    currentState = machine.status.state
-
-    call = @getKloud().info { machineId, currentState }
-
-    .then (response)=>
-
-      kd.log "info response:", response
-      @_clearTrialCounts machine
-      @eventListener.triggerState machine,
-        status      : response.State
-        percentage  : 100
-
-      response
-
-    .timeout globals.COMPUTECONTROLLER_TIMEOUT
-
-    .catch (err)=>
-
-      (@errorHandler call, 'info', machine) err
-
-  # Domain management
-  #
-
-  fetchDomains: do (queue=[])->
+  # Fetchers most of these methods has internal
+  # caches with in ComputeController
+
+  fetchStacks: do (queue=[])->

     (callback = kd.noop)-> kd.singletons.mainController.ready =>

-      @domains ?= []
-
-      if @domains.length > 0
-        callback null, @domains
-        kd.info "Domains returned from cache."
+      if @stacks.length > 0
+        callback null, @stacks
+        kd.info "Stacks returned from cache."
         return

       return  if (queue.push callback) > 1

-      topDomain = "#{nick()}.#{globals.config.userSitesDomain}"
-
-      remote.api.JDomainAlias.some {}, (err, domains)=>
+      remote.api.JComputeStack.some {}, (err, stacks = [])=>

         if err?
           cb err  for cb in queue
           queue = []
           return

-        # Move topDomain to index 0
-        _domains = []
-        for jdomain in domains
-          if jdomain.domain is topDomain
-          then _domains.unshift jdomain
-          else _domains.push    jdomain
-
-        @domains = _domains
-        cb null, @domains  for cb in queue
+        remote.api.JMachine.some {}, (err, _machines = [])=>
+
+          if err?
+            cb err  for cb in queue
+            queue = []
+            return
+
+          machines = []
+          for machine in _machines
+            machines.push new Machine { machine }
+
+          @stacks   = stacks
+          @machines = machines
+
+          @stateChecker?.machines = machines
+          @stateChecker?.start()
+
+          globals.userMachines = machines
+          @emit "MachineDataUpdated"
+
+          cb null, stacks  for cb in queue
+          queue = []
+
+
+
+  fetchMachines: do (queue=[])->
+
+    (callback = kd.noop)-> kd.singletons.mainController.ready =>
+
+      if @machines.length > 0
+        callback null, @machines
+        kd.info "Machines returned from cache."
+        return
+
+      return  if (queue.push callback) > 1
+
+      @fetchStacks (err)=>
+
+        if err?
+          cb err  for cb in queue
+          queue = []
+          return
+
+        cb null, @machines  for cb in queue
         queue = []

-  # Utils beyond this point
-  #
+  fetchMachine: (idOrUid, callback = kd.noop)->
+
+    remote.api.JMachine.one idOrUid, (err, machine)->
+      if showError err then callback err
+      else if machine? then callback null, new Machine { machine }
+
+
+  queryMachines: (query = {}, callback = kd.noop)->
+
+    remote.api.JMachine.some query, (err, machines)=>
+      if showError err then callback err
+      else callback null, (new Machine { machine } for machine in machines)
+
+
+  findMachineFromMachineId: (machineId)->
+
+    for machine in @machines
+      return machine  if machine._id is machineId
+
+
+  findMachineFromQueryString: (queryString)->
+
+    for machine in @machines
+      return machine  if machine.queryString is queryString
+
+
+  fetchAvailable: (options, callback)->
+    remote.api.ComputeProvider.fetchAvailable options, callback
+
+
+  fetchUsage: (options, callback)->
+    remote.api.ComputeProvider.fetchUsage options, callback
+
+
+  fetchUserPlan: (callback = kd.noop)->
+
+    if @lastKnownUserPlan?
+      return callback @lastKnownUserPlan
+
+    kd.singletons.paymentController.subscriptions (err, subscription)=>
+
+      kd.warn "Failed to fetch subscription:", err  if err?
+
+      if err? or not subscription?
+      then callback 'free'
+      else callback @lastKnownUserPlan = subscription.planTitle
+

   fetchPlans: (callback = kd.noop)->

@@ -503,19 +266,313 @@
           else @plans = plans
           callback plans

-  fetchUserPlan: (callback = kd.noop)->
-
-    if @lastKnownUserPlan?
-      return callback @lastKnownUserPlan
-
-    kd.singletons.paymentController.subscriptions (err, subscription)=>
-
-      kd.warn "Failed to fetch subscription:", err  if err?
-
-      if err? or not subscription?
-      then callback 'free'
-      else callback @lastKnownUserPlan = subscription.planTitle
-
+
+  fetchRewards: (options, callback)->
+
+    {unit} = options
+
+    options =
+      unit  : 'MB'
+      type  : 'disk'
+
+    remote.api.JReward.fetchEarnedAmount options, (err, amount)->
+
+      if err
+        amount = 0
+        kd.warn err
+
+      amount = Math.floor amount / 1000  if unit is 'GB'
+
+      callback null, amount
+
+
+  fetchPlanCombo: (provider, callback)->
+
+    [callback, provider] = [provider, callback]  unless callback?
+    provider ?= "koding"
+
+    @fetchUserPlan (plan)=> @fetchPlans (plans)=>
+      @fetchUsage { provider }, (err, usage)=>
+        @fetchRewards { unit: 'GB' }, (err, reward)->
+          callback err, { plan, plans, usage, reward }
+
+
+  # create helpers on top of remote.ComputeProvider
+  #
+
+  create: (options, callback)->
+    remote.api.ComputeProvider.create options, (err, machine)=>
+      @reset yes  unless err?
+      callback err, machine
+
+
+  createDefaultStack: ->
+    return  unless isLoggedIn()
+    remote.api.ComputeProvider.createGroupStack (err, res)=>
+      return  if showError err
+      @reset yes
+
+
+  # remote.ComputeProvider and Kloud kite public methods
+  #
+
+  info: (machine)->
+
+    if @eventListener.followUpcomingEvents machine, yes
+      return Promise.resolve()
+
+    @eventListener.triggerState machine,
+      status      : machine.status.state
+      percentage  : 0
+
+    machineId = machine._id
+    currentState = machine.status.state
+
+    call = @getKloud().info { machineId, currentState }
+
+    .then (response)=>
+
+      kd.log "info response:", response
+      @_clearTrialCounts machine
+      @eventListener.triggerState machine,
+        status      : response.State
+        percentage  : 100
+
+      response
+
+    .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+    .catch (err)=>
+
+      (@errorHandler call, 'info', machine) err
+
+
+  destroy: (machine, force)->
+
+    destroy = (machine)=>
+
+      @eventListener.triggerState machine,
+        status      : Machine.State.Terminating
+        percentage  : 0
+
+      machine.getBaseKite( createIfNotExists = no ).disconnect()
+
+      call = @getKloud().destroy { machineId: machine._id }
+
+      .then (res)=>
+
+        @_force = no
+
+        kd.log "destroy res:", res
+        @emit "MachineBeingDestroyed", machine
+        @_clearTrialCounts machine
+        @eventListener.addListener 'destroy', machine._id
+
+      .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+      .catch (err)=>
+
+        (@errorHandler call, 'destroy', machine) err
+
+    return destroy machine  if force or @_force
+
+    ComputeController_UI.askFor 'destroy', {machine, force}, =>
+      destroy machine
+
+
+  reinit: (machine)->
+
+    ComputeController_UI.askFor 'reinit', {machine, force: @_force}, =>
+
+      @eventListener.triggerState machine,
+        status      : Machine.State.Terminating
+        percentage  : 0
+
+      machine.getBaseKite( createIfNotExists = no ).disconnect()
+
+      call = @getKloud().reinit { machineId: machine._id }
+
+      .then (res)=>
+
+        @_force = no
+
+        kd.log "reinit res:", res
+        @emit "MachineBeingDestroyed", machine
+        @_clearTrialCounts machine
+        @eventListener.addListener 'reinit', machine._id
+
+      .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+      .catch (err)=>
+
+        (@errorHandler call, 'reinit', machine) err
+
+
+  resize: (machine, resizeTo = 10)->
+
+    ComputeController_UI.askFor 'resize', {
+      machine, force: @_force, resizeTo
+    }, =>
+
+      options =
+        machineId : machine._id
+        provider  : machine.provider
+        resize    : resizeTo
+
+      remote.api.ComputeProvider.update options, (err)=>
+
+        return  if showError err
+
+        @eventListener.triggerState machine,
+          status      : Machine.State.Pending
+          percentage  : 0
+
+        machine.getBaseKite( createIfNotExists = no ).disconnect()
+
+        call = @getKloud().resize { machineId: machine._id }
+
+        .then (res)=>
+
+          @_force = no
+
+          kd.log "resize res:", res
+          @_clearTrialCounts machine
+          @eventListener.addListener 'resize', machine._id
+
+        .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+        .catch (err)=>
+
+          (@errorHandler call, 'resize', machine) err
+
+
+  build: (machine)->
+
+    @eventListener.triggerState machine,
+      status      : Machine.State.Building
+      percentage  : 0
+
+    machine.getBaseKite( createIfNotExists = no ).disconnect()
+
+    call = @getKloud().build { machineId: machine._id }
+
+    .then (res)=>
+
+      kd.log "build res:", res
+      @_clearTrialCounts machine
+      @eventListener.addListener 'build', machine._id
+
+    .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+    .catch (err)=>
+
+      (@errorHandler call, 'build', machine) err
+
+
+  start: (machine)->
+
+    @eventListener.triggerState machine,
+      status      : Machine.State.Starting
+      percentage  : 0
+
+    machine.getBaseKite( createIfNotExists = no ).isDisconnected = no
+
+    call = @getKloud().start { machineId: machine._id }
+
+    .then (res)=>
+
+      kd.log "start res:", res
+      @_clearTrialCounts machine
+      @eventListener.addListener 'start', machine._id
+
+    .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+    .catch (err)=>
+
+      (@errorHandler call, 'start', machine) err
+
+
+  stop: (machine)->
+
+    @eventListener.triggerState machine,
+      status      : Machine.State.Stopping
+      percentage  : 0
+      silent      : yes
+
+    machine.getBaseKite( createIfNotExists = no ).disconnect()
+
+    call = @getKloud().stop { machineId: machine._id }
+
+    .then (res)=>
+
+      kd.log "stop res:", res
+      @_clearTrialCounts machine
+      @eventListener.addListener 'stop', machine._id
+
+    .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+    .catch (err)=>
+
+      (@errorHandler call, 'stop', machine) err
+
+
+  setAlwaysOn: (machine, state, callback = kd.noop)->
+
+    options =
+      machineId : machine._id
+      provider  : machine.provider
+      alwaysOn  : state
+
+    remote.api.ComputeProvider.update options, (err)=>
+      @triggerReviveFor machine._id  unless err?
+      callback err
+
+
+  # Domain management
+  #
+
+  fetchDomains: do (queue=[])->
+
+    (callback = kd.noop)-> kd.singletons.mainController.ready =>
+
+      @domains ?= []
+
+      if @domains.length > 0
+        callback null, @domains
+        kd.info "Domains returned from cache."
+        return
+
+      return  if (queue.push callback) > 1
+
+      topDomain = "#{nick()}.#{globals.config.userSitesDomain}"
+
+      remote.api.JDomainAlias.some {}, (err, domains)=>
+
+        if err?
+          cb err  for cb in queue
+          queue = []
+          return
+
+        # Move topDomain to index 0
+        _domains = []
+        for jdomain in domains
+          if jdomain.domain is topDomain
+          then _domains.unshift jdomain
+          else _domains.push    jdomain
+
+        @domains = _domains
+        cb null, @domains  for cb in queue
+        queue = []
+
+
+  setDomain: (machine, newDomain, callback = kd.noop) ->
+
+    @getKloud().setDomain { machineId: machine._id, newDomain }
+    .nodeify callback
+
+
+  # Utils beyond this point
+  #

   triggerReviveFor:(machineId)->

@@ -528,51 +585,6 @@
         kd.info "Revive triggered for #{machineId}", machine

-  setDomain: (machine, newDomain, callback = kd.noop) ->
-
-    @getKloud().setDomain { machineId: machine._id, newDomain }
-    .nodeify callback
-
-
-  fetchPlanCombo: (provider, callback)->
-
-    [callback, provider] = [provider, callback]  unless callback?
-    provider ?= "koding"
-
-    @fetchUserPlan (plan)=> @fetchPlans (plans)=>
-      @fetchUsage { provider }, (err, usage)=>
-        @fetchRewards { unit: 'GB' }, (err, reward)->
-          callback err, { plan, plans, usage, reward }
-
-  fetchRewards: (options, callback)->
-
-    {unit} = options
-
-    options =
-      unit  : 'MB'
-      type  : 'disk'
-
-    remote.api.JReward.fetchEarnedAmount options, (err, amount)->
-
-      if err
-        amount = 0
-        kd.warn err
-
-      amount = Math.floor amount / 1000  if unit is 'GB'
-
-      callback null, amount
-
-
-  findMachineFromMachineId: (machineId)->
-
-    for machine in @machines
-      return machine  if machine._id is machineId
-
-  findMachineFromQueryString: (queryString)->
-
-    for machine in @machines
-      return machine  if machine.queryString is queryString
-
   invalidateCache: (machineId)->

     machine = @findMachineFromMachineId machineId


ComputeController: showBuildLogs: an helper to start watching cloud-init-output logs in IDE

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -8,6 +8,7 @@

 nick                 = require 'app/util/nick'
 isKoding             = require 'app/util/isKoding'
+FSHelper             = require 'app/util/fs/fshelper'
 showError            = require 'app/util/showError'
 isLoggedIn           = require 'app/util/isLoggedIn'

@@ -887,3 +888,16 @@

     kd.singletons.appManager.tell 'IDE', 'stopCollaborationSession'

+
+  showBuildLogs: (machine) ->
+
+    # Path of cloud-init-output log
+    path = '/var/log/cloud-init-output.log'
+    file = FSHelper.createFileInstance { path, machine }
+
+    kd.singletons.appManager.tell 'IDE', 'tailFile', {
+      file
+      description:
+        "Your stack build completed successfully, in the following
+         logs you can find details about your custom stack script."
+    }


Update webtermmessagepane.coffee

Update webtermmessagepane.coffee

--- client/app/lib/terminal/webtermmessagepane.coffee
+++ client/app/lib/terminal/webtermmessagepane.coffee
@@ -67,7 +67,7 @@
     else if err.name is "TimeoutError"

       @setMessage \
-        "Failed to connect your terminal,
+        "Failed to connect to terminal,
         click here to try again.", 'RequestReconnect'

       sendDataDogEvent "TerminalConnectionFailed"


Fix code typo

Fix code typo

--- client/app/views/popover_screens/duplicate.coffee
+++ client/app/views/popover_screens/duplicate.coffee
@@ -9,7 +9,7 @@
     templateContent: require 'views/templates/popover_screens/duplicate'

     events:
-        'click .popover-back': (event) -> @onCancel(event)
+        'click .popover-back': (event) -> @onBack(event)
         'click .answer-no': (event) -> @onNo(event)
         'click .answer-yes': (event) -> @onYes(event)


work on deleteable comments.

work on deleteable comments.

--- client/landing/app/CommonViews/CommentView.coffee
+++ client/landing/app/CommonViews/CommentView.coffee
@@ -302,6 +302,25 @@
     @author = new ProfileLinkView {
       origin
     }
+    if data.originId is KD.whoami().getId()
+      @settingsButton = new KDButtonViewWithMenu
+        style       : 'transparent activity-settings-context'
+        cssClass    : 'activity-settings-menu'
+        title       : ''
+        icon        : yes
+        delegate    : @
+        iconClass   : "cog"
+        menu        : [
+          type      : "contextmenu"
+          items     : [
+            # { title : 'Edit',   id : 1,  parentId : null, callback : => new KDNotificationView type : "mini", title : "
Currently disabled.
" }
+            { title : 'Edit',   id : 1,  parentId : null, callback : => @getSingleton('mainController').emit 'ActivityItemEditLinkClicked', data }
+            { title : 'Delete', id : 2,  parentId : null, callback : => data.delete (err)=> @propagateEvent KDEventType: 'ActivityIsDeleted'  }
+          ]
+        ]
+        callback    : (event)=> @settingsButton.contextMenu event
+    else
+      @settingsButton = new KDCustomHTMLView tagName : 'span', cssClass : 'hidden'

   viewAppended:->
     @setTemplate @pistachio()
@@ -325,6 +344,7 @@
           {{@utils.applyTextExpansions #(body)}}

         <time>{{$.timeago #(meta.createdAt)}}</time>
+
{{> @settingsButton}}

     """


dnd uploader fixes

dnd uploader fixes

--- client/landing/app/CommonViews/DNDUploader.coffee
+++ client/landing/app/CommonViews/DNDUploader.coffee
@@ -28,24 +28,19 @@
       filePath   = "[#{fsFile.vmName}]#{fsFile.path}"
       parentPath = "[#{fsFile.vmName}]#{fsFile.parentPath}"

-      bindAbort = ->
-        nodeView.showProgressView 1
+      bindAbort = (nodeView)->
         nodeView.once "abortProgress", => fsFile.abort yes

-      fsFile._isBeingUploaded = yes
       nodeView = @finder.treeController.nodes[filePath]
       if nodeView
-      then bindAbort()
+      then bindAbort nodeView
       else
         @finder.treeController.on "NodeWasAdded", (nodeView)->
-          if nodeView.getData().path is filePath and fsFile._isBeingUploaded
-            bindAbort()
+          if nodeView.getData().path is filePath
+            bindAbort nodeView

         fsFile.save "", =>
           @finder.expandFolders FSHelper.getPathHierarchy parentPath
-
-    @on "uploadEnd", (fsFile)->
-      fsFile._isBeingUploaded = no

   viewAppended: ->
     super
@@ -62,16 +57,11 @@
         <small>#{if uploadToVM then defaultPath else ''}</small>
       </div>
     """
+    @_uploaded = {}

   drop: (event)->
     super
     {files, items}  = event.originalEvent.dataTransfer
-    # folders are not allowed
-    if items.length
-      for item in items
-        itemEntry = item.webkitGetAsEntry?()
-        if itemEntry.isDirectory
-          return @notify "Folder uploads not available for now.", "error"

     if files.length >= 20
       @notify """
@@ -82,8 +72,26 @@
       You can archive your files and try again.
       """

-    if files.length
+    if items?.item?(0)?.webkitGetAsEntry
+      for item in items
+        entry = item.webkitGetAsEntry()
+        if entry.isDirectory
+          @readDirectory entry.filesystem.root, (files)=>
+            @uploadFiles files, event
+          , =>
+            @uploadFiles files, event
+        else if entry.isFile
+          entry.file (file)=>
+            @uploadFiles [file], event
+    else
+      @uploadFiles files, event
+
+  uploadFiles: (files, event)->
+    @_uploaded or= {}
+    if files?.length
+
       lastFile = files.last
+
       for file, index in files
         sizeInMb = file.size/1024/1024
         if sizeInMb > 100 && @getOptions().uploadToVM
@@ -91,11 +99,20 @@
           continue
         reader = new FileReader
         reader.onloadend = do (file=files[index])=> (readEvent)=>
+
+          fileName = file.fileName or file.name
+
+          if file.relativePath
+            return if @_uploaded[file.relativePath + fileName]
+            @_uploaded[file.relativePath + fileName] = yes
+
           if @getOptions().uploadToVM
-            fsFile = @upload file.name, readEvent.target.result
+            fsFile = @upload fileName, readEvent.target.result, file.relativePath
+
           @emit "dropFile",
             origin  : "external"
-            filename: file.name
+            filename: fileName
+            path    : file.relativePath or no
             instance: fsFile
             content : readEvent.target.result
             isLast  : file is lastFile
@@ -122,6 +139,27 @@

           @reset() if item is lastItem

+  readDirectory: (dirEntry, callback, error)->
+
+    callback.files  or= []
+    callback.i = 0
+
+    dirReader = dirEntry.createReader()
+    relative  = dirEntry.fullPath.replace(/^\//, "").replace /(.+?)\/?$/, "$1/"
+
+    dirReader.readEntries (entries)=>
+      for entry in entries
+        callback.i++
+        if entry.isFile
+          entry.file (file)->
+            file.relativePath = relative + file.name
+            callback.files.push file
+        else
+          @readDirectory entry, callback, error
+      if entries.length is callback.i
+        callback callback.files
+    , error
+
   setPath: (@path=@options.defaultPath)->
     {uploadToVM, title} = @getOptions()
     @updatePartial """
@@ -144,6 +182,7 @@
   saveFile: (fsFile, data)->
     @emit "uploadStart", fsFile
     fsFile.saveBinary data, (err, res, progress)=>
+      progress = res unless progress
       return if err
       if res.finished
         @emit "uploadEnd", fsFile
@@ -152,10 +191,18 @@
       else
         @emit "uploadFile", fsFile, progress.percent

-  upload: (fileName, contents)->
+  upload: (fileName, contents, relativePath)->
+
+    folder = if relativePath and relativePath isnt fileName
+    then "#{@path}/#{relativePath.replace /\/[^\/]*$/, ''}"
+    else @path
+
     modalStack   = KDModalView.createStack lastToFirst: yes
-    fsFolderItem = FSHelper.createFileFromPath @path, 'folder'
-    fsFileItem   = FSHelper.createFileFromPath "#{@path}/#{fileName}"
+    fsFolderItem = FSHelper.createFileFromPath folder, 'folder'
+    fsFileItem   = FSHelper.createFileFromPath "#{folder}/#{fileName}"
+
+    return if fsFolderItem.path.match /\/(\.git|__MACOSX)\/?/
+    return if fsFileItem.path.match   /\/\.DS_Store/

     upload = =>
       fsFileItem.exists (err, exists)=>
@@ -188,7 +235,7 @@

     fsFolderItem.exists (err, exists)=>
       unless exists
-      then fsFolderItem.save -> upload()
+      then FSHelper.createRecursiveFolder fsFolderItem, -> upload()
       else upload()

     return fsFileItem


eventmanager: add validation to event delegation

--- client/Main/messageeventmanager.coffee
+++ client/Main/messageeventmanager.coffee
@@ -67,6 +67,8 @@
       message.replies.push reply
       message.repliesCount++

+      return  unless KD.singletons.socialapi.isFromThisBrowser reply
+
       message.emit "AddReply", reply
       message.emit "update"

@@ -81,5 +83,9 @@
     message.replies = message.replies.filter (reply) -> reply.getId() isnt replyId
     message.repliesCount--

+    return  unless KD.singletons.socialapi.isFromThisBrowser message
+
     message.emit "RemoveReply", reply
     message.emit "update"
+
+


payment: fix broken event emition

payment: fix broken event emition

--- client/Pricing/views/singleplan.coffee
+++ client/Pricing/views/singleplan.coffee
@@ -12,8 +12,6 @@
     {title, cssClass, planFeatures, monthPrice, yearPrice, description} = @getOptions()

     normalizedPrice = monthPrice / 100
-
-    name = title.toLowerCase()

     @addSubView new KDHeaderView
       type     : 'medium'
@@ -39,6 +37,10 @@
     @addSubView new KDButtonView
       style     : 'plan-buy-button'
       title     : 'SELECT'
-      callback  : => @emit 'PlanSelected', name, monthPrice, yearPrice
+      state     : { name, monthPrice, yearPrice }
+      callback  : =>
+        { title, monthPrice, yearPrice } = @getOptions()
+        name = title.toLowerCase()
+        @emit 'PlanSelected', name, monthPrice, yearPrice


Fix recovery token route

Fix recovery token route

--- client/app/MainApp/kodingrouter.coffee
+++ client/app/MainApp/kodingrouter.coffee
@@ -163,7 +163,8 @@
       '/:name?/Activity/:activitySlug'  : goToContent.Activity
       '/:name?/Apps/:appSlug'           : goToContent.Apps

-      '/recover/:recoveryToken': ({params:{recoveryToken}})->
+      '/:name?/Recover/:recoveryToken': ({params:{recoveryToken}})->
+        return if recoveryToken is 'Password'
         mainController.appReady =>
           # TODO: DRY this one
           $('body').addClass 'login'
@@ -181,9 +182,8 @@
                   """
             else
               {loginScreen} = mainController
-              loginScreen.resetForm.addCustomData {recoveryToken}
-              loginScreen.animateToForm "reset"
-            # @utils.defer => @clear()
+              loginScreen.headBannerShowRecovery recoveryToken
+            @clear()

       '/invitation/:inviteToken': ({params:{inviteToken}})->
         inviteToken = decodeURIComponent inviteToken


Formatting

Formatting

--- SingularityUI/app/controllers/TaskDetail.coffee
+++ SingularityUI/app/controllers/TaskDetail.coffee
@@ -154,14 +154,14 @@
         if !task.get('isStillRunning')
             updates = task.get('taskUpdates')
             decomMessage = updates.filter (u) =>
-              return u.statusMessage?.indexOf('DECOMISSIONING') != -1 and u.taskState == 'TASK_CLEANING'
+                return u.statusMessage?.indexOf('DECOMISSIONING') != -1 and u.taskState == 'TASK_CLEANING'
             killedMessage = updates.filter (u) =>
-              return u.taskState == 'TASK_KILLED'
+                return u.taskState == 'TASK_KILLED'
             if decomMessage.length > 0 and killedMessage.length > 0
-              alerts.push
-                title: 'Alert:',
-                message: 'This task was killed due to a slave decommissioning.',
-                level: 'danger'
+                alerts.push
+                  title: 'Alert:',
+                  message: 'This task was killed due to a slave decommissioning.',
+                  level: 'danger'
         return alerts

     refresh: ->


Added failing tests for GH164.

Added failing tests for GH164.

New failing tests for content section using jQuery with an explicit
context ($() function with second param or $().find() method).

--- spec/browser-spec.coffee
+++ spec/browser-spec.coffee
@@ -168,7 +168,14 @@
       "should query from context (unrelated)": (browser)-> assert.equal browser.html(".now", browser.querySelector("#main")), ""
       "should combine multiple elements": (browser)->
         assert.equal browser.html("title, #main a"), "<title>The Living</title>Kill"
-
+    "jQuery":
+      topic: (browser)-> browser.evaluate('window.jQuery')
+      "should query by id": ($)-> assert.equal $('#main').size(), 1
+      "should query by element name": ($)-> assert.equal $('form').attr('action'), '#/dead'
+      "should query by element name (multiple)": ($)-> assert.equal $('label').size(), 2
+      "should query with descendant selectors": ($)-> assert.equal $('body #main a').text(), 'Kill'
+      "should query in context": ($)-> assert.equal $('body').find('#main a', 'body').text(), 'Kill'
+      "should query in context with find()": ($)-> assert.equal $('body').find('#main a').text(), 'Kill'

   "click link":
     topic: ->


vagrant: credential placeholder copy change

--- client/app/lib/providers/config.coffee
+++ client/app/lib/providers/config.coffee
@@ -177,4 +177,4 @@
     credentialFields       :
       queryString          :
         label              : 'Kite ID'
-        placeholder        : 'Querystring for local machine kite'
+        placeholder        : 'ID for my local machine kite'


Fixed wrong usages of console (bugfix for IE)

--- src/livereload.coffee
+++ src/livereload.coffee
@@ -19,12 +19,12 @@

     # i can haz sockets?
     unless @WebSocket = @window.WebSocket || @window.MozWebSocket
-      console.error("LiveReload disabled because the browser does not seem to support web sockets")
+      @console.error("LiveReload disabled because the browser does not seem to support web sockets")
       return

     # i can haz options?
     unless @options = Options.extract(@window.document)
-      console.error("LiveReload disabled because it could not find its own <SCRIPT> tag")
+      @console.error("LiveReload disabled because it could not find its own <SCRIPT> tag")
       return

     # i can haz reloader?
@@ -43,9 +43,9 @@

       error: (e) =>
         if e instanceof ProtocolError
-          console.log "#{e.message}."
+          console.log "#{e.message}." if console?
         else
-          console.log "LiveReload internal error: #{e.message}"
+          console.log "LiveReload internal error: #{e.message}" if console?

       disconnected: (reason, nextDelay) =>
         @listeners.disconnect?()


fix test

fix test

--- spec/hub/hub_spec.coffee
+++ spec/hub/hub_spec.coffee
@@ -330,12 +330,9 @@

             ipso (subject, socket) ->

+                delete subject.clients[id] for id of subject.clients

                 receivers = []
-
-                for id of subject.clients
-
-                    delete subject.clients[id]

                 subject.clients['SOCKET_ID'] =


Hall comment notifications

Hall comment notifications

--- plugins/hall/index.coffee
+++ plugins/hall/index.coffee
@@ -39,11 +39,27 @@
     'Most recent error: {{error.exceptionClass}}: {{error.message}} (details)'
   )

+  @commentMessage: Handlebars.compile(
+    '{{user.name}} commented on {{error.exceptionClass}}: {{error.message}} (details)' +
+    '
   "{{comment.message}}"'
+  )
+
   # Build the request body
   @messagePayload = (config, event) ->
     if event.trigger.type == 'projectSpiking'
       title: @spikingTitle event
       message: @spikingMessage
+        error: event.error
+        trigger: event.trigger
+        project: event.project
+      picture: BUGSNAG_AVATAR
+
+    else if event.trigger.type == 'comment'
+      title: @messageTitle event
+      message: @commentMessage
+        user: event.user
+        comment:
+          message: event.comment.message.truncate(80)
         error: event.error
         trigger: event.trigger
         project: event.project


fixed mock

fixed mock

--- spec/lib/bus-spec.coffee
+++ spec/lib/bus-spec.coffee
@@ -2,7 +2,7 @@

 describe.only 'bus', ->

-  Given -> @date = new Date
+  date = new Date

   Given ->
     @Sio = class Sio extends EventEmitter
@@ -21,12 +21,13 @@
         action: 'say'
         content: 'hello'
         target: 'you'
-        created: @created
+        created: date

   Given ->
     @Bus = requireSubject 'lib/bus', {
-      'socket.io': @Sio
+      'socket.io': @Sio,
+      './message-builder': @Builder
     }

   Given -> @bus = @Bus()
@@ -59,8 +60,8 @@
         action: 'say'
         content: 'hello'
         target: 'you'
-        created: @date
+        created: date
     When -> @message = @bus.message @params
-    Then -> expect(@message.data()).toBe @params
+    Then -> expect(@message.data()).toEqual @params
     And -> expect(@message.listeners('built').length).toBe 1
     And -> expect(@message.listeners('built')[0]).toEqual @bus.onBuiltMessage


Base new Player coordinates off of last line position, if set

--- game.coffee
+++ game.coffee
@@ -57,7 +57,7 @@
       elapsed_time = new_time - @old_time

       for player in @players
-        lastpos = player.lastPos()
+        lastpos = player.currentLinePos() or player.lastPos()
         if lastpos.direction == Game.NORTH
           player.addToLine(new Position([lastpos.x - (Game.VELOCITY * elapsed_time), lastpos.y], Game.NORTH, new_time))
         else if lastpos.direction == Game.SOUTH


Make Player0 line green to make it distinguishable

--- game.coffee
+++ game.coffee
@@ -43,6 +43,7 @@
       @player3 = new Player("Player3", new Position([0, Game.HEIGHT/2], Game.EAST, 0), @canvas)

       @players = [@player0, @player1, @player2, @player3]
+      @player0.current_line.stroke = 'green'

       if @browser
         @canvas.add(p.current_line) for p in @players


Remove legacy config addition from config loop

--- lib/symbol-provider.coffee
+++ lib/symbol-provider.coffee
@@ -129,9 +129,7 @@

     addedConfigEntry = false
     for {value} in allConfigEntries
-      if Array.isArray(value)
-        @addLegacyConfigEntry(value) if value.length
-      else if typeof value is 'object'
+      if not Array.isArray(value) and typeof value is 'object'
         @addConfigEntry(value)
         addedConfigEntry = true


Wrap all changes in a single undo transaction

--- lib/table-formatter.coffee
+++ lib/table-formatter.coffee
@@ -68,8 +68,9 @@
     myIterator = (obj) =>
       obj.replace(@formatTable(obj.match))

-    for range in selectionsRanges
-      editor.backwardsScanInBufferRange(@regex, range, myIterator)
+    editor.getBuffer().transact =>
+      for range in selectionsRanges
+        editor.backwardsScanInBufferRange(@regex, range, myIterator)

   formatTable: (text) ->
     padding = (len, str = ' ') -> str.repeat len


Revert "Also fix QD and Talk tutorial text"

This reverts commit 96d0d44b001c0f9a8f0a18a23609692964439288.

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -710,9 +710,6 @@

       # Start the tutorial
       @tutorial?.start()
-
-      @tutorial?.el.on 'start-tutorial enter-tutorial-step', =>
-        translate.refresh @tutorial.el.get 0

     else
       @submit(e)


disable key bindings in 4up

disable key bindings in 4up

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -209,7 +209,7 @@
     @activateFrame(frame)

   onKeyDown: (e) =>
-    return if @el.hasClass 'playing'  # disable while playing
+    return if @el.hasClass 'playing' or @el.attr('flicker') is 'false' # disable while playing or in 4up
     switch e.which
       when KEYS.one   then @activateFrame(0)
       when KEYS.two   then @activateFrame(1)


Few code lines were translated (#5511)

Few code lines were translated (#5511)

8, 15, 17, 20, 26, 47, 72, 73, 74, 75, 83

--- app/locale/ru.coco.coffee
+++ app/locale/ru.coco.coffee
@@ -5,22 +5,22 @@
 #    meta_description: "Learn typed code through a programming game. Learn Python, JavaScript, and HTML as you solve puzzles and learn to make your own coding games and websites."
 #    built_for_teachers_title: "A Coding Game Built with Teachers in Mind"
 #    built_for_teachers_blurb: "Teaching kids to code can often feel overwhelming. CodeCombat helps all educators teach students how to code in either JavaScript or Python, two of the most popular programming languages. With a comprehensive curriculum that includes six computer science units and reinforces learning through project-based game development and web development units, kids will progress on a journey from basic syntax to recursion!"
-#    built_for_teachers_subtitle1: "Computer Science"
+    built_for_teachers_subtitle1: "Информатика"
 #    built_for_teachers_subblurb1: "Starting with our free Introduction to Computer Science course, students master core coding concepts such as while/for loops, functions, and algorithms."
 #    built_for_teachers_subtitle2: "Game Development"
 #    built_for_teachers_subblurb2: "Learners construct mazes and use basic input handling to code their own games that can be shared with friends and family."
 #    built_for_teachers_subtitle3: "Web Development"
 #    built_for_teachers_subblurb3: "Using HTML, CSS, and jQuery, learners flex their creative muscles to program their own webpages with a custom URL to share with their classmates."
     century_skills_title: "Навыки 21 Века"
-#    century_skills_blurb1: "Students Don't Just Level Up Their Hero, They Level Up Themselves"
+    century_skills_blurb1: "Студенты не просто повышают уровень их героев, Они повышают свой уровень"
 #    century_skills_quote1: "You mess up…so then you think about all of the possible ways to fix it, and then try again. I wouldn't be able to get here without trying hard."
-#    century_skills_subtitle1: "Critical Thinking"
+    century_skills_subtitle1: "Критическое мышление"
 #    century_skills_subblurb1: "With coding puzzles that are naturally scaffolded into increasingly challenging levels, CodeCombat's programming game ensures kids are always practicing critical thinking."
 #    century_skills_quote2: "Everyone else was making mazes, so I thought, ‘capture the flag’ and that’s what I did."
-#    century_skills_subtitle2: "Creativity"
+    century_skills_subtitle2: "Креативность"
 #    century_skills_subblurb2: "CodeCombat encourages students to showcase their creativity by building and sharing their own games and webpages."
 #    century_skills_quote3: "If I got stuck on a level. I would work with people around me until we were all able to figure it out."
-#    century_skills_subtitle3: "Collaboration"
+    century_skills_subtitle3: "Коллаборация"
 #    century_skills_subblurb3: "Throughout the game, there are opportunities for students to collaborate when they get stuck and to work together using our pair programming guide."
 #    century_skills_quote4: "I’ve always had aspirations of designing video games and learning how to code ... this is giving me a great starting point."
     century_skills_subtitle4: "Коммуникация"
@@ -69,10 +69,10 @@
 #    global_title: "Join Our Global Community of Learners and Educators"
     global_subtitle1: "Учащиеся"
     global_subtitle2: "Строки с кодом"
-#    global_subtitle3: "Teachers"
-#    global_subtitle4: "Countries"
-#    go_to_my_classes: "Go to my classes"
-#    go_to_my_courses: "Go to my courses"
+    global_subtitle3: "Учителя"
+    global_subtitle4: "Страны"
+    go_to_my_classes: "Пойти на мои уроки"
+    go_to_my_courses: "Пойти на мои курсы"
     quotes_quote1: "Назовите любую онлайн программу, я попробовал её. Ни один из них не соответствует CodeCombat. Любой учитель, который хочет, чтобы их ученики научились кодировать ... начните здесь!"
 #    quotes_quote2: " I was surprised about how easy and intuitive CodeCombat makes learning computer science. The scores on the AP exam were much higher than I expected and I believe CodeCombat is the reason why this was the case."
 #    quotes_quote3: "CodeCombat has been the most beneficial for teaching my students real-life coding capabilities. My husband is a software engineer and he has tested out all of my programs. He put this as his top choice."
@@ -80,7 +80,7 @@
     see_example: "Посмотреть пример"
     slogan: "Самый захватывающий способ обучения реальному программированию."
     teach_cs1_free: "Обучать CS1 Бесплатно"
-#    teachers_love_codecombat_title: "Teachers Love CodeCombat"
+    teachers_love_codecombat_title: "Учителя любят CodeCombat"
 #    teachers_love_codecombat_blurb1: "Report that their students enjoy using CodeCombat to learn how to code"
 #    teachers_love_codecombat_blurb2: "Would recommend CodeCombat to other computer science teachers"
 #    teachers_love_codecombat_blurb3: "Say that CodeCombat helps them support students’ problem solving abilities"


Add new unauthorized dialog to global interactive state


Fix specs

Fix specs

--- spec/welcome-spec.coffee
+++ spec/welcome-spec.coffee
@@ -8,16 +8,18 @@
       atom.packages.activatePackage("welcome")

     waitsFor ->
-      editor = atom.workspace.getActiveTextEditor()
+      atom.workspace.open.calls.length == 2

   describe "when activated for the first time", ->
-    it "shows the welcome buffer", ->
-      expect(atom.workspace.open).toHaveBeenCalled()
-      expect(editor.getText()).toMatch(/Welcome to Atom/)
+    it "shows the welcome panes", ->
+      panes = atom.workspace.getPanes()
+      expect(panes).toHaveLength 2
+      expect(panes[0].getItems()[0].getTitle()).toBe 'Welcome'
+      expect(panes[1].getItems()[0].getTitle()).toBe 'Welcome Guide'

   describe "when activated again", ->
     beforeEach ->
-      atom.workspace.getActivePane().destroy()
+      atom.workspace.getPanes().map (pane) -> pane.destroy()
       atom.packages.deactivatePackage("welcome")
       atom.workspace.open.reset()

@@ -27,19 +29,22 @@
     it "doesn't show the welcome buffer", ->
       expect(atom.workspace.open).not.toHaveBeenCalled()

-  describe "the welcome:show-welcome-buffer command", ->
+  describe "the welcome:show command", ->
     workspaceElement = null

     beforeEach ->
       workspaceElement = atom.views.getView(atom.workspace)

     it "shows the welcome buffer", ->
-      atom.workspace.getActivePane().destroy()
-
-      atom.commands.dispatch(workspaceElement, 'welcome:show-welcome-buffer')
+      atom.workspace.getPanes().map (pane) -> pane.destroy()
+      expect(atom.workspace.getActivePaneItem()).toBeUndefined()
+      atom.commands.dispatch(workspaceElement, 'welcome:show')

       waitsFor ->
-        editor = atom.workspace.getActiveTextEditor()
+        atom.workspace.getActivePaneItem()

       runs ->
-        expect(editor.getText()).toMatch(/Welcome to Atom/)
+        panes = atom.workspace.getPanes()
+        expect(panes).toHaveLength 2
+        expect(panes[0].getItems()[0].getTitle()).toBe 'Welcome'
+


FIX: Fat arrow

FIX: Fat arrow

--- lib/interactive-process.coffee
+++ lib/interactive-process.coffee
@@ -94,6 +94,6 @@
       return resultP
     @interactiveAction = @interactiveAction.then =>
       debug "Started interactive action block in #{@cwd}"
-      action(interact).then (res) ->
+      action(interact).then (res) =>
         debug "Ended interactive action block in #{@cwd}"
         return res


removed debugger statement

removed debugger statement

--- app/assets/javascripts/cqlpatient.js.coffee
+++ app/assets/javascripts/cqlpatient.js.coffee
@@ -12,7 +12,6 @@
   constructor: (@patient) ->
     @_patient = @patient
     @_datatypes = @buildDatatypes()
-    debugger

   ###
   @returns {String}


ljax:{success,error,complete} hooks

ljax:{success,error,complete} hooks

--- app/assets/javascripts/ljax_rails.js.coffee
+++ app/assets/javascripts/ljax_rails.js.coffee
@@ -7,6 +7,8 @@
       cache: false
       headers: {'X-LJAX': 'true', 'X-LJAX-Container': container.id, 'X-LJAX-Partial': $(container).data('ljax-partial')}
       success: (data, status, xhr) ->
-        $(container).replaceWith data
+        $(container).replaceWith(data).trigger('ljax:success')
       error: (data, status, xhr) ->
-        console.log "error: #{data}"
+        $(container).trigger('ljax:error')
+      complete: ->
+        $(container).trigger('ljax:complete')


Link directly to the packages pane in Settings.

--- lib/view/error-view.coffee
+++ lib/view/error-view.coffee
@@ -19,7 +19,7 @@
             @button class: 'btn inline-block-tight', click: 'notRightNow', 'Not Right Now'

   openSettings: ->
-    atom.workspace.open 'atom://config'
+    atom.workspace.open 'atom://config/packages'
     @remove()

   notRightNow: ->


Pay attention to your own comments

Pay attention to your own comments

--- app/assets/javascripts/temp_chart.js.coffee
+++ app/assets/javascripts/temp_chart.js.coffee
@@ -7,7 +7,7 @@

     @duration  = @container.data('duration') || 21600

-    @graph_end = moment().valueOf() / 1e3 - 86400 # REMOVE THIS OFFSET THIS WAS FOR TESTING
+    @graph_end = moment().valueOf() / 1e3
     @graph_start = @graph_end - @duration

   draw: =>


refactor get-next-index

refactor get-next-index

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

 exports.programs = (sprite) ->
+  get_next_index = (current_index, length) ->
+    next_index = current_index + 1
+    return next_index if next_index < length
+    sprite.put 'running', 'repeat'
+    return 0
+
   program_behavior = (name) -> {
     _AUTHORITY: {
       selected: (world) -> world.index == world.get('next_index')
@@ -28,24 +34,18 @@

     next_index: 0
     reset_index: (world) -> world.put 'next_index', 0
-    next_command: (world) ->
-      current_index = world.get('next_index')
-      next_index = current_index + 1
+    next_instruction: (world) ->
+      instructions = world.find_child('instructions').find_children()
+      current_index = world.get 'next_index'

-      instructions = world.find_child('instructions').find_children()
-
-      # if this is our last instruction
-      if instructions.length <= next_index
-        next_index = 0
-        sprite.put 'running', 'repeat'
-
-      world.put 'next_index', next_index
+      next_index = get_next_index(current_index, instructions.length)
+      world.put 'next_index', next_index

       instructions[current_index]

     tick: (world, args) ->
       return unless world.get 'selected'
-      action = world.get 'next_command'
+      action = world.get 'next_instruction'
       sprite.call 'prepare', action.get('value') if action

     collision: (world, args) ->


count delete clicks

count delete clicks

--- src/layout/mixins/programs.coffee
+++ src/layout/mixins/programs.coffee
@@ -76,6 +76,7 @@
       { _LABEL: 'program_name', name: name }
       make.buttons "action", contents, my.command, (button, args) ->
         button.up.remove_child(button)
+        button.send 'click'
         button.send 'brick', -1

     ]


account registration dialog will be shown even if all accounts balances are 0

--- app/js/controllers/account.coffee
+++ app/js/controllers/account.coffee
@@ -199,10 +199,10 @@
             Wallet.refresh_accounts()

     $scope.regDial = ->
-        if Wallet.asset_balances[0]
-          $modal.open
+        #if Wallet.asset_balances[0]
+        $modal.open
             templateUrl: "registration.html"
             controller: "RegistrationController"
             scope: $scope
-        else
-          Growl.error '','Account registration requires funds.  Please fund one of your accounts.'
+        #else
+        #  Growl.error '','Account registration requires funds.  Please fund one of your accounts.'


Cleanup coffeescript

Cleanup coffeescript

--- src/scripts/modules/featured-books/featured-books.coffee
+++ src/scripts/modules/featured-books/featured-books.coffee
@@ -39,8 +39,8 @@
       @$el.find('.book').removeAttr('style')
       @_expanded = false

-    stopCarousel: (options) ->
-      if options?.finish then @$el.find('.book').finish() # Immediately finish the animation
+    stopCarousel: (options = {}) ->
+      if options.finish then @$el.find('.book').finish() # Immediately finish the animation
       clearInterval(@_carousel)
       @_carousel = null

@@ -87,6 +87,6 @@
       @_carousel = setInterval(nextFeatured, 7000)

     close: () ->
-      $(window).off("resize", @_resizer)
-      clearInterval(@_carousel)
+      $(window).off('resize', @_resizer)
+      @stopCarousel()
       super()


upd assocs

upd assocs

--- app/resources/view.coffee
+++ app/resources/view.coffee
@@ -109,4 +109,7 @@

   trigger: (event,data) ->
     data.type = event
-    super "update", data +    super "update", data
+
+  off: (callback) ->
+    super "update", callback


Fix #809

Fix #809

--- app/coffee/modules/issues/lightboxes.coffee
+++ app/coffee/modules/issues/lightboxes.coffee
@@ -28,7 +28,7 @@
 ## Issue Create Lightbox Directive
 #############################################################################

-CreateIssueDirective = ($repo, $model, $rs, $rootscope, lightboxService) ->
+CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService) ->
     link = ($scope, $el, $attrs) ->
         form = $el.find("form").checksley()
         $scope.issue = {}
@@ -55,13 +55,12 @@
             promise = $repo.create("issues", $scope.issue)

             promise.then (data) ->
+                $rootscope.$broadcast("issueform:new:success", data)
                 lightboxService.close($el)
-                console.log "succcess", data
-                $rootscope.$broadcast("issueform:new:success", data)
+                $confirm.notify("success")

-            # FIXME: error handling?
             promise.then null, ->
-                console.log "FAIL"
+                $confirm.notify("error")

         $el.on "click", ".button-green", (event) ->
             event.preventDefault()
@@ -73,8 +72,15 @@

     return {link:link}

+module.directive("tgLbCreateIssue", ["$tgRepo", "$tgConfirm", "$rootScope", "lightboxService",
+                                     CreateIssueDirective])

-CreateBulkIssuesDirective = ($repo, $rs, $rootscope, lightboxService) ->
+
+#############################################################################
+## Issue Bulk Create Lightbox Directive
+#############################################################################
+
+CreateBulkIssuesDirective = ($repo, $rs, $confirm, $rootscope, lightboxService) ->
     link = ($scope, $el, attrs) ->
         $scope.$on "issueform:bulk", (ctx, projectId, status)->
             lightboxService.open($el)
@@ -97,55 +103,15 @@
             promise.then (result) ->
                 $rootscope.$broadcast("issueform:new:success", result)
                 lightboxService.close($el)
+                $confirm.notify("success")

-            # TODO: error handling
             promise.then null, ->
-                console.log "FAIL"
+                $confirm.notify("error")

         $scope.$on "$destroy", ->
             $el.off()

     return {link: link}

-module.directive("tgLbCreateIssue", [
-    "$tgRepo",
-    "$tgModel",
-    "$tgResources",
-    "$rootScope",
-    "lightboxService",
-    CreateIssueDirective
-])
-
-module.directive("tgLbCreateBulkIssues", [
-    "$tgRepo",
-    "$tgResources",
-    "$rootScope",
-    "lightboxService",
-    CreateBulkIssuesDirective
-])
-
-#############################################################################
-## Issue Create Lightbox Directive
-#############################################################################
-
-# BulkCreateIssuesDirective = ($repo, $model, $rs, $rootscope) ->
-#     link = ($scope, $el, $attrs) ->
-#         $scope.$on "issueform:new", ->
-#             $el.removeClass("hidden")
-#
-#         $scope.$on "$destroy", ->
-#             $el.off()
-#
-#         $el.on "click", ".close", (event) ->
-#             event.preventDefault()
-#             $el.addClass("hidden")
-#
-#     return {link:link}
-
-# module.directive("tgLbCreateIssue", [
-#     "$tgRepo",
-#     "$tgModel",
-#     "$tgResources",
-#     "$rootScope",
-#     CreateIssueDirective
-# ])
+module.directive("tgLbCreateBulkIssues", ["$tgRepo", "$tgResources", "$tgConfirm", "$rootScope",
+                                          "lightboxService", CreateBulkIssuesDirective])


partial solution for #25

partial solution for #25

- at least you can update your algorithm now when changing the files

--- app/routes/algorithmRouter.coffee
+++ app/routes/algorithmRouter.coffee
@@ -68,9 +68,9 @@
       routeParts[routeParts.length - 1]++
       newRoute = routeParts.join('/')
     AlgorithmManagement.updateStatus(req.params.identifier, 'delete')
-    AlgorithmManagement.updateRoute(req.params.identifier, newRoute)
     #remove /route/info.json file
     AlgorithmManagement.deleteInfoFile('/data/json' + serviceInfo.path)
+    AlgorithmManagement.updateRoute(req.params.identifier, newRoute)
     AlgorithmManagement.removeFromRootInfoFile(serviceInfo.path)
     DockerManagement.removeImage(serviceInfo.image_name, (error) ->
       if(not error?)
@@ -83,6 +83,7 @@
             route = AlgorithmManagement.generateUrl(req.body)
             #check if we can find the route already
             imageName = AlgorithmManagement.generateImageName(req.body)
+            AlgorithmManagement.generateFolders(route)
             createAlgorithm(req,res, route, identifier, imageName)
         )
     )


hide 'view code' button if package is not installed

--- lib/package-detail-view.coffee
+++ lib/package-detail-view.coffee
@@ -91,7 +91,7 @@

     else
       @startupTime.hide()
-
+      @openButton.hide()

   subscribeToPackageManager: ->


+ id for model ajax

+ id for model ajax

--- app/assets/javascripts/mixins/model_ajax_mixin.js.coffee
+++ app/assets/javascripts/mixins/model_ajax_mixin.js.coffee
@@ -1,9 +1,10 @@
 Barbecue.ModelAjaxMixin = Ember.Mixin.create

  _ajax: (url, method, options) ->
+    id = @get 'id'
     type    = @get 'constructor'
     adapter = @get('store').adapterFor type
-    url     = '%@/%@'.fmt adapter.buildURL(type.typeKey), url
+    url     = '%@/%@/%@'.fmt adapter.buildURL(type.typeKey), id, url

     adapter.ajax url, method, options


Remove redundant edge styling

Remove redundant edge styling

--- app/scripts/alchemy/drawing/drawEdge.coffee
+++ app/scripts/alchemy/drawing/drawEdge.coffee
@@ -81,8 +81,6 @@
                     "M #{sourceX-offsetX},#{sourceY-offsetY} A #{hyp}, #{hyp} #{utils.edgeAngle(d)} 0, 1 #{targetX - arrowX}, #{targetY - arrowY}"
                 .each (d)->
                     d3.select(@).style utils.edgeStyle d
-            edge.selectAll 'path, line'
-                .style (d) -> utils.edgeStyle(d)

         else
             edge.select '.edge-line'


Added icon for KEY_UP action

Added icon for KEY_UP action

--- app/coffee/modules/main/dashboard/views/macroList.coffee
+++ app/coffee/modules/main/dashboard/views/macroList.coffee
@@ -96,6 +96,10 @@
       active_position = { position: 16, css: 'fa-clock-o', tooltip: '100ms Delay' }
       return { active_position }

+    else if @model.get('key_up')
+      active_position = { position: 256, css: 'fa-arrow-up', tooltip: 'Wait for key release' }
+      return { active_position }
+
     else
       position = @model.get('position')
       active_position = _.findWhere(positions, { position: position })


restore original URL after each test

restore original URL after each test

--- tests/batman/test_helper.coffee
+++ tests/batman/test_helper.coffee
@@ -7,6 +7,13 @@
   exports.document = w.document
   global.notStrictEqual = (actual, expected, message) -> ok expected != actual, message
   exports.IN_NODE = true
+
+if exports.history?.pushState?
+  originalLocation = window.location.href
+  originalReset = QUnit.reset
+  QUnit.reset = ->
+    window.history.pushState({},'',originalLocation)
+    originalReset.apply(this, arguments)

 if exports.IN_NODE
   do ->


JCredential: fix linter error, no need fat arrow

--- workers/social/lib/social/models/computeproviders/credential.coffee
+++ workers/social/lib/social/models/computeproviders/credential.coffee
@@ -276,7 +276,7 @@
       $in: [ delegate.getId(), group.getId() ]
     }

-    Relationship.someData relSelector, relOptions, (err, cursor) =>
+    Relationship.someData relSelector, relOptions, (err, cursor) ->
       return callback err  if err?
       cursor.toArray callback


remove copy paste

remove copy paste

--- apps/artwork_purchase/client/purchase_signup_form.coffee
+++ apps/artwork_purchase/client/purchase_signup_form.coffee
@@ -12,9 +12,6 @@
   _.extend @prototype, Form

   initialize: ({ @user }) -> #
-
-  # initializeMailcheck: ->
-  #   Mailcheck.run('#js-mailcheck-input-modal', '#js-mailcheck-hint-modal', false)

   submit: ({ success, error, isWithAccountCallback }) ->
     @user.set (data = @serializeForm())


Check in a cool impl of the content broker ;)

--- library/gearing/content.coffee
+++ library/gearing/content.coffee
@@ -41,23 +41,40 @@
 module.exports.Broker = class Broker extends events.EventEmitter

     # Content negotiate the request/response pair to use the correct
-    # protocol. The protocol is implemented by the associated flusher
-    # that might or might not have been previously associated with
-    # the specified regular expression pattern that matches `Accept`.
+    # protocol. The protocol is implemented by the content negotiator
+    # that might or might not have been previously add to the broker.
+    # The negotiator returns a function if it can handle the pair.
     negotiate: (request, response, content) ->
-        registry = @constructor.registry ?= {}
-        for own pattern, flusher of registry
-            matches = response.accepts pattern
-            return flusher arguments... if matches
+        registry = @constructor.registry ?= []
+        for own index, negotiator of registry
+            args = [request, response, content]
+            flusher = negotiator arguments...
+            handles = _.isFunction flusher
+            return flusher args... if handles
         response.write content.toString()

-    # Register the specified content flusher with the broker. The
-    # flusher is associated with the pattern that will be used to
-    # match the request/response pair to be handled by the flusher.
-    # Please refer to the `accepts` middleware for more information.
-    @associate: (pattern, flusher) ->
-        registry = @registry ?= {}
-        stringified = _.isString pattern
-        regexify = (s) -> new RegExp RegExp.escape s
-        pattern = regexify pattern if stringified
-        registry[pattern] = flusher; this
+    # Register the specified content negotiator with the broker. The
+    # The negotiator returns a function if it can handle the request
+    # and response pair. If specific negotiator cannot handle the
+    # pair, it should return anything other than a function object.
+    @associate: (negotiator) ->
+        isValid = _.isFunction negotiator
+        invalid = "Checker is not a valid method"
+        throw new Error(invalid) unless isValid
+        (@registry ?= []).unshift negotiator
+
+    # Associate the JSON negotiator with the broker. This method
+    # checks if the content is either `Object` or `Array`, and if
+    # it is then it sets the appropriate `Content-Type` header and
+    # writes out the properly encoded JSON response to the client.
+    @associate (request, response, content) ->
+        isArray = _.isArray content
+        isObject = _.isObject content
+        return unless isArray or isObject
+        (request, response, content) ->
+            jsonType = "application/json"
+            doesHtml = response.accepts /html/
+            spaces = if doesHtml then 4 else null
+            response.setHeader "Content-Type", jsonType
+            jsoned = (x) -> JSON.stringify x, null, spaces
+            response.write jsoned content


minor syntax conv changes in the restful

--- library/nucleus/restful.coffee
+++ library/nucleus/restful.coffee
@@ -72,7 +72,7 @@
         wrongSignature = "malformed limitation signature"
         assert _.isString(synopsis), "got no synopsis"
         assert _.isFunction(limitation), noLimitation
-        assert limitation.length is 3, wrongSignature
+        assert limitation.length >= 3, wrongSignature
         assert _.isArray inherted = @$condition or []
         return @$condition = inherted.concat
             limitation: limitation
@@ -95,7 +95,7 @@
         wrongSignature = "a wrong implement signature"
         try implement = _.find arguments, _.isFunction
         assert _.isFunction(implement), noImplement
-        assert implement.length is 5, wrongSignature
+        assert implement.length >= 3, wrongSignature
         assert _.isArray inherited = @$middleware or []
         @$middleware = inherited.concat implement; @

@@ -142,15 +142,15 @@
         [tokens, knowns] = [super, @constructor.SUPPORTED]
         return @unsupported arguments... unless method in knowns
         missing = "Missing implementation for #{method} method"
-        throw new Error missing unless method of this
-        variables = [tokens.resource, tokens.domain]
-        headers = @upstreamAsync "headers", _.identity
-        partial = _.partial headers, request, response
-        response.on "header", -> partial variables...
+        throw new Error missing.toString() unless method of this
+        assert variables = [tokens.resource, tokens.domain]
+        assert headers = @upstreamAsync "headers", _.identity
+        assert partial = _.partial headers, request, response
+        response.on "header", -> try partial variables...
         assert mw = @constructor.middleware().bind this
         prestreamer = @upstreamAsync "preprocess", =>
             mw([request, response, variables...]) (error) =>
-                this[method](request, response, variables...)
+                this[method] request, response, variables...
                 poststreamer = @upstreamAsync "postprocess"
                 poststreamer request, response, variables...
         prestreamer request, response, variables...


Use the resolved value in all cases for now

--- apps/artist/routes.coffee
+++ apps/artist/routes.coffee
@@ -30,13 +30,13 @@

         res.locals.sd.ARTIST = artist.toJSON()
         res.locals.sd.TAB = tab = req.params.tab or ''
-        res.locals.sd.STATUSES = statuses.statuses
+        res.locals.sd.STATUSES = statuses = statusesRequest.value

         res.render 'index',
           artist: artist
           carousel: carousel
           tab: tab
-          statuses: statuses.statuses
+          statuses: statuses
           nav: nav
           jsonLD: stringifyJSONForWeb(artist.toJSONLD())


handling the exception when the text() or html() call with a non-exist element

--- src/quo.output.coffee
+++ src/quo.output.coffee
@@ -4,12 +4,12 @@
         if value or $$.toType(value) is "number"
           @each -> @textContent = value
         else
-            @[0].textContent
+            (if @length > 0 then @[0].textContent else '')

     $$.fn.html = (value) ->
         type = $$.toType(value)
         if type is "undefined"
-            @[0].innerHTML
+            (if @length > 0 then @[0].innerHTML else)
         else
             @each ->
                 if type is "string"


Updating

Updating

--- config.cson
+++ config.cson
@@ -12,7 +12,7 @@
         ".arcconfig"
         "BUCK.autodeps"
       ]
-      "source.matlab": [
+      "source.octave": [
         "m"
       ]
       "source.python": [


Better event names

Better event names

--- src/retry-ajax.coffee
+++ src/retry-ajax.coffee
@@ -1,6 +1,6 @@
 events =
-  RETRY: 'ajaxRetry.vtex'
-  FAIL_RETRY: 'failAjaxRetry.vtex'
+  AJAX_RETRY: 'ajaxRetry.vtex'
+  AJAX_ERROR: 'ajaxError.vtex'

 $.ajaxPrefilter (options, originalOptions, jqXHR) ->
   jqXHR.retry = (opts) ->
@@ -23,7 +23,6 @@
     # whenever we do make this request, pipe its output to our deferred

     nextRequest = ->
-      $(window).trigger(events.RETRY, input)
       $.ajax(ajaxOptions).retry(
         times: times - 1
         timeout: opts.timeout
@@ -31,6 +30,8 @@
       return

     if times > 1 and (!jqXHR.statusCodes or $.inArray(input.status, jqXHR.statusCodes) > -1)
+      $(window).trigger(events.AJAX_RETRY, input)
+
       # implement Retry-After rfc
       # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.37
       if retryAfter
@@ -49,7 +50,7 @@
         nextRequest()
     else
       if (input.statusText isnt 'abort' or jqXHR.statusText isnt 'abort')
-        $(window).trigger(events.FAIL_RETRY, input)
+        $(window).trigger(events.AJAX_ERROR, input)
       # no times left, reject our deferred with the current arguments
       output.rejectWith this, arguments
     output


Cache bust

Cache bust

--- src/shakeshack.coffee
+++ src/shakeshack.coffee
@@ -9,4 +9,4 @@

 module.exports = (robot) ->
   robot.respond /(shake|shack) cam/i, (msg) ->
-    msg.send 'http://www.shakeshack.com/camera.jpg'+    msg.send 'http://www.shakeshack.com/camera.jpg?' + new Date().getTime()


testrunner: a bit more DRY

testrunner: a bit more DRY

--- src/testrunner.coffee
+++ src/testrunner.coffee
@@ -11,6 +11,7 @@
 # TODO: find a better way to do this.
 # https://github.com/brunch/brunch/pull/111#issuecomment-2244266
 TEMP = "/tmp/brunchtest"
+SPECFILE = path.join TEMP, "specs.js"

 exports.run = (options, callback) ->
@@ -40,14 +41,14 @@

   fs.mkdir TEMP, 0755, ->
     # Write specs to temporary folder.
-    fs.writeFileSync TEMP + "/specs.js", specs.join "\n"
+    fs.writeFileSync SPECFILE, specs.join "\n"
     # Run specs in fake browser.
     jsdom.env
       html: path.join brunchdir, "index.html"
       scripts: [
         path.resolve options.buildPath, "web/js/app.js"
         path.resolve __dirname, "../vendor/jasmine.js"
-        "/tmp/brunchtest/specs.js"
+        SPECFILE
       ]
       done: (error, window) ->
         helpers.logError error if error?


demo resets for now

demo resets for now

--- src/views/editor.cjsx
+++ src/views/editor.cjsx
@@ -17,8 +17,8 @@
     dom.className @textarea.parentElement, 'f-editor-editing'

     @editable =

-
-    @editable.innerHTML = localStorage["/twi-light/f-editor/demo-html"] or """
+    # localStorage["/twi-light/f-editor/demo-html"] or
+    @editable.innerHTML = """

These shortcuts only work on chrome right now (WIP)

we have ctrl+b to  bold

we also have ctrl+i for italics


add append x, [timeout], [listen]

add append x, [timeout], [listen]

--- coffee/lib.coffee
+++ coffee/lib.coffee
@@ -828,8 +828,8 @@
 			tr: ///^(?:table|tbody|tfoot|thead)$///
 			option: ///^select$///
 			li: ///^(?:ol|ul)$///
-
-
+
+
 		T = []; html = []; pos = 0 ; s = i_html
 		pop = ->
 			tag = T.pop()
@@ -1552,9 +1552,20 @@

 	before: insert$.inline "before", ""
 	after: insert$.inline "after", "e=e.nextSibling"
-	insertBefore: (val) -> val=@wrap(val).before this ; this
+	insertBefore: (val) -> @wrap(val).before this ; this
 	insertAfter: (val) -> @wrap(val).after this ; this
-	append: (val) -> e=@element; (for v in @wrap(val).all() then e.appendChild v); this
+	append: (val, timeout, listen) ->
+		val = @wrap val
+		if timeout
+			for i in val.items()
+				i.before i1=@wrap("

").css(visibility: 'hidden').css(x=i.css 'width,height,float,position,display'.split /,/).animate width: 0, height: 0, timeout
+				say x
+				delete x.width, x.height
+				@append i2=@wrap("

").css(visibility: 'hidden', width:0, height:0).css(x).animate width: @width(), height: @height(), timeout
+				save = i.css ['position']
+				i.css(position: 'absolute').animate left: i2.left(), top: i2.top(), timeout, do(i1, i2, i) => => @append i.css save; i1.union(i2).free(); if listen then if typeof listen == 'string' then @send listen else listen.call this
+		else e=@element; (for v in val.all() then e.appendChild v)
+		this
 	appendTo: (val) -> @wrap(val).append this ; this
 	prepend: (val) -> f=(e=@element).firstChild; (for v in @wrap(val).all() then e.insertBefore v, f); this
 	prependTo: (val) -> @wrap(val).prepend this ; this


newkoding: export variable KONFIG_JSON

newkoding: export variable KONFIG_JSON

--- config/main.dev.coffee
+++ config/main.dev.coffee
@@ -248,9 +248,10 @@
       env = """
       export GOPATH=#{projectRoot}/go
       export GOBIN=#{projectRoot}/go/bin
+      export KONFIG_JSON='#{KONFIG.JSON}'

       """
-      env += "export #{key}='#{val}'\n" for key,val of KONFIG.ENV
+      # env += "export #{key}='#{val}'\n" for key,val of KONFIG.ENV
       return env

     workersRunList = ->


Update inspector_address.coffee

Update inspector_address.coffee

--- app/assets/javascripts/inspector_address.coffee
+++ app/assets/javascripts/inspector_address.coffee
@@ -409,7 +409,7 @@
       {
         element: ".leaflet-control-zoom.leaflet-bar.leaflet-control"
         intro: "Space is always at a premium in NYC and some properties may have more than one building on it - one right on the street and another in the backyard. Both of them share the same address. Use the zoom function to see who owns this building. "
-        position: "right"
+        position: "left"
         polygon_index: 13
       }
       {


Change gold xp graph colors to yellow

Change gold xp graph colors to yellow

--- app/assets/javascripts/matches/goldxp.js.coffee
+++ app/assets/javascripts/matches/goldxp.js.coffee
@@ -6,6 +6,8 @@
 window.DOTA2RAILS.matches.components.graph = (() ->
   line_home = null
   chart = null
+  gold_color = d3.hsl(50, 1, 0.5).brighter(0.2)
+  xp_color = d3.hsl(50, 1, 0.5).darker(1.2)
   init = ->
     nv.addGraph
       generate: ->
@@ -76,7 +78,7 @@
             else if hero in player_teams['dire']
               d += if herodata[idx]? then herodata[idx] else herodata[herodata.length - 1]
           values.push {'x': time, 'y': r - d}
-        dataset.push {key: data_label, color: (if data_label is 'gold' then 'blue' else 'green'), values: values}
+        dataset.push {key: data_label, color: (if data_label is 'gold' then gold_color else xp_color), values: values}
       dataset

   update = (time) ->


preventing ghostclicks on purchase button

--- app/assets/javascripts/mobile/_mobile.js.coffee
+++ app/assets/javascripts/mobile/_mobile.js.coffee
@@ -60,7 +60,13 @@
                 ($ ".product-thumbnail[data-index='#{index}']")
                     .removeClass('hide').addClass('on')

-            ($ '.product-price').tap -> scrollTo 0, ($ @).offset().top - 56
+            # setTimeout is required because
+            # tap was clicking the purchase
+            # link after scrollTo
+            ($ '.product-details').tap ->
+                setTimeout =>
+                    scrollTo 0, ($ @).offset().top - 56
+                , 100

             redirect_to_order_form = ->
                 data =


removing product-gallery-control click event - the hit areas were too small for touch devices

--- app/assets/javascripts/mobile/_mobile.js.coffee
+++ app/assets/javascripts/mobile/_mobile.js.coffee
@@ -51,14 +51,6 @@
                     ($ @).prev().removeClass('hide')
                     ($ '.product-gallery-control.on')
                         .removeClass('on').prev().addClass('on')
-
-            ($ '.product-gallery-control').click ->
-                index = ($ @).data('index')
-                ($ '.product-gallery-control.on').removeClass('on')
-                ($ @).addClass('on')
-                ($ '.product-thumbnail.on').addClass('hide').removeClass('on')
-                ($ ".product-thumbnail[data-index='#{index}']")
-                    .removeClass('hide').addClass('on')

             # setTimeout is required because
             # tap was clicking the purchase


fix flash messages showing

fix flash messages showing

--- app/assets/javascripts/pt/tags/remote.js.coffee
+++ app/assets/javascripts/pt/tags/remote.js.coffee
@@ -13,7 +13,7 @@
           $(document).trigger 'page:change'
         $(this).ajaxSubmit
           success: (html, responseText, xhr) ->
-            unless xhr.getResponseHeader("content-type").match('text/javascript').length
+            unless (type = xhr.getResponseHeader("content-type").match('text/javascript')) && type.length
               addHtml html
           error: (response, status) ->
             switch response.status


Set Status.initialized to true when account is logged out. Closes #24

--- app/assets/javascripts/sprangular/module.coffee
+++ app/assets/javascripts/sprangular/module.coffee
@@ -60,5 +60,6 @@
   $rootScope.$on '$routeChangeError', ->
     Status.routeChanging = false

-  Account.init().then ->
-    Status.initialized = true
+  Account.init()
+    .success -> Status.initialized = true
+    .error -> Status.initialized = true


dirty yet great image proxy, thank you chris

--- client/landing/app/Applications/Activity.kdapplication/views/embedboxviews.coffee
+++ client/landing/app/Applications/Activity.kdapplication/views/embedboxviews.coffee
@@ -1,3 +1,6 @@
+proxifyUrl =(url)->
+  "https://api.koding.com/1.0/image.php?url="+ encodeURIComponent(url)
+
 class EmbedBoxLinksViewItem extends KDListItemView

   constructor:(options,data)->
@@ -29,13 +32,13 @@
       callback :=>
         @changeEmbed()

-    @favicon = data.favicon_url or ""
+    @favicon = (data.favicon_url) or ""

     @faviconImage = new KDCustomHTMLView
       tagName     : "img"
       cssClass    : "embed-favicon hidden"
       attributes  :
-        src       : @favicon
+        src       : proxifyUrl @favicon
         alt       : data.title

     @faviconImage.show() if @favicon isnt ""
@@ -54,11 +57,12 @@

     @setClass "active"

-  setFavicon:(fav="")->
-    @favicon = fav
-
-    @faviconImage.setDomAttributes src:@favicon
-    @faviconImage.show()
+  setFavicon:(fav)->
+    if fav?
+      @favicon = fav
+
+      @faviconImage.setDomAttributes src:proxifyUrl @favicon
+      @faviconImage.show()

   viewAppended:->
     super()
@@ -255,10 +259,10 @@
       if @getEmbedImageIndex() < @getData().link_embed?.images.length-1
         imgSrc = @getData().link_embed?.images?[@getEmbedImageIndex()]?.url
         if imgSrc
-          @getDelegate().embedImage.setSrc imgSrc
+          @getDelegate().embedImage.setSrc proxifyUrl imgSrc
         else
           # imgSrc is undefined - this would be the place for a default
-          fallBackImgSrc="http://koding.com/images/service_icons/Koding.png"
+          fallBackImgSrc="https://koding.com/images/service_icons/Koding.png"
           @getDelegate().embedImage.setSrc fallBackImgSrc

         # Either way, set the embedImageIndex to the appropriate nr
@@ -329,7 +333,7 @@

-
+

@@ -365,8 +369,8 @@

   # this includes a fallback for when the embedimageindex is out of bounds
   # it will however still request a nonsensical image src
-    @imageLink  = @getData().link_embed?.images?[@getData().link_embed_image_index]?.url or\
-                  @getData().link_embed?.images?[0]?.url or\
+    @imageLink  = proxifyUrl(@getData().link_embed?.images?[@getData().link_embed_image_index]?.url) or\
+                  proxifyUrl(@getData().link_embed?.images?[0]?.url) or\
                   "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" # hardcode a default

     @imageAltText = (@getData().link_embed?.title + \


Variable cleanup in refining.coffee

Variable cleanup in refining.coffee

--- src/coffee/refining.coffee
+++ src/coffee/refining.coffee
@@ -1,8 +1,3 @@
-#query = require("pg-query")
-
-
-
-
 #oreList = query("select itemName from Ore order by itemName", (err, rows, result) ->
 #	alert rows )

@@ -10,23 +5,21 @@
 #alert oreList
 #alert "3333"
 refiningTable = null
-refineMe = []
-#accepts optional array of values as 2nd parameter for parameterized queries

 AddNewRefiningElement = ->
-  Quantity = parseInt($("#inputQuantity").val(), 10)
-  unless isNaN(Quantity)
+  quantity = parseInt($("#inputQuantity").val(), 10)
+  unless isNaN(quantity)
     itemName = $("#oreInput1").val()

-    rowData = refiningTable.row((rowIdx) ->
-      return refiningTable.cell(rowIdx, 0).data() is itemName
+    oreRow = refiningTable.row((rowID) ->
+      return refiningTable.cell(rowID, 0).data() is itemName
     ).index()

-    if rowData? # Found
-      newData = refiningTable.cell(rowData, 1).data()
-      refiningTable.cell(rowData, 1).data(newData + Quantity)
+    if oreRow? # Found
+      oldData = refiningTable.cell(oreRow, 1).data()
+      refiningTable.cell(oreRow, 1).data(oldData + quantity)
     else # Not found
-      refiningTable.row.add([itemName, Quantity])
+      refiningTable.row.add([itemName, quantity])

     refiningTable.draw()
   return


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/active_bounds.coffee
+++ src/active_bounds.coffee
@@ -12,4 +12,4 @@

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


extract data source correctly on line selections (#6404)

* extract data source correctly on line selections

* clean up code

--- bokehjs/src/coffee/models/tools/gestures/tap_tool.coffee
+++ bokehjs/src/coffee/models/tools/gestures/tap_tool.coffee
@@ -28,8 +28,9 @@

       renderers_by_source = @model._computed_renderers_by_data_source()

-      for ds, renderers of renderers_by_source
-        sm = renderers[0].data_source.selection_manager
+      for _, renderers of renderers_by_source
+        ds = renderers[0].data_source
+        sm = ds.selection_manager
         did_hit = sm.select(@, (@plot_view.renderer_views[r.id] for r in renderers), geometry, final, append)

         if did_hit and callback?


backend_postgres: syntax fixes

backend_postgres: syntax fixes

--- src/local/backend_postgres.coffee
+++ src/local/backend_postgres.coffee
@@ -216,7 +216,7 @@
     # Affiliation management
     ##

-    getAffiliation = (node, user, cb) ->
+    getAffiliation: (node, user, cb) ->
         db = @db
         async.waterfall [(cb2) ->
             db.query "SELECT affiliation FROM affiliations WHERE node=$1 AND \"user\"=$2", [ node, user ], cb2
@@ -224,7 +224,7 @@
             cb2 null, (res.rows[0] and res.rows[0].affiliation) or "none"
         ], cb

-    setAffiliation = (node, user, affiliation, cb) ->
+    setAffiliation: (node, user, affiliation, cb) ->
         db = @db
         async.waterfall [(cb2) ->
             db.query "SELECT affiliation FROM affiliations WHERE node=$1 AND \"user\"=$2", [ node, user ], cb2