Update docs for visual test helpers. :memo:

--- tests/js/base-visual.coffee
+++ tests/js/base-visual.coffee
@@ -17,6 +17,8 @@
   # Configuration:
   #
   # - `label` - header text describing the test topic.
+  # - `anchorName` - for the section on its docs page, used to generate `docsUrl`.
+  # - `asFragments` - will render the test differently, directly inside `<body>`.
   # - `className` - a hook for any custom css.
   # - `footerHtml` - optional html for controls for additional testing.
   $.visualTest = (config) ->


Agentpie: changed label layout bounding box dimensions

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -283,8 +283,8 @@
                     d2.r
                 )
                 d.l = rightEdge
-                d.x = d.l + bbox.width / 2 + 5
-                d.r = d.l + bbox.width + 10
+                d.x = d.l + bbox.width / 2 + 2#5
+                d.r = d.l + bbox.width + 5#10
             else
                 console.log 'no conflicts for ', myInstanceDataId, ' ', d


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -283,7 +283,8 @@
             .duration(1500)
             #.attrTween("d", pieTween)
             .attrTween("d", (d,i) ->
-                console.log("d",d.value)
+                console.log("d",d)
+                console.log("i",i)
                 i = d3.interpolate({startAngle: 0, endAngle: 0}, {startAngle: d.startAngle, endAngle: d.endAngle})
                 return (t) ->
                     b = i(t)


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -147,11 +147,13 @@
         $(@node).children(".more-info").text($(@node).attr("data-moreinfo"))
         $(@node).children(".updated-at").text(@get('updatedAtMessage'))

-        width = 750 #width
-        height = 450 #height
+        # Build pie
+
+        width = 750
+        height = 450
         radiuso = 135 #outer radius
         radiusi = 90 #inner radius
-        radius = height / 2
+        radius = Math.min(width, height) / 2 #palette radius min
         labelRadius = 180

         color = d3.scale.category20()
@@ -170,6 +172,8 @@
             .attr("height", height)
             .append("svg:g")
             .attr("transform", "translate(" + width/2 + "," + height/2 + ")")
+
+        # end Build pie

         #create a marker element if it doesn't already exist
         defs = svg.select("defs")
@@ -234,7 +238,7 @@
             .remove()

         labelLayout = d3.geom.quadtree()
-        labellayout.extent([[-width,-height], [width,height] ])
+            .extent([[-width,-height], [width,height] ])
             .x((d) -> return d.x)
             .y((d) -> return d.y)
             #([]) #create an empty quadtree to hold label positions


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -257,16 +257,12 @@
                 #x1,y1,x2,y2 are the bounds of the rectangle that
                 #this node covers

-                if  (x1 > d.r + maxLabelWidth/2)
-                        #left edge of node is to the right of right edge of label
-                    ||(x2 < d.l - maxLabelWidth/2)
-                        #right edge of node is to the left of left edge of label
-                    ||(y1 > d.b + maxLabelHeight/2)
-                        #top (minY) edge of node is greater than the bottom of label
-                    ||(y2 < d.t - maxLabelHeight/2 )
-                        #bottom (maxY) edge of node is less than the top of label
-
-                      return true #don't bother visiting children or checking this node
+                #1. left edge of node is to the right of right edge of label
+                #2. right edge of node is to the left of left edge of label
+                #3. top (minY) edge of node is greater than the bottom of label
+                #4. bottom (maxY) edge of node is less than the top of label
+                if  (x1 > d.r + maxLabelWidth/2) ||(x2 < d.l - maxLabelWidth/2) ||(y1 > d.b + maxLabelHeight/2) || (y2 < d.t - maxLabelHeight/2 )
+                    return true #don't bother visiting children or checking this node

                 p = node.point
                 v = false, h = false


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -190,7 +190,8 @@
       d.y = -Math.cos(a) * (that.radius - 20)
       d.b = d.oy = d.y + 5
       d.t = d.y - bbox.height - 5
-      console.log 'getBBox'
+      console.log 'd.x' d.x
+      console.log 'd.y' d.y
       ### check whether the default position
          overlaps any other labels
       ###


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -149,7 +149,7 @@
             b = i(t);
             return $(@node).arc(b)

-  render: (data) ->
+  renderX2: (data) ->
         #console.log("update pie", data);

         if !data
@@ -377,3 +377,88 @@
                 return "M" + (d.r-2) + "," + d.b + "L" + (d.l+2) + "," + d.b + " " + d.cx + "," + d.cy

         )
+  render: (data) ->
+
+        if !data
+          data = @get("value")
+        if !data
+          return
+
+        $(@node).children(".title").text($(@node).attr("data-title"))
+        $(@node).children(".more-info").text($(@node).attr("data-moreinfo"))
+        $(@node).children(".updated-at").text(@get('updatedAtMessage'))
+
+        # Build pie
+
+        width = 750
+        height = 450
+        radiuso = 135 #outer radius
+        radiusi = 90 #inner radius
+        radius = Math.min(width, height) / 2 #palette radius min
+        labelRadius = 180
+
+        color = d3.scale.category20()
+
+        $(@node).children("svg").remove();
+
+        pie = d3.layout.pie()
+            .sort(null)
+            #.value((d) -> d.value)
+
+        arc = d3.svg.arc()
+          .outerRadius(radiuso)
+          .innerRadius(radiusi)
+
+        svg = d3.select(@node).append("svg")
+            .attr("width", width)
+            .attr("height", height)
+            .append("g")
+            .attr("transform", "translate(" + width/2 + "," + height/2 + ")")
+
+        #Create/select <g> elements to hold the different types of graphics
+        #and keep them in the correct drawing order
+        pathGroup = svg.select("g.piePaths")
+        if pathGroup.empty()
+            pathGroup = svg.append("g").attr("class", "piePaths")
+
+        pointerGroup = svg.select("g.pointers")
+        if pointerGroup.empty()
+            pointerGroup = svg.append("g").attr("class", "pointers")
+
+        labelGroup = svg.select("g.labels")
+        if labelGroup.empty()
+            labelGroup = svg.append("g").attr("class", "labels")
+
+        path = pathGroup.selectAll("path.pie")
+            #.data(data)
+            .data(pie(data))
+
+        path.transition()
+            .duration(1500)
+            #.attrTween("d", pieTween);
+            .attrTween("d", (d,i) ->
+                i = d3.interpolate({startAngle: 0,endAngle: 0}, {startAngle: d.startAngle,endAngle: d.endAngle})
+                return (t) ->
+                    b = i(t)
+                    return arc(b)
+            )
+
+        path.exit()
+            .transition()
+            .duration(300)
+            .attrTween("d", (d,i) ->
+                i = d3.interpolate({startAngle: d.startAngle,endAngle: d.endAngle},{startAngle: 2 * Math.PI,endAngle: 2 * Math.PI})
+                return (t) ->
+                    b = i(t)
+                    return arc(b)
+            )
+            .remove()
+
+        labels = labelGroup.selectAll("text")
+            .data(data.sort((p1,p2) -> return p1.startAngle - p2.startAngle))
+        labels.enter()
+            .append("text")
+            .attr("text-anchor", "middle")
+        labels.exit()
+            .remove()
+


Added a helper to convert a ruby hash to a delta.

--- tests/scripts/fuzzer.coffee
+++ tests/scripts/fuzzer.coffee
@@ -20,6 +20,20 @@
       d = JSON.parse(docDelta)
       window.Fuzzer[deltaRef] =
         new window.Tandem.Delta(d.startLength, d.endLength, d.ops)
+
+    # Create a delta from a Ruby hash
+    createDelta: (template) ->
+      startLength = template['startLength']
+      endLength = template['endLength']
+      ops = _.map(template['ops'], (op) ->
+        if op['value']?
+          return new window.Tandem.InsertOp(op['value'], op['attributes'])
+        else if op["start"]?
+          return new window.Tandem.RetainOp(op['start'], op['end'], op['attributes'])
+        else
+          throw "Receive op that is not insert or retain: #{JSON.stringify(op)}"
+      )
+      window.Fuzzer.currentDelta = new window.Tandem.Delta(startLength, endLength, ops)

     createRandomDelta: ->
       randomDelta = window.Tandem.DeltaGen.getRandomDelta(window.Fuzzer.docDelta, 1)


add check to verify fuzzer results

add check to verify fuzzer results

--- tests/scripts/fuzzer.coffee
+++ tests/scripts/fuzzer.coffee
@@ -42,11 +42,24 @@
     reader.applyDelta(delta)
   )

+  finishedFuzzing = _.after(100, ->
+    writerHtml = Tandem.Utils.cleanHtml(writer.doc.root.innerHTML)
+    readerHtml = Tandem.Utils.cleanHtml(reader.doc.root.innerHTML)
+    writerDelta = writer.getDelta()
+    readerDelta = reader.getDelta()
+
+    if writerHtml == readerHtml && _.isEqual(writerDelta, readerDelta)
+      console.log "Fuzzing passed"
+    else
+      console.error "Fuzzing failed", writer, reader
+  )
+
   _.each([1..100], ->
     _.defer( ->
       operation = getRandomOperation(writer)
       if operation?
         writer[operation.op].apply(writer, operation.args)
+      finishedFuzzing()
     )
   )
 )


Update show.js.coffee

Update show.js.coffee

--- app/views/knowledges/show.js.coffee
+++ app/views/knowledges/show.js.coffee
@@ -18,6 +18,9 @@
 		$('#content_text').html("<%= j(render @knowledge) %>");
 		$('#content_text').css({'left': '5%'}).animate({'left': '0%'}, 200);
 		# $('#content_area').css({'padding': '5% 5%'}).animate({'padding': '0% 15px'}, 200);
+		setTimeout (->
+			$('html, body').height($(document).height())), 500
+
 		init_wmd()
 		init_knowledge()
 		$('.knowledge').click ->


Groups: account/group always on and remove vm credit/debit operations are refactored

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -142,13 +142,10 @@
     {connection: delegate: account} = client
     {vmName, status} = options
     JDomain = require './domain'
-    JDomain.isDomainEligible {domain: vmName}, (err, webHome) =>
-      return callback err if err
-
-      selector =
-        hostnameAlias : vmName
-        webHome       : webHome
-      @one selector, (err, vm) ->
+    JDomain.isDomainEligible {domain: vmName, newDomain: no}, (err, webHome) =>
+      return callback err  if err
+
+      @one hostnameAlias: vmName, (err, vm) ->
         return callback err if err
         return callback new KodingError "VM Not FOUND" unless vm

@@ -157,9 +154,11 @@
         JGroup.one { _id: groupId }, (err, group)->
           return callback err  if err
           return callback new KodingError "Group not found"  unless group
-          group.fetchSubscription (err, subscription) ->
+          object = if group.slug is "koding" then account else group
+
+          object.fetchSubscription (err, subscription) ->
             return callback err  if err
-            return callback new KodingError "Group subscription not found"  unless subscription
+            return callback new KodingError("Subscription not found","NOTSUBSCRIBED")  unless subscription

             JPaymentPack.one tags: "alwayson", (err, pack) ->
               return callback err  if err
@@ -639,11 +638,8 @@
     JVM.removeRelatedDomains this
     super callback

-  credit: (group, callback)->
-    group.fetchSubscription (err, subscription)=>
-      return callback err  if err
-      return callback new KodingError 'Group subscription not found'  unless subscription
-
+  removeFromSubscription: (account, group, callback)->
+    kallback = (subscription) =>
       if subscription.status is 'canceled'
         @remove callback
       else
@@ -651,6 +647,17 @@
           return callback err  if err
           @remove callback

+    if group.slug is "koding"
+      account.fetchSubscription (err, subscription) =>
+        return callback err  if err
+        return @remove callback  unless subscription # so this is a free account
+        kallback subscription
+    else
+      group.fetchSubscription (err, subscription) =>
+        return callback err  if err
+        return callback new KodingError "Group subscription not found"  unless subscription
+        kallback subscription
+
   @removeByHostname = secure (client, hostnameAlias, callback)->
     {delegate} = client.connection

@@ -666,8 +673,8 @@
         JPermissionSet.checkPermission client, "delete vms", group, (err, hasPermission)->
           return callback err  if err
           return callback new KodingError "You do not have permission to delete this vm"  unless hasPermission
-          if vm.planCode is 'free' then vm.remove callback
-          else vm.credit group, callback
+          vm.removeFromSubscription delegate, group, callback
+

   fetchVmByHostname = (account, hostnameAlias, callback) ->
     account.fetchUser (err, user) =>


Create missing kd.io domain if it's missing.

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -114,6 +114,7 @@
             if not err and user
               user.fetchAccount 'koding', (err, account)=>
                 if not err and account
+                  @ensureDomainSettings {account, vm, type, nickname, groupSlug}
                   @createDomains account, hostnameAliases, hostnameAliases[0]

   @ensureDomainSettings = ({account, vm, type, nickname, groupSlug})->


Bugfixes.

Bugfixes.

--- emulator/nescoffee.coffee
+++ emulator/nescoffee.coffee
@@ -145,7 +145,7 @@
     setVideoDebug: (enabled = true) ->
         logger.info "Setting debugging video output to #{if enabled then 'on' else 'off'}"
         @videoDebug = enabled
-        @updateVideoRect()
+        @updateVideoRect() if @canvas

     isVideoDebug: ->
         @videoDebug
@@ -177,8 +177,9 @@
     setVideoScale: (scale = 1) ->
         logger.info "Setting video scale to #{scale}"
         @videoScale = scale
-        @updateVideoRect()
-        @drawFrame()
+        if @canvas
+            @updateVideoRect()
+            @drawFrame()

     setMaxVideoScale: ->
         @setVideoScale @getMaxVideoScale()


ctrl-z undo; ctrl-shift-z redo

ctrl-z undo; ctrl-shift-z redo

--- assets/coffeescripts/firepad.coffee
+++ assets/coffeescripts/firepad.coffee
@@ -8,8 +8,16 @@
     matchBrackets: true,
     mode: 'ruby'
   })
+  $(document).keypress((obj)->
+    if (obj.keyCode==26 && obj.ctrlKey ) || (obj.metaKey && obj.keyCode==122)
+      if(!obj.shiftKey)
+        codeMirror.undo()
+      else
+        codeMirror.redo()
+      return false
+  )

-  # Destroy the Firepad connection.
+# Destroy the Firepad connection.
   dispose_fp = () ->
     if firepad?
       firepad.dispose()
@@ -47,11 +55,11 @@
           # Build Firebase connection.
           url = 'https://mads.firebaseio.com/' + json['project'] + '/' + json['hash']
           firepadRef = new Firebase(url)
-          firepad = Firepad.fromCodeMirror(firepadRef, codeMirror, { userId: "" })
+          firepad = Firepad.fromCodeMirror(firepadRef, codeMirror, { userId: "algumacoisa" })

           # Create user list.
           firepadUserList = FirepadUserList.fromDiv(firepadRef.child('users'),
-            document.getElementById('userlist'), "")
+            document.getElementById('userlist'), "algumacoisa")

           # First time initialization; if the history
           # is empty (new file) load the files contents


feat(tap): automatically style buttons that direct back

--- modules/directives/tap/index.coffee
+++ modules/directives/tap/index.coffee
@@ -42,8 +42,11 @@
       element.attr 'bp-bound-margin', '5'
       options.boundMargin = 5
     # * Apply `bp-button-back` class if tap will result in reverse transition.
-    if /to\(('|")[A-Za-z]+('|"),true\)/.test attrs.bpTap
-      element.addClass 'bp-button-back'
+    if element.is 'bp-button'
+      toStateName = (attrs.bpTap.match /to\(('|")([A-Za-z]+)('|")/)?[2]
+      if toStateName and angular.isFunction scope.getDirection
+        dir = scope.getDirection to: toStateName
+        if dir is 'reverse' then element.addClass 'bp-button-back'

     touch = {}


Allow input completer to recover from promise failures

--- web/site/inputCompleter.coffee
+++ web/site/inputCompleter.coffee
@@ -35,7 +35,7 @@

         handle = (r) ->
           if r && typeof r.then == 'function'
-            r.then(handle)
+            r.then(handle, () -> sent = undefined)
           else
             sent = undefined
             if Array.isArray(r)


open nw dev tools on f12

open nw dev tools on f12

--- client/init.coffee
+++ client/init.coffee
@@ -1,7 +1,7 @@
 do ->
   if window.require?
     nww = require('nw.gui').Window.get()
-    $ -> # font size control in node-webkit
+    $ ->
       $(document)
         .on 'menu-select', '.m-zi', zi = -> nww.zoomLevel++;   false
         .on 'menu-select', '.m-zo', zo = -> nww.zoomLevel--;   false
@@ -9,6 +9,7 @@
         .on 'keydown', '*', 'ctrl+shift+= ctrl+=', zi
         .on 'keydown', '*', 'ctrl+-',              zo
         .on 'keydown', '*', 'ctrl+0',              zr
+        .on 'keydown', '*', 'f12', -> nww.showDevTools(); false
       return
   Dyalog.urlParams = {}
   for kv in (location + '').replace(/^[^\?]*($|\?)/, '').split '&'


Fix 特定のサイトが403を返すバグ

Fix 特定のサイトが403を返すバグ

６件以上のサイトを登録すると最後のサイトが403エラーを返す件について、healthExam
ineイベント内部でのfor文に起因していると目をつけたので、for文は外側で行うように
変更した。

--- src/healthExamine.coffee
+++ src/healthExamine.coffee
@@ -9,16 +9,17 @@

 module.exports = (robot) ->
   ### Satus Check Event ###
-  robot.on 'healthExamine', (sites, flags, _room="bot") ->
+  robot.on 'healthExamine', (site, flags, _room="bot") ->
     console.log(_room) #@@
-    for obj, key in sites
-      options = {
-        url: obj.url
-      }
-      request.get options, (err, res, body) ->
-        if err
-          robot.send {room: _room}, "Error: \"#{obj.url}\" Connection fail." if flags[0] is 1
-        else if res.statusCode is obj.status #想定値と異なる時のみ通知
-          robot.send {room: _room}, "SUCCESS: \"#{obj.url}\" has been alive :)" if flags[1] is 1
-        else if res.statusCode isnt obj.status #想定値と異なる時のみ通知
-          robot.send {room: _room}, "ERROR: \"#{obj.url}\" [expect]: #{obj.status}, [actual]: #{res.statusCode}" if flags[2] is 1+    console.dir site #@@
+    options = {
+      url: site.url
+    }
+    request.get options, (err, res, body) ->
+      if err
+        robot.send {room: _room}, "Error: \"#{site.url}\" Connection fail." if flags[0] is 1
+      else if res.statusCode is site.status #想定値と異なる時のみ通知
+        robot.send {room: _room}, "SUCCESS: \"#{site.url}\" has been alive :)" if flags[1] is 1
+      else if res.statusCode isnt site.status #想定値と異なる時のみ通知
+        console.log res #@@
+        robot.send {room: _room}, "ERROR: \"#{site.url}\" [expect]: #{site.status}, [actual]: #{res.statusCode}" if flags[2] is 1


Improve prompt text in scratch space

Improve prompt text in scratch space

--- client/tawk.coffee
+++ client/tawk.coffee
@@ -257,7 +257,7 @@
     if members.length
       SYNCAREA
         key_: server + '/group/' + gid
-        placeholder: 'This is your group scratch space'
+        placeholder: 'This is your group\'s shared text box. You can share links in here.'
         style:
           width: '100%'
           color: '#282c34'


fixed bug with tag notifications.

fixed bug with tag notifications.

--- server/Translator.coffee
+++ server/Translator.coffee
@@ -148,16 +148,6 @@
     reward = @tags[tag].weight / users.length
     tron.test( 'check_number', reward )

-    #Alert post author that a tag graduated on his post
-    Notifications.insert( {
-      user: @author_id
-      type: 2
-      post: @_id
-      points: reward
-      tag: tag
-      timestamp: new Date()
-    } )
-
     for user_id in users
       user_doc = Users.findOne( user_id )
       # add reward to user for tag
@@ -181,8 +171,8 @@

       #Alert taggers that their tag graduated
       Notifications.insert( {
-        user: @author_id
-        type: 1
+        user: user_id
+        type: unless user_id is @author_id then 1 else 2
         post: @_id
         points: reward
         tag: tag


Changed port

Changed port

--- server/bin/server.coffee
+++ server/bin/server.coffee
@@ -2,7 +2,7 @@
 app = require '../app.coffee'
 config = require '../config.json'

-port = Number config.port || 3000
+port = Number process.env.port || 5000

 server = app.listen port, ->
   debug 'Server running on port: ' + port


special rule for favicon.ico

special rule for favicon.ico

--- server/code/index.coffee
+++ server/code/index.coffee
@@ -17,6 +17,9 @@
 app.get '/', (req, resp) ->
   resp.render 'index', { scripts: js 'app' }

+# Avoids "Error: Cannot find module 'ico'"
+app.get '/favicon.ico', (req, resp) -> resp.send 404
+
 # TODO: sort out nice way of serving templates
 app.get '/:page', (req, resp) ->
   resp.render req.params.page


callback correction + typo

callback correction + typo

--- server/init/patch.coffee
+++ server/init/patch.coffee
@@ -31,6 +31,6 @@
                                         if err
                                                 log.info "#{konnector.slug} | #{err}"
                                         else
-                                                log.info "#{konnector.slug} | patching succeed"
+                                                log.info "#{konnector.slug} | patching succeeded"

-                callback
+                callback()


Fix issues with opacity gradients

Fix issues with opacity gradients

When there is opacity ≠ 1 specified, the gradient is drawn and masked on a "Tiling Pattern". The tiling has BBox, XStep & YStep values equal to the dimensions of the page, to avoid any clipping or tiling repetition. Patterns are unaffected by the "Current Transformation Matrix" so the clipping rectangle is always aligned with the page borders (SMasks and Groups don't have this property). Problems could possibly happen if a gradient is embedded on multiple pages (this doesn't work anyway) of different size.

This pull request resolves the following issues:
- Fix use of opacity gradients with fillAndStroke (#214)
- Fix the reuse of the same gradient after transformations
- Fix clipping of some gradients with opacity
- Add 'transform' function to the gradient object

--- lib/gradient.coffee
+++ lib/gradient.coffee
@@ -2,7 +2,7 @@
   constructor: (@doc) ->
     @stops = []
     @embedded = no
-    @transform = [1, 0, 0, 1, 0, 0]
+    @_transform = [1, 0, 0, 1, 0, 0]
     @_colorSpace = 'DeviceRGB'

   stop: (pos, color, opacity = 1) ->
@@ -10,9 +10,14 @@
     @stops.push [pos, @doc._normalizeColor(color), opacity]
     return this

-  embed: ->
-    return if @embedded or @stops.length is 0
+  transform: (m11, m12, m21, m22, dx, dy) ->
+    @_transform = [m11, m12, m21, m22, dx, dy]
+    return this
+
+  embed: (m) ->
+    return if @stops.length is 0
     @embedded = yes
+    @matrix = m

     # if the last stop comes before 100%, add a copy at 100%
     last = @stops[@stops.length - 1]
@@ -53,17 +58,6 @@

     @id = 'Sh' + (++@doc._gradCount)

-    # apply gradient transform to existing document ctm
-    m = @doc._ctm.slice()
-    [m0, m1, m2, m3, m4, m5] = m
-    [m11, m12, m21, m22, dx, dy] = @transform
-    m[0] = m0 * m11 + m2 * m12
-    m[1] = m1 * m11 + m3 * m12
-    m[2] = m0 * m21 + m2 * m22
-    m[3] = m1 * m21 + m3 * m22
-    m[4] = m0 * dx + m2 * dy + m4
-    m[5] = m1 * dx + m3 * dy + m5
-
     shader = @shader fn
     shader.end()

@@ -71,9 +65,8 @@
       Type: 'Pattern'
       PatternType: 2
       Shading: shader
-      Matrix: (+v.toFixed(5) for v in m)
+      Matrix: (+v.toFixed(5) for v in @matrix)

-    @doc.page.patterns[@id] = pattern
     pattern.end()

     if (@stops.some (stop) -> stop[2] < 1)
@@ -83,59 +76,70 @@
       for stop in @stops
         grad.stop stop[0], [stop[2]]

-      grad = grad.embed()
-
-      group = @doc.ref
-        Type: 'Group'
-        S: 'Transparency'
-        CS: 'DeviceGray'
-
-      group.end()
+      grad = grad.embed(@matrix)

-      resources = @doc.ref
-        ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI']
-        Shading:
-          Sh1: grad.data.Shading
-
-      resources.end()
+      pageBBox = [0, 0, @doc.page.width, @doc.page.height]

       form = @doc.ref
         Type: 'XObject'
         Subtype: 'Form'
         FormType: 1
-        BBox: [0, 0, @doc.page.width, @doc.page.height]
-        Group: group
-        Resources: resources
+        BBox: pageBBox
+        Group:
+          Type: 'Group'
+          S: 'Transparency'
+          CS: 'DeviceGray'
+        Resources:
+          ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI']
+          Pattern:
+            Sh1: grad

-      form.end "/Sh1 sh"
+      form.write "/Pattern cs /Sh1 scn"
+      form.end "#{pageBBox.join(" ")} re f"

-      sMask = @doc.ref
-        Type: 'Mask'
-        S: 'Luminosity'
-        G: form
-
-      sMask.end()
-
       gstate = @doc.ref
         Type: 'ExtGState'
-        SMask: sMask
+        SMask:
+          Type: 'Mask'
+          S: 'Luminosity'
+          G: form

-      @opacity_id = ++@doc._opacityCount
-      name = "Gs#{@opacity_id}"
+      gstate.end()

-      @doc.page.ext_gstates[name] = gstate
-      gstate.end()
-
+      opacityPattern = @doc.ref
+        Type: 'Pattern'
+        PatternType: 1
+        PaintType: 1
+        TilingType: 2
+        BBox: pageBBox
+        XStep: pageBBox[2]
+        YStep: pageBBox[3]
+        Resources:
+          ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI']
+          Pattern:
+            Sh1 : pattern
+          ExtGState:
+            Gs1 : gstate
+
+      opacityPattern.write "/Gs1 gs /Pattern cs /Sh1 scn"
+      opacityPattern.end "#{pageBBox.join(" ")} re f"
+
+      @doc.page.patterns[@id] = opacityPattern
+
+    else
+      @doc.page.patterns[@id] = pattern
+
     return pattern
-
+
   apply: (op) ->
-    @embed() unless @embedded
+    # apply gradient transform to existing document ctm
+    [m0, m1, m2, m3, m4, m5] = @doc._ctm.slice()
+    [m11, m12, m21, m22, dx, dy] = @_transform
+    m = [m0 * m11 + m2 * m12, m1 * m11 + m3 * m12, m0 * m21 + m2 * m22, m1 * m21 + m3 * m22, m0 * dx + m2 * dy + m4, m1 * dx + m3 * dy + m5]
+
+    @embed(m) unless @embedded and m.join(" ") is @matrix.join(" ")
     @doc.addContent "/#{@id} #{op}"
-
-    if @opacity_id
-      @doc.addContent "/Gs#{@opacity_id} gs"
-      @doc._sMasked = true
-
+
 class PDFLinearGradient extends PDFGradient
   constructor: (@doc, @x1, @y1, @x2, @y2) ->
     super
@@ -166,4 +170,4 @@
   opacityGradient: ->
     return new PDFRadialGradient(@doc, @x1, @y1, @r1, @x2, @y2, @r2)

-module.exports = {PDFGradient, PDFLinearGradient, PDFRadialGradient}+module.exports = {PDFGradient, PDFLinearGradient, PDFRadialGradient}


Remove console.log spam

Remove console.log spam

--- annotran/static/scripts/annotator/sidebar.coffee
+++ annotran/static/scripts/annotator/sidebar.coffee
@@ -203,7 +203,6 @@
       # convert this to a range in the current document and extract the start and end points
       range = new xpathRange.SerializedRange(packager).normalize(document.body)
       for rect in range.toRange().getClientRects()
-        console.log(rect)
         # In Chrome, for some reason, elements get selected here that occupy the entire viewport minus 12 pixels
         # so this check pushes rectangles that are not covered. We have no idea why this is the case.
         if rect.right < window.innerWidth - 12


update lightbox propTypes

update lightbox propTypes

--- app/assets/javascripts/components/light-box.cjsx
+++ app/assets/javascripts/components/light-box.cjsx
@@ -12,8 +12,8 @@
     onSubject: React.PropTypes.func.isRequired
     nextPage: React.PropTypes.func.isRequired
     prevPage: React.PropTypes.func.isRequired
-    totalSubjectPages: React.PropTypes.number.isRequired
-    subjectCurrentPage: React.PropTypes.number.isRequired
+    totalSubjectPages: React.PropTypes.number
+    subjectCurrentPage: React.PropTypes.number

   getInitialState:->


Add KeyboardManager to Exo.Views.Modal, close on esc

--- app/assets/javascripts/exo/views/modal.js.coffee
+++ app/assets/javascripts/exo/views/modal.js.coffee
@@ -13,6 +13,7 @@
     scrollable: true
     zIndex: 1024
     destroyOnHide: false
+    escToClose: true

     headerSelector:  "> .modal-header"
     sidebarSelect:   "> .modal-sidebar"
@@ -25,6 +26,8 @@

       # @todo
       @dimensions ||= { width: null, height: null }
+
+      @keyboardManager ||= new Exo.KeyboardManager(self)

     positionElement: ->
       headerHeight = @$header.outerHeight()
@@ -73,6 +76,12 @@

       this

+    handleKeyUp: (key, e) ->
+      switch key
+        when "esc"
+          @hide() if @escToClose
+      false
+
     getOverlay: ->
       @_overlay ||= $("

").css(zIndex: @zIndex - 1).appendTo(document.body)

@@ -92,6 +101,7 @@
       @getOverlay().show() if @modal
       @$el.show()
       @positionElement()
+      @keyboardManager.nominate(this)
       @trigger "show"

     hide: (e) ->
@@ -138,6 +148,10 @@
       @_overlay?.remove()
       super

+    destroy: ->
+      super
+      @keyboardManager.revoke(this)
+
     #
     # Private
     #


OX-343 Minor improvements

OX-343 Minor improvements

--- app/assets/javascripts/oxalis/model/route.coffee
+++ app/assets/javascripts/oxalis/model/route.coffee
@@ -132,7 +132,7 @@
         point = @branchStack.pop()
         @stateLogger.push()
         if point
-          @activeNode = point
+          @setActiveNode(point.id)
           @activeNode.type = TYPE_USUAL

           @trigger("setBranch", false, @activeNode.id)
@@ -145,7 +145,7 @@
       point = @branchStack.pop()
       @stateLogger.push()
       if point
-        @activeNode = point
+        @setActiveNode(point.id)
         @activeNode.type = TYPE_USUAL

         @trigger("setBranch", false, @activeNode.id)
@@ -238,7 +238,7 @@
     @trigger("newActiveNodeRadius", radius)

-  setActiveNode : (nodeID, mergeTree) ->
+  setActiveNode : (nodeID, mergeTree = false) ->
     lastActiveNode = @activeNode
     lastActiveTree = @activeTree
     for tree in @trees
@@ -348,7 +348,6 @@

     if deletedNode.neighbors.length > 1
       # Need to split tree
-      # TODO: Handle with stateLogger
       newTrees = []
       @trigger("removeSpheresOfTree", @activeTree.nodes.concat(deletedNode))
       oldActiveTreeId = @activeTree.treeId
@@ -360,7 +359,7 @@

         @activeTree.nodes = []
         @getNodeListForRoot(@activeTree.nodes, deletedNode.neighbors[i])
-        @activeNode = deletedNode.neighbors[i]
+        @setActiveNode(deletedNode.neighbors[i].id)
         newTrees.push(@activeTree)

         if @activeTree.treeId != oldActiveTreeId
@@ -373,7 +372,7 @@

     else if @activeNode.neighbors.length == 1
       # no children, so just remove it.
-      @activeNode = deletedNode.neighbors[0]
+      @setActiveNode(deletedNode.neighbors[0].id)
       @trigger("deleteActiveNode", deletedNode)
     else
       @deleteTree(false)


Bug fix: edit Position

Bug fix: edit Position

--- app/assets/javascripts/oxalis/model/route.coffee
+++ app/assets/javascripts/oxalis/model/route.coffee
@@ -126,7 +126,7 @@
     # Get Branchpoints
     for branchPoint in @branchStack
       result.branchPoints.push({id : branchPoint.id})
-    result.editPosition = @flycam.getGlobalPos
+    result.editPosition = @flycam.getGlobalPos()
     result.trees = []
     for tree in @trees
       # Don't save empty trees (id is null)


update weather

update weather

--- app/assets/javascripts/weather/weather.js.coffee
+++ app/assets/javascripts/weather/weather.js.coffee
@@ -78,8 +78,19 @@

 init_theme = (block) ->
-#  snowfall()
-  rainfall()
+  stopRainfall()
+  stopSnowfall()
+
+  amount = window.innerWidth * window.innerHeight / window.weather_def_del
+
+  if (block.rain && block.snow)
+    amount /= 2
+
+  if (block.rain)
+    rainfall(amount)
+
+  if (block.snow)
+    snowfall()

 window.wheater_proc = ->


Fully generalized all sequence combinators except lazyConcat().

--- lib/sequence.coffee
+++ lib/sequence.coffee
@@ -73,13 +73,6 @@
   seq["#{name}__"] = (s, args...) -> f.call(seq, s,      args...)
   Sequence::[name] = (args...)    -> f.call(seq, this,   args...)

-seq.oldCombinator = oldCombinator = (name, f) ->
-  seq[name] = (args...) ->
-    seq.fold seq.map(args, seq), (s, t) -> f.call(seq, s, t)
-
-  seq["#{name}__"] = (s, t)    -> f.call(seq, s, t)
-  Sequence::[name] = (args...) -> seq[name] this, args...
-
 seq.combinator = combinator = (name, f) ->
   namex = "#{name}__"
   seq[namex]       = (seqs)    -> f.call seq, seqs
@@ -179,29 +172,31 @@
     x = t?.first()
     seq.forall__ t?.rest(), (y) -> equal x, y

-oldCombinator 'lazyConcat', (s, next) ->
-  if s
-    @conj s.first(), => @lazyConcat__ s.rest(), next
-  else
-    next()
-
-oldCombinator 'concat', (s, t) ->
-  if s
-    @lazyConcat__ s, -> t
-  else
-    t
+seq.lazyConcat = (s, t) ->
+  if s
+    @conj s.first(), => @lazyConcat s.rest(), t
+  else
+    seq t()
+
+Sequence.lazyConcat = (t) -> seq.lazyConcat this, t
+
+combinator 'concat', (seqs) ->
+  if seqs
+    @lazyConcat seqs.first(), => @concat__ seqs.rest()
+  else
+    null

 combinator 'interleave', (seqs) ->
   live = seqs?.select (s) -> s?
   if live?
     firsts = live.map((s) -> s.first())
-    @lazyConcat__ firsts, => @interleave__ live.map (s) -> s.rest()
+    @lazyConcat firsts, => @interleave__ live.map (s) -> s.rest()
   else
     null

 method 'flatten', (s) ->
   if s and seq s.first()
-    @lazyConcat__ seq(s.first()), => @flatten__ s.rest()
+    @lazyConcat seq(s.first()), => @flatten__ s.rest()
   else if s?.rest()
     @flatten__ @dropWhile__ s.rest(), (x) -> not seq x
   else


prevent using old versions of anaconda python

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -271,41 +271,72 @@
     callback(result?)
   )

-pythonLocation = (callback, locations) ->
+pythonLocation = (callback) ->
   if global.cachedPythonLocation?
     callback(global.cachedPythonLocation)
-  else
-    if not locations?
-      locations = [
-        __dirname + path.sep + 'python' + path.sep + 'pythonw',
-        'python3',
-        'pythonw',
-        'python',
-        '/usr/local/bin/python3',
-        '/usr/local/bin/python',
-        '/usr/bin/python3',
-        '/usr/bin/python',
-      ]
-      i = 39
-      while i >= 27
-        locations.push '\\python' + i + '\\pythonw'
-        locations.push '\\Python' + i + '\\pythonw'
-        i--
-    args = ['--version']
-    if locations.length is 0
-      callback(null)
-      return
-    pattern = /\d+\.\d+/
-    location = locations[0]
-    child_process.execFile(location, args, (error, stdout, stderr) ->
-      if not error?
-        if stdout? and stdout.match(pattern) or stderr? and stderr.match(pattern)
-          global.cachedPythonLocation = location
-          callback(location)
-      else
-        locations.splice(0, 1)
-        pythonLocation(callback, locations)
-    )
+    return
+
+  locations = [
+    __dirname + path.sep + 'python' + path.sep + 'pythonw',
+    'python3',
+    'pythonw',
+    'python',
+    '/usr/local/bin/python3',
+    '/usr/local/bin/python',
+    '/usr/bin/python3',
+    '/usr/bin/python',
+  ]
+  i = 39
+  while i >= 27
+    if i < 30 or i > 32
+      locations.push '\\python' + i + '\\pythonw'
+      locations.push '\\Python' + i + '\\pythonw'
+    i--
+
+  findPython(locations, callback)
+
+findPython = (locations, callback) ->
+  if locations.length is 0
+    callback(null)
+    return
+
+  binary = locations.shift()
+  log.debug 'Looking for python at: ' + binary
+
+  args = ['--version']
+  child_process.execFile(binary, args, (error, stdout, stderr) ->
+    output = stdout.toString() + stderr.toString()
+    if not error and isSupportedPythonVersion(binary, output)
+      global.cachedPythonLocation = binary
+      log.debug 'Valid python version: ' + output
+      callback(binary)
+    else
+      log.debug 'Invalid python version: ' + output
+      findPython(locations, callback)
+  )
+
+isSupportedPythonVersion = (binary, versionString) ->
+  # Only support Python 2.7+ because 2.6 has SSL problems
+  if binary.toLowerCase().includes('python26')
+    return false
+
+  anaconda = /continuum|anaconda/gi
+  isAnaconda = not not anaconda.test(versionString)
+  re = /python\s+(\d+)\.(\d+)\.(\d+)\s/gi
+  ver = re.exec(versionString)
+  if not ver?
+    return isAnaconda
+
+  # Older Ananconda python distributions not supported
+  if isAnaconda
+    if parseInt(ver[1]) >= 3 and parseInt(ver[2]) >= 5
+      return true
+  else
+    # Only support Python 2.7+ because 2.6 has SSL problems
+    if parseInt(ver[1]) >= 2 or parseInt(ver[2]) >= 7
+      return true
+
+  return false

 installPython = (callback) ->
   pyVer = '3.5.2'


Style.

Style.

--- lib.coffee
+++ lib.coffee
@@ -530,6 +530,7 @@

         onRendered: ->
           # @ is a template instance.
+
           try
             # Same as for onCreated above.
             @component._componentInternals.inOnRendered = true
@@ -547,6 +548,8 @@

         onDestroyed: ->
           @autorun (computation) =>
+            # @ is a template instance.
+
             # We wait for all children components to be destroyed first.
             # See https://github.com/meteor/meteor/issues/4166
             return if @component.componentChildren().length
@@ -563,7 +566,6 @@
               @component._componentInternals.isDestroyed ?= new ReactiveField true
               @component._componentInternals.isDestroyed true

-              # @ is a template instance.
               componentOrMixin = null
               while componentOrMixin = @component.getFirstWith componentOrMixin, 'onDestroyed'
                 componentOrMixin.onDestroyed()


Add spec for D3Pie::renderPiePath

Add spec for D3Pie::renderPiePath

--- spec/chart-d3-pie-spec.coffee
+++ spec/chart-d3-pie-spec.coffee
@@ -21,6 +21,7 @@
       animateRotate: true
       animateScale: true
       onAnimationComplete: -> 'foo'
+    args.margin = top: 5, right: 5, bottom: 5, left: 5
     instance = new Chart.D3Pie('#svg', args.data, {})

   afterEach ->
@@ -42,16 +43,6 @@
   describe '::attrSegmentStroke', ->
     it 'pending'

-  describe '::renderPie', ->
-    it 'should return an array', ->
-      actual = instance.renderPie(args.data, args.options)
-      expect(actual).to.be.an(Array)
-
-  xdescribe '::renderPiePath', ->
-    it 'should return an array', ->
-      # actual = instance.renderPiePath()
-      expect(actual).to.be.an(Array)
-
   describe '::getOuterRadius', ->
     it 'should return a Number', ->
       actual = instance.getOuterRadius(450, 400.5, 5)
@@ -64,6 +55,22 @@

   describe '::render', ->
     it 'pending'
+
+  describe '::renderPie', ->
+    it 'should return an array', ->
+      actual = instance.renderPie(args.data, args.options)
+      expect(actual).to.be.an(Array)
+
+  describe '::renderPiePath', ->
+    it 'should return an array', ->
+      outerRadius = instance.getOuterRadius(
+        instance.width, instance.height, args.margin
+      )
+      innerRadius = instance.getInnerRadius(outerRadius, args.options)
+      arc = instance.getArc(innerRadius, outerRadius)
+      sl = instance.renderPie(args.data, args.options)
+      actual = instance.renderPiePath(sl, arc)
+      expect(actual).to.be.an(Array)

   describe '::setAnimationComplete', ->
     context 'when an argument is invalid', ->


Add logging for deletes

Add logging for deletes

Signed-off-by: Max Brunsfeld <78036c9b69b887700d5846f26a788d53b201ffbb@gmail.com>

--- spec/marker-index-spec.coffee
+++ spec/marker-index-spec.coffee
@@ -188,6 +188,7 @@
             markers.push({id, start, end})
           else
             [{id}] = markers.splice(random(markers.length - 1), 1)
+            # console.log "#{j}: delete(#{id})"
             markerIndex.delete(id)

           # console.log markerIndex.rootNode.toString()


Updated changelog

Updated changelog

--- .annotations.cson
+++ .annotations.cson
@@ -2,14 +2,11 @@
   {
     lineNumber: 20
     title: "Text"
-    content: "text annotation"
+    content: '''
+      text annotation2345
+
+    '''
     type: "text"
-  }
-  {
-    lineNumber: 20
-    title: "HTML"
-    content: "HTML annotation"
-    type: "html"
   }
   {
     lineNumber: 20
@@ -19,17 +16,17 @@
   }
   {
     lineNumber: 20
+    title: "project relative file URL"
+    content: '''
+      file://$code-annotations/class_diagram.png
+
+    '''
+    type: "link"
+  }
+  {
+    lineNumber: 20
     title: "URL"
     content: "https://atom.io/"
     type: "link"
   }
-  {
-    lineNumber: 20
-    title: "project relative file URL"
-    content: '''
-      file://$code-annotations/class_diagram.png
-
-    '''
-    type: "link"
-  }
 ]


fixed bug with multiple submit buttons in one form

--- lib/assets/javascripts/attachinary.js.coffee
+++ lib/assets/javascripts/attachinary.js.coffee
@@ -69,7 +69,9 @@

         @$input.prop 'disabled', true
         if @config.disableWith
-          @$submit.data 'old-val', @$submit.val()
+          @$submit.each (index,input) =>
+            $input = $(input)
+            $input.data 'old-val', $input.val()
           @$submit.val  @config.disableWith
           @$submit.prop 'disabled', true

@@ -91,7 +93,9 @@

         @checkMaximum()
         if @config.disableWith
-          @$submit.val  @$submit.data('old-val')
+          @$submit.each (index,input) =>
+            $input = $(input)
+            $input.val  $input.data('old-val')
           @$submit.prop 'disabled', false


Pass app root to pool cwd

Pass app root to pool cwd

--- src/pow/server.coffee
+++ src/pow/server.coffee
@@ -1,4 +1,5 @@
 http = require 'http'
+path = require 'path'
 sys  = require 'sys'

 {createPool}         = require 'nack/pool'
@@ -26,7 +27,8 @@
     @server.close()

   createApplicationPool: (config) ->
-    pool = createPool config, size: 3, idle: idle
+    root = path.dirname config
+    pool = createPool config, size: 3, idle: idle, cwd: root

     # TODO: Pump this to a file
     sys.pump pool.stdout, process.stdout


Fixed typo.

Fixed typo.

--- src/properties.coffee
+++ src/properties.coffee
@@ -3,14 +3,14 @@
 {Events} = require './events'
 {pairToObject, serializeObject, extend, map} = require './helpers'

-prexix = "_prop_"
+prefix = "_prop_"
 exp = /^_prop_/
 define = Object.defineProperties # we check for the plural because it is unsupported in buggy IE8

 Serenade.Properties =
   property: (name, options={}) ->
-    @[prexix + name] = options
-    @[prexix + name].name = name
+    @[prefix + name] = options
+    @[prefix + name].name = name
     if define
       Object.defineProperty @, name,
         get: -> Serenade.Properties.get.call(this, name)
@@ -40,8 +40,8 @@
       @_undefer(name)
       names.push(name)
       @attributes or= {}
-      if @[prexix + name]?.set
-        @[prexix + name].set.call(this, value)
+      if @[prefix + name]?.set
+        @[prefix + name].set.call(this, value)
       else
         @attributes[name] = value
       @_defer(name)
@@ -49,13 +49,13 @@

   get: (name) ->
     @attributes or= {}
-    if @[prexix + name]?.get
-      @[prexix + name].get.call(this)
+    if @[prefix + name]?.get
+      @[prefix + name].get.call(this)
     else
       @attributes[name]

   format: (name) ->
-    format = @[prexix + name]?.format
+    format = @[prefix + name]?.format
     if typeof(format) is 'string'
       Serenade._formats[format].call(this, @get(name))
     else if typeof(format) is 'function'


config: change kontroldeamons log level to notice

--- config/main.vagrant.coffee
+++ config/main.vagrant.coffee
@@ -366,7 +366,7 @@
     neo4jfeeder   : "notice"
     oskite        : "notice"
     kontrolproxy  : "notice"
-    kontroldaemon : "info"
+    kontroldaemon : "notice"
     userpresence  : "notice"
     vmproxy       : "notice"
     graphitefeeder: "notice"


remove pistachios: bugfixes

remove pistachios: bugfixes

--- client/Finder/filetree/itemviews/fileitem.coffee
+++ client/Finder/filetree/itemviews/fileitem.coffee
@@ -58,7 +58,9 @@
   mouseDown:-> yes

   viewAppended:->
-    super()
+    @setTemplate @pistachio()
+    @template.update()
+
     @hideLoader()
     @decorateItem()


client: redirect quetion marks to learn.koding.com

--- client/Main/avatararea/avatarareaiconmenu.coffee
+++ client/Main/avatararea/avatarareaiconmenu.coffee
@@ -12,9 +12,14 @@
         title    : 'Help'

     @helpIcon.click = (event)=>
-      KD.singletons.helpController.showHelp this
+      window.open "http://learn.koding.com"
       KD.utils.stopDOMEvent event
-      @animation?.destroy()
+
+      # We disabled this feature since '?' relies on vm to be up for
+      # certain items to show properly. SA
+      #
+      # KD.singletons.helpController.showHelp this
+      # @animation?.destroy()

     @notificationsPopup = new AvatarPopupNotifications
       cssClass : "notifications"


 Merge branch 'master.lastRoute' into 'master'

Master.Last Route

--- client/Main/maincontroller/maincontroller.coffee
+++ client/Main/maincontroller/maincontroller.coffee
@@ -157,14 +157,13 @@
         # window location path is set to last route to ensure visitor is not
         # redirected to another page
         @utils.defer ->
-          firstRoute = KD.getSingleton("router").visitedRoutes.first
-
-          firstRoute = KD.getSingleton("router").visitedRoutes.first
-          if firstRoute and /^\/(?:Reset|Register|Verify|Confirm)\//.test firstRoute
-            firstRoute = "/Activity"
+          lastRoute = KD.getSingleton("router").visitedRoutes.last
+
+          if lastRoute and /^\/(?:Reset|Register|Verify|Confirm)\//.test lastRoute
+            lastRoute = "/Activity"

           {entryPoint} = KD.config
-          KD.getSingleton('router').handleRoute firstRoute or '/Activity', {replaceState: yes, entryPoint}
+          KD.getSingleton('router').handleRoute lastRoute or '/Activity', {replaceState: yes, entryPoint}

   setVisitor:(visitor)-> @visitor = visitor
   getVisitor: -> @visitor


activity: define `MessageEventHelper::removeInteraction` method

--- client/Social/Activity/messageeventhelper.coffee
+++ client/Social/Activity/messageeventhelper.coffee
@@ -14,6 +14,16 @@
     return  unless message = @messages[messageId]

     fn = @bound "add#{typeConstant.capitalize()}"
+    KD.utils.defer => fn message, event
+
+
+  removeInteraction: (event) ->
+
+    {messageId, typeConstant} = event
+
+    return  unless message = @messages[messageId]
+
+    fn = @bound "remove#{typeConstant.capitalize()}"
     KD.utils.defer => fn message, event


Revert "Use right to complete."
Right is used to move.

This reverts commit 026675e379d9041a50fd5ff1b006c41bb8e1a17f.

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -30,7 +30,7 @@
             C.focusPrompt e

         isOverride = (e) ->
-            override   = ([9, 38, 39, 40].indexOf(e.keyCode) isnt -1)
+            override   = ([9, 38, 40].indexOf(e.keyCode) isnt -1)
             override or= ([13].indexOf(e.keyCode) isnt -1 and not e.shiftKey)
             override or= ([74, 75, 76, 82, 85].indexOf(e.keyCode) isnt -1 and e.ctrlKey)

@@ -44,8 +44,8 @@
             # tab
             else if e.keyCode is 9 and e.shiftKey
                 C.autoComplete.nav -1
-            # tab or right
-            else if (e.keyCode is 9 and not e.shiftKey) or (e.keyCode is 39)
+            # tab
+            else if e.keyCode is 9 and not e.shiftKey
                 C.autoComplete.nav +1
             # up
             else if e.keyCode is 38


Add try to prevent crash of ticker

Add try to prevent crash of ticker

--- views/components/main/parts/countdown-timer.cjsx
+++ views/components/main/parts/countdown-timer.cjsx
@@ -76,9 +76,12 @@
     @timeRemaining = @constructor.getTimeRemaining(@state.completeTime, currentTime)
     @stopTick() if @timeRemaining < 1
     if @state.completeTime >= 0
-      @textLabel.textContent = resolveTime @timeRemaining if @textLabel?
-      @props.tickCallback(@timeRemaining) if @props.tickCallback?
-      @props.completeCallback() if @timeRemaining < 1 and @props.completeCallback?
+      try
+        @textLabel.textContent = resolveTime @timeRemaining if @textLabel?
+        @props.tickCallback(@timeRemaining) if @props.tickCallback?
+        @props.completeCallback() if @timeRemaining < 1 and @props.completeCallback?
+      catch error
+        console.err(error)
     @timeRemaining--
   render: ->
      @textLabel = ref}>{resolveTime @timeRemaining}


Few formatting corrections

Few formatting corrections

--- client/app/lib/providers/snapshotlistitem.coffee
+++ client/app/lib/providers/snapshotlistitem.coffee
@@ -79,9 +79,9 @@
       callback : @bound 'confirmDeleteSnapshot'

     @addSubView @editView = new JView
-      cssClass: 'edit hidden'
-      pistachioParams: {@editInput, @editRenameBtn, @editCancelBtn}
-      pistachio: """
+      cssClass        : 'edit hidden'
+      pistachioParams : { @editInput, @editRenameBtn, @editCancelBtn }
+      pistachio       : """

           {{> editInput}}

@@ -92,9 +92,9 @@
         """

     @addSubView @infoView = new JView
-      cssClass: 'info'
-      pistachioParams: {@labelView, @infoRenameBtn, @infoDeleteBtn}
-      pistachio: """
+      cssClass        : 'info'
+      pistachioParams : { @labelView, @infoRenameBtn, @infoDeleteBtn }
+      pistachio       : """

           {{> labelView}}
           (#{data.storageSize}GB)
@@ -116,17 +116,17 @@
   confirmDeleteSnapshot: ->

     modal = kd.ModalView.confirm
-      title: 'Delete snapshot?'
-      ok:
-        title: 'Yes'
-        style: 'solid red medium'
-        callback: =>
+      title      : 'Delete snapshot?'
+      ok         :
+        title    : 'Yes'
+        style    : 'solid red medium'
+        callback : =>
           modal.destroy()
           @deleteSnapshot()
-      cancel:
-        style: 'solid light-grey medium'
-        type: 'button'
-        callback: -> modal.destroy()
+      cancel     :
+        style    : 'solid light-grey medium'
+        type     : 'button'
+        callback : -> modal.destroy()

   ###*


Edited isNaN check in fullpie.coffee

Edited isNaN check in fullpie.coffee

--- widgets/fullpie/fullpie.coffee
+++ widgets/fullpie/fullpie.coffee
@@ -48,9 +48,26 @@
     for val in data
       sum += val.value

+    arcs.append("svg:text").attr("transform", (d, i) ->
+      procent_val = Math.round(data[i].value/sum * 100)
+      d.innerRadius = (radius * (100-procent_val)/100) - 45  #45=max text size/2
+      d.outerRadius = radius
+      "translate(" + arc.centroid(d) + ")")
+      .attr('fill', "#fff")
+      .attr("text-anchor", "middle").text((d, i) -> data[i].label).attr('font-size', '28px')
+      .append('svg:tspan')
+      .attr('x', 0)
+      .attr('dy', 25)
+      .attr('font-size', '90%')
+      .text((d,i) -> data[i].value + ' (' + Math.round(data[i].value/sum * 100) + '%)')
+
 #    arcs.append("svg:text").attr("transform", (d, i) ->
-#      procent_val = Math.round(data[i].value/sum * 100)
-#      d.innerRadius = (radius * (100-procent_val)/100) - 45  #45=max text size/2
+#      percent_val = Math.round(data[i].value/sum * 100)
+#	  if .isNan(percent_val)
+#		percent_val = 0
+#	  if .isNan(data[i].value)
+#	    data[i].val = 0
+#      d.innerRadius = (radius * (100-percent_val)/100) - 45  #45=max text size/2
 #      d.outerRadius = radius
 #      "translate(" + arc.centroid(d) + ")")
 #      .attr('fill', "#fff")
@@ -59,21 +76,4 @@
 #      .attr('x', 0)
 #      .attr('dy', 25)
 #      .attr('font-size', '90%')
-#      .text((d,i) -> data[i].value + ' (' + Math.round(data[i].value/sum * 100) + '%)')
-
-    arcs.append("svg:text").attr("transform", (d, i) ->
-      percent_val = Math.round(data[i].value/sum * 100)
-	  if .isNan(percent_val)
-		percent_val = 0
-	  if .isNan(data[i].value)
-	    data[i].val = 0
-      d.innerRadius = (radius * (100-percent_val)/100) - 45  #45=max text size/2
-      d.outerRadius = radius
-      "translate(" + arc.centroid(d) + ")")
-      .attr('fill', "#fff")
-      .attr("text-anchor", "middle").text((d, i) -> data[i].label).attr('font-size', '28px')
-      .append('svg:tspan')
-      .attr('x', 0)
-      .attr('dy', 25)
-      .attr('font-size', '90%')
-      .text((d,i) -> data[i].value + ' (' + percent_val + '%)')
+#      .text((d,i) -> data[i].value + ' (' + percent_val + '%)')


Testpie testing

Testpie testing

--- widgets/testpie/testpie.coffee
+++ widgets/testpie/testpie.coffee
@@ -68,20 +68,12 @@
         ).attr('font-size', '28px')
         .append('svg:tspan')
         .attr('x', 0)
-        .attr('dy', 1em)
+        .attr('dy', 25)
         .attr('font-size', '90%')
         .text((d,i) ->
           if data[i].value != 0
-            data[i].value
-        )
-        .append('svg:tspan')
-        .attr('x', 0)
-        .attr('dy', 1em)
-        .attr('font-size', '70%')
-        .text((d,i) ->
-          if data[i].value != 0
-            '(' + Math.round(data[i].value/sum * 100) + '%)'
-        )
+            data[i].value + ' (' + Math.round(data[i].value/sum * 100) + '%)'
+		)

 # mod
 #	arcs.append("svg:text").attr("transform", (d, i) ->


Work on RequireJS Jasmine test.

Work on RequireJS Jasmine test.

--- lms/static/coffee/spec/requirejs_spec.coffee
+++ lms/static/coffee/spec/requirejs_spec.coffee
@@ -25,24 +25,27 @@

 describe "RequireJS module creation", ->
-  inCallback = undefined
-  it "check that we can use RequireJS.define() to create a module", ->
+  inDefineCallback = undefined
+  inRequireCallback = undefined
+  it "check that we can use RequireJS define() and require() a module", ->
     runs ->
-      inCallback = false
-      RequireJS.define [], ->
-        inCallback = true
+      inDefineCallback = false
+      inRequireCallback = false
+      RequireJS.define "test_module", [], ->
+        inDefineCallback = true
         module_status: "OK"
+
+      RequireJS.require "test_module", (test_module) ->
+        inRequireCallback = true
+        expects(test_module.module_status).toBe "OK"

     waitsFor (->
-      inCallback
+      return false  if (inDefineCallback isnt true) or (inRequireCallback isnt true)
+      true
     ), "We should eventually end up in the defined callback", 1000
     runs ->
-      expects(inCallback).toBeTruthy()
+      expects(inDefineCallback).toBeTruthy()
+      expects(inRequireCallback).toBeTruthy()

-
-
-# it('check that we can use RequireJS.require() to get our defined module', function () {
-
-# });


Be explicit about implicit returns in PlayState.

--- src/state-play.coffee
+++ src/state-play.coffee
@@ -48,6 +48,7 @@

       @_initDebugMixin
       @_initDebugDisplayMixin @game
+      return

     create: ->
       @physics.startSystem Physics.ARCADE
@@ -61,6 +62,7 @@
       @onHit.add (trigger) =>
         unless @quit trigger
           @inStateMenu.toggle()
+        return
       , @

       if @developing
@@ -69,8 +71,10 @@
           @background.debugging = @platforms.debugging = @player.debugging =
             @debugging
           @debugDisplay.reset() unless @debugging
+          return
         @gui.add(@, 'detachedCamera').onFinishChange =>
           @_toggleCameraAttachment()
+          return
         @gui.addOpenFolder('gravity').addRange @physics.arcade.gravity, 'y'

       # First:
@@ -84,6 +88,7 @@
       @_addInStateMenu()

       @_toggleCameraAttachment on
+      return

     update: ->
       @physics.arcade.collide @player.sprite, @platforms.layer
@@ -102,10 +107,12 @@
       @camera.updatePositionWithCursors @cursors if @detachedCamera

       @end() if @_isPlayerReadyToEnd()
+      return

     render: ->
       @_renderDebugDisplay() if @debugging
       @_renderDebugOverlays() if @debugging
+      return

     shutdown: ->
       # Null references to disposable objects we don't own.
@@ -122,12 +129,15 @@
       ]

       @gui?.destroy()
+      return

     onBlur: =>
       @music?.pause()
+      return

     onFocus: =>
       @music?.resume()
+      return

     # Main own methods
     # ----------------
@@ -145,12 +155,16 @@
       # Then render ending display.
       _.delay =>
         @_renderEndingDisplay()
+        return
       , 5 * Timer.SECOND
+      return

     quit: (trigger) ->
       return no unless @ended or trigger instanceof Key
       @inStateMenu.toggle off
-      _quit = => @state.startWithTransition null, 'menu', yes
+      _quit = =>
+        @state.startWithTransition null, 'menu', yes
+        return
       if @music.volume is 0
         _quit()
       else
@@ -158,6 +172,7 @@
         @music.fadeOut 3 * Timer.SECOND
         # Then go back to menu while clearing world.
         @music.onFadeComplete.addOnce _quit
+      return

     # Subroutines
     # -----------
@@ -167,6 +182,7 @@
       @background = new Background { parallaxTolerance }, @game
       @background.addImages _.template('bg<%= zIndex %>'), 16
       @background.layout()
+      return

     _addInStateMenu: ->
       @inStateMenu = new InStateMenu [
@@ -175,7 +191,10 @@
         ['Press 0, -, + for volume', { fontSize: fontSmall }]
         ['Press Q to quit', { fontSize: fontSmall }]
       ], @game,
-        pauseHandler: (paused) => @player.control = not paused
+        pauseHandler: (paused) =>
+          @player.control = not paused
+          return
+      return

     _addKey: (keyCode, callback) ->
       key = @input.keyboard.addKey keyCode
@@ -190,6 +209,7 @@
       @mate = @add.sprite x, y, 'mate', 1
       @mate.anchor = new Point 0.5, 0.5
       @mate.animations.add 'end', [4..MateLastFrame], 12
+      return

     _addMusic: ->
       @userVolume = 1
@@ -198,9 +218,10 @@
       @music.play()
       @gui?.add(@music, 'mute').listen()
       increment = 0.1
-      @loudKey = @_addKey Keyboard.EQUALS, => @userVolume += increment
-      @muteKey = @_addKey Keyboard.ZERO, => @music.mute = not @music.mute
-      @quietKey = @_addKey Keyboard.UNDERSCORE, => @userVolume -= increment
+      @loudKey = @_addKey Keyboard.EQUALS, => @userVolume += increment; return
+      @muteKey = @_addKey Keyboard.ZERO, => @music.mute = not @music.mute; return
+      @quietKey = @_addKey Keyboard.UNDERSCORE, => @userVolume -= increment; return
+      return

     _addPlatforms: ->
       @platforms = new Platforms
@@ -214,17 +235,20 @@
       )
       # Use for debugging ending.
       #@startingPoint = @platforms.ledges[-2...-1][0].createMidpoint @platforms
+      return

     _addPlayer: ->
       origin = @startingPoint
       @player = new Player(
         { origin }, @game, @cursors, @gui?.addOpenFolder 'player'
       )
+      return

     _addText: (text, style) ->
       _.defaults style, { fill: '#fff', font: 'Enriqueta' }
       text = @addCenteredText text, @textLayout, style
       tween = @fadeTo text, Timer.SECOND, 1
+      return

     _isPlayerReadyToEnd: ->
       (@player.state is 'still' and @player.control is on and
@@ -236,6 +260,7 @@
       if @player.debugging
         @renderDebugDisplayItems (layoutX, layoutY) =>
           @debugDisplay.bodyInfo @player.sprite, layoutX, layoutY
+          return
         , 6
         @renderDebugDisplayItems @player.debugTextItems

@@ -244,6 +269,7 @@
         @debugDisplay.body @player.sprite
         @debugDisplay.spriteBounds @player.sprite
         @debugDisplay.spriteBounds @player.cameraFocus
+      return

     _renderEndingDisplay: ->
       @textLayout = { y: 120, baseline: 40 }
@@ -252,6 +278,8 @@
         .onComplete.addOnce =>
           @_addText 'Click to play again', { fontSize: fontSmall }
           @ended = yes
+          return
+      return

     _shakeOnPlayerFall: ->
       return no unless (
@@ -259,6 +287,7 @@
         @player.distanceFallen() > shakeFallH
       )
       @camera.shake()
+      return

     _toggleCameraAttachment: (attached) ->
       attached ?= not @detachedCamera
@@ -273,6 +302,7 @@
       else
         @camera.unfollow()
         @player.cursors = null
+      return

     _updateVolumeOnPlayerLand: ->
       return no unless @player.nextState is 'landing'
@@ -282,6 +312,7 @@
       volume *= @math.clamp @userVolume, 0, 2 # Factor in user controls.
       volume = @math.clamp volume, 0.2, 0.8
       @music.fadeTo Timer.SECOND, volume
+      return

   _.extend( PlayState::,
     AnimationMixin, DebugMixin, DebugDisplayMixin, TextMixin


Delete symlink targets if deleting symlink

This is a minor thing to make #117 even simpler.

--- src/steps/move.coffee
+++ src/steps/move.coffee
@@ -88,7 +88,12 @@
     observers.push observer

 deleteOriginal = (data) ->
-  Rx.Observable.fromPromise del [data.source].concat(data.acfSource), force: yes
+  sources = [data.source].concat(data.acfSource)
+  # If data.source is a symlink, delete both the link and its target
+  realSource = fs.realpathSync data.source
+  if realSource isnt data.source
+    sources = sources.concat realSource
+  Rx.Observable.fromPromise del sources, force: yes

 module.exports =
   DUMMY_SIZE: DUMMY_SIZE


eventemitter fixed

eventemitter fixed

--- core/KDEventEmitter.coffee
+++ core/KDEventEmitter.coffee
@@ -12,21 +12,21 @@
   _on = (registry, eventName, callback)->
     # on can be defined before any emit, so create
     # the event registry, if it doesn't exist.
-    registry[event] ?= []
+    registry[eventName] ?= []
     # register the listeners callback.
-    registry[event].push callback
+    registry[eventName].push callback

   _off = (registry, eventName, callback)->
-    if event is "*"
+    if eventName is "*"
       registry = {}
     # reset the listener container so no event
     # will be propagated to previously registered
     # listener callbacks.
-    else if callback and registry[event]
-      cbIndex = registry[event].indexOf callback
-      registry[event].splice 1, cbIndex if cbIndex >= 0
+    else if callback and registry[eventName]
+      cbIndex = registry[eventName].indexOf callback
+      registry[eventName].splice 1, cbIndex if cbIndex >= 0
     else
-      registry[event] = []
+      registry[eventName] = []

   getEventParser = (event)->
@@ -44,44 +44,44 @@
   @emit : ->
     # slice the arguments, 1st argument is the event name,
     # rest is args supplied with emit.
-    [event, args...] = [].slice.call arguments
+    [eventName, args...] = [].slice.call arguments
     # create listener container if it doesn't exist
-    _e[event] ?= []
+    _e[eventName] ?= []
     # call every listener inside the container with the arguments (args)
-    listener.apply null,args for listener in _e[event] if _e[event]?
+    listener.apply null,args for listener in _e[eventName] if _e[eventName]?

-  @on   :(event,callback)->  _on _e, event, callback
-  @off  :(event, callback)-> _off _e, event, callback
+  @on   :(eventName, callback)-> _on _e, eventName, callback
+  @off  :(eventName, callback)-> _off _e, eventName, callback

   constructor:->
     @KDEventEmitterEvents  = {}
     @_e = @KDEventEmitterEvents[@constructor.name] = {}

-  emit:(event, args...)->
-    @_e[event] ?= []
+  emit:(eventName, args...)->
+    @_e[eventName] ?= []

     listenerStack = []

-    for own eventName of @_e
-      continue if eventName is event
-      parser = getEventParser eventName
-      if parser.test event
-        listenerStack = listenerStack.concat @_e[eventName].slice(0)
+    for own eventToBeFired of @_e
+      continue if eventToBeFired is eventName
+      parser = getEventParser eventToBeFired
+      if parser.test eventName
+        listenerStack = listenerStack.concat @_e[eventToBeFired].slice(0)

-    listenerStack = listenerStack.concat @_e[event].slice(0)
+    listenerStack = listenerStack.concat @_e[eventName].slice(0)

     listenerStack.forEach (listener)=>
       listener.apply @, args

-  on  :(event, callback) -> _on  @_e, event, callback
-  off :(event, callback) -> _off @_e, event, callback
-  unsubscribe:(event, callback) -> _off @_e, event, callback
+  on  :(eventName, callback) -> _on  @_e, eventName, callback
+  off :(eventName, callback) -> _off @_e, eventName, callback
+  unsubscribe:(eventName, callback) -> _off @_e, eventName, callback

-  once:(event, callback) ->
-    _callback = () =>
+  once:(eventName, callback) ->
+    _callback = =>
       args = [].slice.call arguments
-      @unsubscribe event, _callback
+      @unsubscribe eventName, _callback
       callback.apply @, args

-    @on event, _callback
+    @on eventName, _callback


Move count reset.

Move count reset.

--- keypress.coffee
+++ keypress.coffee
@@ -76,6 +76,7 @@
     if typeof combo["on_" + event] is "function"
         if event is "release"
             combo["on_" + event] combo.count
+            combo.count = 0
         else
             combo["on_" + event]()
     # We need to mark that keyup has already happened
@@ -286,7 +287,6 @@
     unless keys_remaining
         if combo.is_counting
             _fire "release", combo
-            combo.count = 0
         _remove_from_active_combos combo

     # We also need to check other combos that might still be in active_combos


Style tweak

Style tweak

--- keypress.coffee
+++ keypress.coffee
@@ -529,7 +529,7 @@
         if typeof combo_dictionary["keys"] is "string"
             combo_dictionary["keys"] = combo_dictionary["keys"].split " "
         for own property, value of @_defaults
-            if combo_dictionary[property] == undefined
+            if combo_dictionary[property] is undefined
                 combo_dictionary[property] = value
         combo = new Combo combo_dictionary


Kloud:askInfoFromClient: check klient of given machine, if exists ask for ping and wait pong up to 5 seconds

--- client/Main/kite/kites/kloud.coffee
+++ client/Main/kite/kites/kloud.coffee
@@ -36,6 +36,37 @@
     new Promise (resolve, reject) =>
       @requestingInfo[machineId] ?= []
       @requestingInfo[machineId].push { resolve, reject }
+
+
+  askInfoFromKlient: (machineId, callback) ->
+
+    {kontrol, computeController} = KD.singletons
+    {klient}   = kontrol.kites
+    machineUid = computeController.findUidFromMachineId machineId
+
+    if not klient? or not machineId?
+      return callback null
+
+    klientKite = klient[machineUid]
+
+    if not klientKite?
+      return callback null
+
+    klientKite.ping()
+
+      .then (res)->
+
+        if res is "pong"
+        then callback State: Machine.State.Running
+        else callback null
+
+      .timeout 5000
+
+      .catch ->
+
+        callback null
+
+
   askInfoFromKloud: (machineId, currentState) ->

     @tell 'info', { machineId }


app: do not listen AppIsBeingShown events within appcontrollers since there are multiple instances of same type of applications

--- client/app/lib/appcontroller.coffee
+++ client/app/lib/appcontroller.coffee
@@ -2,12 +2,6 @@
 KDViewController   = kd.ViewController
 getAppOptions      = require './util/getAppOptions'
 globals            = require 'globals'
-isarray            = require 'isarray'
-
-bants = globals.modules.reduce (acc, x) -> # todo: donot expose globals.modules
-  acc[x.name] = x
-  return acc
-, {}

 module.exports =

@@ -17,8 +11,8 @@

     super

+    { mainController } = kd.singletons
     { name, version } = @getOptions()
-    { mainController, appManager, shortcuts } = kd.singletons

     mainController.ready =>
       # defer should be removed
@@ -27,30 +21,14 @@
         { appStorageController } = kd.singletons
         @appStorage = appStorageController.storage name, version or "1.0.1"

-    @_active = false

-    appManager.on 'AppIsBeingShown', (app) =>
-      name = app.getOption('name').toLowerCase()
-      keys = bants[name].shortcuts
-      return  unless isarray keys
-
-      if app is this and not @_active
-        @_active = true
-        for key in bants[name].shortcuts
-          shortcuts.on "key:#{key}", @handleShortcut
-      else if app isnt this and @_active
-        @_active = false
-        for key in bants[name].shortcuts
-          shortcuts.removeListener "key:#{key}", @handleShortcut
-
-
-  handleShortcut: (e) ->
-    console.warn 'not implemented'
+  handleQuery:(query)->
+    @ready => @feedController?.handleQuery? query

   createContentDisplay: (models, callback)->
     console.warn 'not implemented'

-  handleQuery:(query)->
-    @ready => @feedController?.handleQuery? query
+  handleShortcut: (e) ->
+    console.warn 'not implemented'


account: #fetchDomains was listed twice as a shared method

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -139,7 +139,6 @@
         'fetchRelatedTagsFromGraph'
         'fetchRelatedUsersFromGraph'
         'fetchDomains'
-        'fetchDomains'
         'unlinkOauth'
         'changeUsername'
         'markUserAsExempt'


mp4: Throw error when AVC configuration version is not 1

--- mp4.coffee
+++ mp4.coffee
@@ -1546,6 +1546,8 @@

     # AVCDecoderConfigurationRecord
     @configurationVersion = bits.read_byte()
+    if @configurationVersion isnt 1
+      throw new "mp4: avcC: unknown configurationVersion: #{@configurationVersion}"
     @AVCProfileIndication = bits.read_byte()
     @profileCompatibility = bits.read_byte()
     @AVCLevelIndication = bits.read_byte()


terminal: webTermView => terminalView

terminal: webTermView => terminalView

--- client/finder/filetree/controllers/findertreecontroller.coffee
+++ client/finder/filetree/controllers/findertreecontroller.coffee
@@ -454,9 +454,9 @@
   openTerminalFromHere: (nodeView) ->
     @appManager.open "Terminal", (appInstance) =>
       path          = nodeView.getData().path
-      {webTermView} = @appManager.getFrontApp().getView().tabView.getActivePane().getOptions()
-
-      webTermView.on "WebTermConnected", (server) =>
+      {terminalView} = @appManager.getFrontApp().getView().tabView.getActivePane().getOptions()
+
+      terminalView.on "WebTermConnected", (server) =>
         server.input "cd #{path}\n"

   ###


Fixes #623.

Fixes #623.

Removes 'Pull using rebase' command from the overhead menu

--- menus/git-plus.cson
+++ menus/git-plus.cson
@@ -87,10 +87,6 @@
           ]
         },
         {
-          'label': 'Pull Using Rebase'
-          'command': 'git-plus:pull-using-rebase'
-        },
-        {
           'label': 'Rebase'
           'command': 'git-plus:rebase'
         },