fixed typo

fixed typo

--- async/Sequence.coffee
+++ async/Sequence.coffee
@@ -143,7 +143,7 @@
             for argName in paramList
                 temp.push context[argName]
                 if DEBUG and not context[argName]?
-                    console.warn "JSUtils.Sequence::_createParamListFromContext: Tryed to create argument '#{argName}' but context does not contain such a key. Maybe there is something wrong (the function arguments don't match the context). Given function:", func, "Given context:", context
+                    console.warn "JSUtils.Sequence::_createParamListFromContext: Tried to create argument '#{argName}' but context does not contain such a key. Maybe there is something wrong (the function arguments don't match the context). Given function:", func, "Given context:", context
             return temp
         return context


bumped version

bumped version

--- lib/helper.coffee
+++ lib/helper.coffee
@@ -1,3 +1,3 @@
 @Helper =
   extractTags: (appDef) -> appDef?.match(/#\s?tags:\s?([\w \d]+)\n/)?[1]?.split(' ').filter (x) -> x != null && x.length > 0
-  appVersion: -> '4.1.2'
+  appVersion: -> '4.2.0-dev'


kernel: onShellMessage parses plain JMP messages

--- lib/kernel.coffee
+++ lib/kernel.coffee
@@ -199,40 +199,58 @@
         @watchCallbacks.push(watchCallback)

     onShellMessage: (message) ->
-        message.type = message.header.msg_type
-
         console.log "shell message:", message

-        if _.has(message, ['parent_header', 'msg_id'])
-            callback = @executionCallbacks[message.parent_header.msg_id]
-        if callback? and _.has(message, ['content', 'status'])
-
-            if message.content.status == 'ok'
-                if message.type == 'complete_reply'
-                    matches = message.content.matches
-                    # matches = _.map matches, (match) -> {text: match}
-                    callback(matches)
-                else if message.type == 'inspect_reply'
-                    callback {
-                        data: message.content.data
-                        found: message.content.found
-                    }
-                else
-                    callback {
-                        data: 'ok'
-                        type: 'text'
-                        stream: 'status'
-                    }
-
-            else if message.content.status == 'error'
-                errorString = message.content.ename
-                if message.content.evalue.length > 0
-                    errorString = errorString + "\n" + message.content.evalue
-                callback {
-                    data: errorString
-                    type: 'text'
-                    stream: 'error'
-                }
+        msg_id = message.parent_header?.msg_id
+        if msg_id?
+            callback = @executionCallbacks[msg_id]
+
+        unless callback?
+            return
+
+        unless message.content?
+            console.log "onShellMessage: Missing message.content"
+            return
+
+        status = message.content.status
+        if status is 'error'
+            errorLines = []
+
+            ename = message.content.ename
+            if ename?
+                errorLines.push ename
+
+            evalue = message.content.evalue
+            if evalue?.length
+                errorLines = errorLines.concat evalue
+
+            callback
+                data:   errorLines.join '\n'
+                type:   'text'
+                stream: 'error'
+
+        else if status is 'ok'
+            msg_type = message.header?.msg_type
+
+            if msg_type is 'execution_reply'
+                callback
+                    data:   'ok'
+                    type:   'text'
+                    stream: 'status'
+
+            else if msg_type is 'complete_reply'
+                callback message.content.matches
+
+            else if msg_type is 'inspect_reply'
+                callback
+                    data:  message.content.data
+                    found: message.content.found
+
+            else
+                callback
+                    data:   'ok'
+                    type:   'text'
+                    stream: 'status'

     onIOMessage: (message) ->


Code enabling hook_main_connect_handler_ was missing from last commit.

--- lib/malifi.coffee
+++ lib/malifi.coffee
@@ -108,7 +108,10 @@
       else
         next(new Error('meta.actions_ is not defined.'))

-  return malifiConnectHandler
+  return if options.hook_main_connect_handler_
+    options.hook_main_connect_handler_(malifiConnectHandler)
+  else
+    malifiConnectHandler

 exports = module.exports = malifi


don't move if confirmed empty

don't move if confirmed empty

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -909,6 +909,7 @@
     if (charsMax > 1)
       options =
         autoConfirmTimeout: @getConfig("findConfirmByTimeout")
+        onConfirm: (@input) => if @input then @processOperation() else @cancelOperation()
         onChange: (@preConfirmedChars) => @highlightTextInCursorRows(@preConfirmedChars, "pre-confirm")
         onCancel: =>
           @vimState.highlightFind.clearMarkers()


Make component Ready state handling more robust

--- src/lib/NoFlo.coffee
+++ src/lib/NoFlo.coffee
@@ -76,9 +76,7 @@
                 port: port

             unless process.component.inPorts and process.component.inPorts[port]
-                throw new Error "No inport '#{port}' defined in process #{process.id}" if process.component.isReady()
-                process.component.once "ready", =>
-                    @connectPort socket, process, port, inbound
+                throw new Error "No inport '#{port}' defined in process #{process.id}"
                 return

             return process.component.inPorts[port].attach socket
@@ -88,9 +86,7 @@
             port: port

         unless process.component.outPorts and process.component.outPorts[port]
-            throw new Error "No outport '#{port}' defined in process #{process.id}" if process.component.isReady()
-            process.component.once "ready", =>
-                @connectPort socket, process, port, inbound
+            throw new Error "No outport '#{port}' defined in process #{process.id}"
             return

         process.component.outPorts[port].attach socket
@@ -118,9 +114,17 @@
         from = @getNode edge.from.node
         unless from
             throw new Error "No process defined for outbound node #{edge.from.node}"
+        unless from.component.isReady()
+            from.component.once "ready", =>
+                @addEdge edge
+            return
         to = @getNode edge.to.node
         unless to
             throw new Error "No process defined for inbound node #{edge.to.node}"
+        unless to.component.isReady()
+            to.component.once "ready", =>
+                @addEdge edge
+            return

         @connectPort socket, from, edge.from.port, false
         @connectPort socket, to, edge.to.port, true
@@ -144,18 +148,16 @@
         to = @getNode initializer.to.node
         unless to
             throw new Error "No process defined for inbound node #{initializer.to.node}"
+
+        unless to.component.isReady() or to.component.inPorts[initializer.to.port]
+            to.component.once "ready", =>
+                @addInitial initializer
+            return
+
         @connectPort socket, to, initializer.to.port, true
-        @sendInitial socket, to.component, initializer.to.port, initializer.from.data
         @connections.push socket
-
-    sendInitial: (socket, component, port, data) ->
-        unless component.isReady()
-            unless component.inPorts[port] and component.inPorts[port].isAttached()
-                component.once "ready", =>
-                    @sendInitial socket, component, port, data
-                return
         socket.connect()
-        socket.send data
+        socket.send initializer.from.data
         socket.disconnect()

 exports.createNetwork = (graph, debug = false) ->


Refactoring on number.coffee

Refactoring on number.coffee

--- lib/number.coffee
+++ lib/number.coffee
@@ -55,6 +55,48 @@

 asNum = (n) ->
   if Math.abs(n) < BASE then new CheckedInt n else LongInt.fromNative n
+
+# ----
+
+# The `num` function provides the public interface.
+
+num = (n = 0) ->
+  switch typeof n
+    when 'number'
+      num.fromNative n
+    when 'string'
+      num.parse n
+    else
+      if n instanceof NumberBase
+        n
+      else
+        throw new Error "expected a number, got #{n}"
+
+num.fromNative = (n) ->
+  throw new Error "expected an integer, got #{n}" unless n == Math.floor n
+  asNum n
+
+num.parse = (n) ->
+  unless /^[+-]?\d+$/.test n
+    throw new Error "expected an integer literal, got '#{n}'"
+
+  [s, m] = switch n[0]
+    when '-' then [-1, n[1..]]
+    when '+' then [ 1, n[1..]]
+    else          [ 1, n]
+
+  if m.length <= BASE_LENGTH
+    new CheckedInt parseInt n
+  else
+    parsed = (to) ->
+      if to > 0
+        from = Math.max 0, to - BASE_LENGTH
+        seq.conj parseInt(m[from...to]), -> parsed from
+      else
+        null
+
+    new LongInt s, parsed m.length
+

 # ----

@@ -110,23 +152,30 @@
       x

   operator = (name, f) ->
-    NumberBase[name]   = (args...) -> f.call NumberBase, args...
-    NumberBase::[name] = (args...) -> NumberBase[name] this, args...
-
-  for name in ['cmp', 'plus', 'minus', 'times', 'div', 'mod']
+    num[name]          = (args...) -> f.call num, args...
+    NumberBase::[name] = (args...) -> num[name] this, args...
+
+  for name in ['cmp', 'plus', 'minus', 'times', 'div', 'mod', 'gcd']
     do (name) ->
       namex = "#{name}__"
       operator name, (a, b) ->
         [x, y] = upcast a, b
-        @downcast x[namex] y
-
-  gcd: (other) ->
+        NumberBase.downcast x[namex] y
+
+  gcd__: (other) ->
     step = (a, b) -> if b.sgn() > 0 then -> step b, a.mod(b) else a

-    [a, b] = [@abs(), makeNum(other).abs()]
-    if a > b then trampoline step a, b else trampoline step b, a
-
-  pow: (other) ->
+    [x, y] = [@abs(), other.abs()]
+    if x.cmp(y) > 0 then trampoline step x, y else trampoline step x, y
+
+  for name in ['neg', 'abs', 'sgn']
+    do (name) ->
+      namex = "#{name}__"
+      operator name, (a) -> makeNum(a)[namex]()
+
+  operator 'sqrt', (a) -> NumberBase.downcast makeNum(a)['sqrt__']()
+
+  operator 'pow', (a, b) ->
     step = (p, r, s) ->
       if s.sgn() > 0
         if s.mod(2).sgn() > 0
@@ -136,7 +185,7 @@
       else
         p

-    trampoline step makeNum(1), this, makeNum other
+    NumberBase.downcast trampoline step makeNum(1), makeNum(a), makeNum(b)

 # ----

@@ -158,13 +207,13 @@

   constructor: (@val = 0) ->

-  neg: -> new CheckedInt -@val
-
-  abs: -> new CheckedInt Math.abs @val
-
-  sgn: -> if @val < 0 then -1 else if @val > 0 then 1 else 0
-
-  sqrt: -> new CheckedInt Math.floor Math.sqrt @val
+  neg__: -> new CheckedInt -@val
+
+  abs__: -> new CheckedInt Math.abs @val
+
+  sgn__: -> if @val < 0 then -1 else if @val > 0 then 1 else 0
+
+  sqrt__: -> new CheckedInt Math.floor Math.sqrt @val

   cmp__: (other) ->
     if @val < other.val then -1 else if @val > other.val then 1 else 0
@@ -193,11 +242,11 @@
 class LongInt extends NumberBase
   constructor: (sign, @digits) -> @sign = if @digits? then sign else 0

-  neg: -> new LongInt -@sign, @digits
-
-  abs: -> new LongInt 1, @digits
-
-  sgn: -> @sign
+  neg__: -> new LongInt -@sign, @digits
+
+  abs__: -> new LongInt 1, @digits
+
+  sgn__: -> @sign

   zeroes = BASE.toString()[1..]

@@ -221,11 +270,11 @@
       if cmp(r, rn) then -> step(rn) else rn
     trampoline step s.take n >> 1

-  sqrt: ->
+  sqrt__: ->
     if @sign == 0
       asNum 0
     else if @sign > 0
-      NumberBase.downcast new LongInt 1, sqrt @digits
+      new LongInt 1, sqrt @digits
     else
       throw new Error "expected a non-negative number, got #{this}"

@@ -373,52 +422,10 @@
       new LongInt 0, null

 # ----
-
-# The `number` function provides the public interface.
-
-number = (n = 0) ->
-  switch typeof n
-    when 'number'
-      number.fromNative n
-    when 'string'
-      number.parse n
-    else
-      if n instanceof NumberBase
-        n
-      else
-        throw new Error "expected a number, got #{n}"
-
-number.fromNative = (n) ->
-  throw new Error "expected an integer, got #{n}" unless n == Math.floor n
-  asNum n
-
-number.parse = (n) ->
-  unless /^[+-]?\d+$/.test n
-    throw new Error "expected an integer literal, got '#{n}'"
-
-  [s, m] = switch n[0]
-    when '-' then [-1, n[1..]]
-    when '+' then [ 1, n[1..]]
-    else          [ 1, n]
-
-  if m.length <= BASE_LENGTH
-    new CheckedInt parseInt n
-  else
-    parsed = (to) ->
-      if to > 0
-        from = Math.max 0, to - BASE_LENGTH
-        seq.conj parseInt(m[from...to]), -> parsed from
-      else
-        null
-
-    new LongInt s, parsed m.length
-
-
-# ----
 # Exporting.

 exports ?= this.pazy ?= {}
-exports.number = number
+exports.num = num

 # ----

@@ -450,11 +457,11 @@
   show -> undefined

   log ''
-  show -> number(98).gcd 21
-  show -> number(77777).gcd 21
-
-  log ''
-  show -> a = number Math.pow 2, 13
+  show -> num(98).gcd 21
+  show -> num(77777).gcd 21
+
+  log ''
+  show -> a = num Math.pow 2, 13
   show -> LongInt.fromNative a.val
   show -> a.plus 2
   show -> a.times 1
@@ -462,37 +469,43 @@
   show -> a.plus 2000

   log ''
-  show -> number -123456789000000
-  show -> number '-1234'
-  show -> number '-123456789000000'
-
-  log ''
-  show -> number(123456789).plus  876543211
-  show -> number(123456789).minus 123450000
-  show -> number(123456789).minus 123456790
-  show -> number(123456789).minus 123456789
-  show -> number(123456789).plus -123450000
-
-  log ''
-  show -> number(12345).times 100001
-  show -> number(11111).times 9
-  show -> number(111).div 37
-  show -> number(111111).div 37
-  show -> number(111111111).div 37
-  show -> number(111111111).div 12345679
-  show -> number(99980001).div 49990001
-  show -> number(20001).div 10001
-
-  log ''
-  show -> number(111).mod 37
-  show -> number(111112).mod 37
-  show -> number(111111111).mod 12345679
-
-  log ''
-  show -> number(9801).sqrt()
-  show -> number(998001).sqrt()
-  show -> number(99980001).sqrt()
-
-  log ''
-  show -> number(10).pow 6
-  show -> number(2).pow 16
+  show -> num -123456789000000
+  show -> num '-1234'
+  show -> num '-123456789000000'
+
+  log ''
+  show -> num(123456789).plus  876543211
+  show -> num(123456789).minus 123450000
+  show -> num(123456789).minus 123456790
+  show -> num(123456789).minus 123456789
+  show -> num(123456789).plus -123450000
+
+  log ''
+  show -> num(12345).times 100001
+  show -> num(11111).times 9
+  show -> num(111).div 37
+  show -> num(111111).div 37
+  show -> num(111111111).div 37
+  show -> num(111111111).div 12345679
+  show -> num(99980001).div 49990001
+  show -> num(20001).div 10001
+
+  log ''
+  show -> num(111).mod 37
+  show -> num(111112).mod 37
+  show -> num(111111111).mod 12345679
+
+  log ''
+  show -> num(9801).sqrt()
+  show -> num(998001).sqrt()
+  show -> num(99980001).sqrt()
+
+  log ''
+  show -> num(10).pow 6
+  show -> num(2).pow 16
+
+  log ''
+  show -> num.plus 123456789, 876543211
+  show -> num.sqrt 99980001
+  show -> num.pow 2, 16
+  show -> num.abs -12345


Fix template loading and throw error when template not found

--- src/marbles/view.js.coffee
+++ src/marbles/view.js.coffee
@@ -10,10 +10,11 @@

   # Marbles assumes you have pre-compiled hogan templates
   # TODO: Should be able to use any templating library
-  @templates: window.HoganTemplates
   @getTemplate: (template_path) ->
-    throw new Error("Marbles.View.templates is not defined. Expected an object conatining compiled hogan (mustache) templates.") unless @templates
-    @templates[template_path]
+    throw new Error("Marbles.View.templates is not defined. Expected an object conatining compiled hogan (mustache) templates.") unless (@templates ?= window.HoganTemplates)
+    template = @templates[template_path]
+    throw new Error("Marbles.View.templates[#{template_path}] is not defined.") unless template
+    template

   @detach: (cid) ->
     delete @instances.all[cid]


Fix type inspection

Fix type inspection

Because the editor was not saved before inspecting types, frequently Error(0) messages were shown, as the editor content and the idris representation would differ.

--- lib/idris-controller.coffee
+++ lib/idris-controller.coffee
@@ -155,6 +155,7 @@

   getTypeForWord: ({ target }) =>
     editor = @getEditor()
+    @saveFile editor
     uri = editor.getURI()
     word = Symbol.serializeWord @getWordUnderCursor(editor)


Add class mentions-kinder to all editables

--- src/mentions-kinder.coffee
+++ src/mentions-kinder.coffee
@@ -304,7 +304,7 @@
   # requires @$originalInput
   _setupElements: ->
     @$wrap = $('

')
-    @$editable = $('

')
+    @$editable = $('

')
     @$editable.addClass("mentions-kinder-#{if @multiline then 'multiline' else 'singleline'}")
     @$input = $("<input type='hidden'/>")
     @$input.attr('name', @$originalInput.attr('name'))


Revert "Remove the needless br-tags which are created on paste operations"

This reverts commit ed0c487d2684af1eb6e77b7fd24e8e614e2ee99b.

--- src/mentions-kinder.coffee
+++ src/mentions-kinder.coffee
@@ -349,8 +349,6 @@
       # clean all children and replace node with them
       if node.childNodes?.length > 0
         @_cleanChildNodes(node)
-        if node.childNodes[node.childNodes.length - 1].nodeName.toUpperCase() == 'BR'
-          $(node.childNodes.item(node.childNodes.length - 1)).remove()
       # remove node
       else
         $(node).remove()


ignore stdout and stderr from spawned interpreter

--- proxy.coffee
+++ proxy.coffee
@@ -180,7 +180,7 @@
           log "listening for connections from spawned interpreter on #{hp}"
           exe = process.env.DYALOG_IDE_INTERPRETER_EXE || if process.platform == 'darwin' then '../Dyalog/mapl' else 'dyalog'
           log "spawning interpreter #{JSON.stringify exe}"
-          child = spawn exe, ['+s', '-q'], env: extend process.env, RIDE_CONNECT: hp, RIDE_INIT: "CONNECT:#{hp}"
+          child = spawn exe, ['+s', '-q'], stdio: ['pipe', 'ignore', 'ignore'], env: extend process.env, RIDE_CONNECT: hp, RIDE_INIT: "CONNECT:#{hp}"
           toBrowser '*spawned', pid: child.pid
           child.on 'error', (err) ->
             server?.close(); server = client = null; toBrowser '*spawnedError', {message: '' + err, code: err.code}; child = null; return


Removed backlog refactoring story; move to TODO comment.

--- core/app/backbone/models/wheel.coffee
+++ core/app/backbone/models/wheel.coffee
@@ -1,3 +1,10 @@
+#TODO: refactoring of this and related files.
+# - don't use multiple is_user_opinion(s), but one attribute user_opinion
+# - make unset state explicit.
+# - simplify rest api (no need for delete!)
+# - try to get rid of userOpinionWithS (facts_new_view)
+# - pursue these changes into related code.
+
 class window.Wheel extends Backbone.Model
   defaults:
     opinion_types: {}


Add a description for each package

Add a description for each package

--- package.cson
+++ package.cson
@@ -1 +1,2 @@
-'main': 'lib/status-bar-view'
+'main': './lib/status-bar-view'
+'description': 'Display information about the current editor such as cursor position, file path, grammar, current branch, and line diff count.'


Bugfix: #142 Prevent click on flows between other organisations respectively other media Fixed code warning and commented out code

--- packages/custom/transparency/coffee/public/directives/sankeyDirective.coffee
+++ packages/custom/transparency/coffee/public/directives/sankeyDirective.coffee
@@ -43,7 +43,7 @@

-        updateDiagram = (oldValue,newValue)->
+        updateDiagram = ()->
             svg.selectAll("g").remove()
             height = Math.max 500, $scope.data.nodes.length*20
             h = height + margin.top + margin.bottom
@@ -78,7 +78,7 @@
                     d.source.name = gettextCatalog.getString(d.source.name)
                 else if d.target.name is 'Other media'
                     d.target.name = gettextCatalog.getString(d.target.name)
-                if (d.source.type is "o" and d.target.type is "m")
+                if (d.source.type is "o" and d.target.type is "m" and d.source.name isnt gettextCatalog.getString("Other organisations") and d.target.name isnt gettextCatalog.getString("Other media"))
                     div.html("""#{d.source.name} (#{formatNumber((d.value/d.source.value)*100)}%) → #{d.target.name} (#{formatNumber((d.value/d.target.value)*100)}%)
#{(formatNumber(d.value))} (§#{d.type})

 #{gettextCatalog.getString('Click for Details')}

                          """)
@@ -89,16 +89,17 @@
                     .style("left", (d3.event.pageX) + "px")
                     .style("top", (d3.event.pageY - 28) + "px")

-            .on "mouseout", (d) ->
+            .on "mouseout", () ->
                 div.transition()
                 .duration(500)
                 .style("opacity", 0)
             .on "click", (d) ->
-                $scope.linkClick()(d)
+                if d.source.name isnt gettextCatalog.getString("Other organisations") and d.target.name isnt gettextCatalog.getString("Other media")
+                    $scope.linkClick()(d)

             #make sure that all labels are invisible once the page is left
-            $rootScope.$on '$stateChangeStart', (stateEvent,toState,toParams,fromState,fromParams) ->
+            $rootScope.$on '$stateChangeStart', () ->
                 div.style("opacity",0)

@@ -137,7 +138,7 @@
                          """)
                 .style("left", (d3.event.pageX) + "px")
                 .style("top", (d3.event.pageY - 28) + "px")
-            .on "mouseout", (d) ->
+            .on "mouseout", () ->
                 div.transition()
                 .duration(500)
                 .style("opacity", 0)
@@ -166,26 +167,13 @@
             .attr("x", 6 + sankey.nodeWidth())
             .attr("text-anchor", "start");

-            dragmove = (d) ->
-                d3.select(this)
-                .attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")")
-                sankey.relayout()
-                link.attr("d", path)
-
-
-        #$scope.$watch 'data', updateDiagram, true
         $scope.$watch 'data', updateDiagram
-
-        #$scope.$root.$on '$stateChangeStart', -> alert "GO"

 app.directive 'tpaMultiBarChart',[ '$compile', ($compile) ->
     restrict: 'E'
     scope:
         data: '='
-        #height: '='
-        #width: '='
-        #id: '='
         showXAxis: '='
         showYAxis: '='
         tooltips: '='
@@ -194,20 +182,16 @@
     link: ($scope,element,attrs) ->
         setAttribute = (attributeName,defaultValue) ->
             if angular.isDefined($scope[attributeName]) then $scope[attributeName] else defaultValue
-        #width = (attrs.width or 600)
-        #height = (attrs.height or 600)
         if not attrs.id
             attrs.id = 'tpaMultiBar'
             element.attr 'id', 'tpaMultiBar'
         svg = angular.element "svg"
         svg.attr 'id', attrs.id+'svg'
-        #svg.attr("width", width)
-        #svg.attr("height", height)
         element.attr 'id',attrs.id
         $compile(svg)($scope)
         element.append(svg)
         svg = d3.select('#'+attrs.id+'svg')
-        updateDiagram = (oldValue,newValue)->
+        updateDiagram = ()->
             svg.selectAll("g").remove()
             dummy =
                 [
@@ -221,15 +205,8 @@
                 .rotateLabels(setAttribute 'rotateLabels',0)
                 .showControls(setAttribute 'showControls',false)
                 .groupSpacing(0.1)
-                #.showLabels(true)
-                #.showLegend(setAttribute 'showLegend',true)
-                #chart.tooltipContent $scope.tooltipFn() if angular.isDefined $scope.tooltipFn
-                #svg.datum($scope.data)
                 svg.datum(dummy)
                 .call(chart)
-                #d3.selectAll('.nv-slice')
-                #.on 'click', (e) ->
-                #    nv.tooltip.cleanup()
                 chart
         $scope.$watch 'data', updateDiagram, true

@@ -246,15 +223,11 @@
         goFn: '&?'
         preventClickFn: '='
     link: ($scope,element,attrs) ->
-        #width = (attrs.width or 600)
-        #height = (attrs.height or 600)
         if not attrs.id
             element.attr 'id', 'tpaSankey'
         svg = d3.select('#' + element.attr 'id')
         .append('svg')
-        #svg.attr("width", width)
-        #svg.attr("height", height)
-        updateDiagram = (oldValue,newValue)->
+        updateDiagram = ()->
             svg.selectAll("g").remove()
             nv.addGraph ->
                 chart = nv.models.pieChart()


fix test

fix test


Add Semicolon (#2)

Add Semicolon (#2)

--- lib/sorter.coffee
+++ lib/sorter.coffee
@@ -56,6 +56,11 @@
 				# map
 				separations = [
 					{
+						find: ';'
+						join: '; '
+						split: ';'
+					}
+					{
 						find: '='
 						join: ' = '
 						split: '='


Fixed bug with calendar permalink

Fixed bug with calendar permalink

--- app/assets/javascripts/backbone/views/reservation_calendar_view.coffee
+++ app/assets/javascripts/backbone/views/reservation_calendar_view.coffee
@@ -117,7 +117,7 @@
   permalink: ->
     base = [location.protocol, '//', location.host, location.pathname].join('')
     view = @calendar.fullCalendar('getView')
-    viewType = view.name.replace('agenda', '')
+    viewType = view.name.replace('agenda', '').toLowerCase()
     date = view.intervalStart.format(Mess.TIME_FORMATS.compactDate)
     "#{base}?view=#{viewType}&date=#{date}"


Use content in FullNews

Use content in FullNews

--- client/components/FullNews.coffee
+++ client/components/FullNews.coffee
@@ -9,7 +9,7 @@
         for m, i in props.news.materials
             a

{m.header}

-

+

         res
         }


last missing piece

last missing piece

--- lib/worker.coffee
+++ lib/worker.coffee
@@ -363,6 +363,7 @@
         if the_result != undefined
           console.log "worker: no need to yield (#{key})"
           return the_result
+        yielded = true
         console.log "worker: helper yielding (#{key})"
         v = Fiber.yield()
         console.log "worker: helper resumed (#{key})"


updated udp protocol

updated udp protocol

--- pushd.coffee
+++ pushd.coffee
@@ -3,7 +3,7 @@
 zlib = require 'zlib'
 url = require 'url'
 Netmask = require('netmask').Netmask
-settings = require '/settings'
+settings = require './settings'
 redis = require('redis').createClient(settings.server.redis_socket or settings.server.redis_port, settings.server.redis_host,
                             {no_ready_check: true})
 Subscriber = require('./lib/subscriber').Subscriber


Check that the password is not undefined

--- pushd.coffee
+++ pushd.coffee
@@ -48,7 +48,7 @@
         if not settings.server.auth[username]?
             logger.error "Unknown user #{username}"
             return false
-        passwordOK = password is settings.server.auth[username].password
+        passwordOK = password? and password is settings.server.auth[username].password
         if not passwordOK
             logger.error "Invalid password for #{username}"
         return passwordOK


archive

archive

--- app/application.coffee
+++ app/application.coffee
@@ -25,6 +25,8 @@
     App.player = new App.Models.Player()
     App.playerView = new App.Views.Player()

+    #App.notify.send 'Archive is down', 'Archive.org is down for routine maintenance, therefore no audio will stream properly. Please follow @phishvids on twitter for updates. Sorry for the inconvenience.', 'sticky', true
+
   pushAnchors: ->
     # Thanks Gib + Art.sy
     # Globally capture clicks. If they are internal and not in the pass


refactored the app setup

refactored the app setup

--- app/boilerplate.coffee
+++ app/boilerplate.coffee
@@ -4,86 +4,117 @@

 env = process.env.NODE_ENV || 'development'

+EventEmitter = require('events').EventEmitter
+pkg = require './package'
+fs = require 'fs'
 path = require 'path'
 express = require 'express'
+coffeescript = require 'connect-coffee-script'
 bodyParser = require 'body-parser'
+serveStatic = require 'serve-static'
 methodOverride = require 'method-override'
 lessMiddleware = require 'less-middleware'
 morgan = require 'morgan'

-app = express()
+class BoilerplateApp extends EventEmitter

-app.set 'title', 'Express Boilerplate'
-app.set 'views', __dirname + '/views'
-app.set 'view engine', 'jade'
+  ###
+  Constructor
+  ###
+  constructor: ()->
+    console.log ''
+    console.log ''
+    console.log '\x1b[33m'
+    console.log fs.readFileSync(path.join(__dirname, 'BANNER'), {encoding:'utf8'})
+    console.log pkg.name, 'version', pkg.version
+    console.log '\x1b[0m'
+    console.log ''

-app.locals.layout = true
+  ###
+  Init
+  ###
+  initialize: ()->
+    @initializeConfigurations()
+    @initializeRoutes()
+    @appServer = @app.listen process.env.PORT, ()=>
+      console.log 'listening on port ', process.env.PORT
+      @emit('BoilerplateApp.ready')

-app.use morgan('dev')
-app.use bodyParser.json()
-app.use bodyParser.urlencoded( extended: true )
-app.use methodOverride()
+  ###
+  Init Configurations
+  ###
+  initializeConfigurations: ()->
+    # Paths
+    @staticDir = path.join(__dirname, '/static')
+    @sourceDir = path.join(__dirname, '/src')

-# Less
-app.use lessMiddleware path.join(__dirname, '/src'),
-    dest: __dirname + '/out'
-  ,
-    yuicompress: (process.env.NODE_ENV is 'production')
-    sourceMap: (process.env.NODE_ENV is 'development')
-    compress: (process.env.NODE_ENV is 'production')
+    @app = express()

-# CoffeeScript
-app.use require('connect-coffee-script')(
-  src: __dirname + '/src'
-  dest: __dirname + '/out'
-)
+    @app.set 'title', 'Express Boilerplate'

-# Static dir
-app.use express.static(__dirname + '/out')
+    @app.locals.layout = true

-# Robots
-app.use(function (req, res, next) {
-  if req.url == '/robots.txt'
-    res.type 'text/plain'
-    res.send if process.env.NODE_ENV is 'production' then "User-agent: *" else "User-agent: *\nDisallow: /"
-  else
-    next()
+    # Logger
+    @app.use morgan('dev')
+
+    # Body Parser
+    @app.use bodyParser.json
+      limit: '50mb'
+    @app.use bodyParser.urlencoded
+     extended: true
+     limit: '50mb'
+
+     # Method override
+    @app.use methodOverride()
+
+    # Jade
+    @app.set 'views', __dirname + '/views'
+    @app.set 'view engine', 'jade'
+
+    # Less
+    @app.use lessMiddleware @sourceDir,
+      dest: @staticDir
+      render:
+        yuicompress: (process.env.NODE_ENV is 'production')
+        sourceMap: (process.env.NODE_ENV is 'development')
+        compress: (process.env.NODE_ENV is 'production')
+
+    # CoffeeScript - TODO: swap out with browserify or webpack
+    @app.use coffeescript
+      src: @sourceDir
+      dest: @staticDir
+
+    # Static dir
+    @app.use serveStatic @staticDir
+
+    # development only
+    if env == 'development'
+      # set development configuration here
+      @app.locals.pretty = true
+
+    # production only
+    if env == 'production'
+      # set production configuration here
+      console.log 'prod'
+
+  ###
+  Init Routes
+  ###
+  initializeRoutes: ()->
+
+    # Robots
+    require('./lib/routes/robots')(@app)
+
+    # routes
+    @app.get '/', (req, res) ->
+      res.render 'index',
+        title: "Home"
+
+    # 404
+    require('./lib/routes/404')(@app)

-# development only
-if env == 'development'
-  # set development configuration here
-  app.locals.pretty = true
+boilerplateApp = new BoilerplateApp()
+boilerplateApp.initialize()

-# production only
-if env == 'production'
-  # set production configuration here
-  console.log 'prod'
-
-
-# routes
-app.get '/', (req, res) ->
-  res.render 'index',
-    title: "Home"
-
-
-# 404
-app.use (req, res, next) ->
-  res.status(404)
-
-  # html
-  if req.accepts('html')
-    res.render '404', url: req.url
-    return
-
-  # json
-  if req.accepts('json')
-    res.send error: 'Not found'
-    return
-
-  # default to plain-text
-  res.type('txt').send 'Not found'
-
-
-app.listen 3000
-console.log 'listening on port 3000'
+module.exports = boilerplateApp


Fixed ordering problem I introduced with /play/ladder routes.

--- app/core/Router.coffee
+++ app/core/Router.coffee
@@ -86,11 +86,11 @@

     'play-old': go('play/MainPlayView')  # This used to be 'play'.
     'play': go('play/CampaignView')
-    'play/:map': go('play/CampaignView')
     'play/ladder/:levelID': go('ladder/LadderView')
     'play/ladder': go('ladder/MainLadderView')
     'play/level/:levelID': go('play/level/PlayLevelView')
     'play/spectate/:levelID': go('play/SpectateView')
+    'play/:map': go('play/CampaignView')

     'preview': go('HomeView')


rebuild list only when broken

rebuild list only when broken

--- app/models/task.coffee
+++ app/models/task.coffee
@@ -78,19 +78,19 @@
                 delete idList[task.id]
                 task = idList[nextTaskId]

-            # Rebuild linked list if there are unlinked tasks
-            lastTask = result[result.length - 1]
-            brokenTasks = [lastTask]
-            for taskId of idList
-                task = idList[taskId]
-                task.previousTask = lastTask.id
-                lastTask.nextTask = task.id
-                result.push task
-                lastTask = task
-                brokenTasks.push task
-
-            # Save correction if something wrong happened
-            if brokenTasks.length > 0
+            if Object.keys(idList).length > 0
+                console.log 'LL is broken'
+                # Rebuild linked list if there are unlinked tasks
+                lastTask = result[result.length - 1]
+                brokenTasks = [lastTask]
+                for taskId of idList
+                    task = idList[taskId]
+                    task.previousTask = lastTask.id
+                    lastTask.nextTask = task.id
+                    result.push task
+                    lastTask = task
+                    brokenTasks.push task
+
                 # TODO: make saving with async.
                 for task in brokenTasks
                     attributes =


添加sm-12类

--- lib/login/login-view.coffee
+++ lib/login/login-view.coffee
@@ -17,7 +17,7 @@
 				@div class: 'col-sm-12 col-md-12 ', =>
 					@input type:'checkbox',style:'margin-right:2px;margin-left:18px;'
 					@label  "记住密码" ,class:'checkBox_label_view'
-				@div class: 'col-md-12 text-right', =>
+				@div class: 'col-sm-12 col-md-12 text-right', =>
 					@button  "登 录",name: 'loginBtn', class:'btn loginBtn'
 					@button  "取 消", outlet:'cancelBtn', click: 'onCancelClick',name: 'loginCancelBtn', class:'btn cancelBtn'


Some test

Some test


fixed tests with latest changes

fixed tests with latest changes

--- test/main.coffee
+++ test/main.coffee
@@ -1,13 +1,12 @@
 # no node core in stack trace
 require 'clarify'

+util = require 'util'
 should = require 'should'
 sinon = require 'sinon'
 winston = require 'winston'

 describe 'testing', -> it 'works', -> (42).should.equal 42
-
-sinon.stub winston, 'add'

 describe 'plugin', ->

@@ -23,16 +22,9 @@
   it 'could be required', -> should.exist lib

   it 'inherits from Winston', ->
-    cloudwatch = winston.add.args[0][0]
-    cloudwatch.super_.should.equal winston.Transport
+    lib.super_.should.equal winston.Transport

   it 'uploads logs to Cloudwatch', ->
-    winston.add.restore()
-    # refresh the module in cache so that we use
-    # the correct winston.add
-    name = require.resolve '../index'
-    delete require.cache[name]
-    require '../index'
-
+    winston.add(lib, {});
     winston.info 'testing'
     plugin.upload.calledOnce.should.be.true


data: top-commitments pagecount param added

--- app/routes-data.coffee
+++ app/routes-data.coffee
@@ -565,10 +565,8 @@
         else
           cond += " AND (aiddata_purpose_code='#{purpose}')"

-
     pageSize = 10
     page = 0
-

     if @query.page
       unless /^[0-9]{0,15}$/.test @query.page
@@ -578,9 +576,13 @@
         offset = @query.page

-    pg.sql "
-      select
-        donor, recipient,
+    console.log @query.pagecount
+    if @query.pagecount?
+      q = "CEIL(count(*)/#{pageSize}) AS pagecount"
+      order = ""
+      limit = ""
+    else
+      q = "donor, recipient,
         short_description,
         short_description_original_language,
         long_description,
@@ -588,13 +590,21 @@
         other_involved_institutions,
         to_char(commitment_amount_usd_constant, 'FM99999999999999999999') as amount_constant,
         COALESCE(aiddata2.aiddata_purpose_code, aiddata2.crs_purpose_code, '99000') AS purpose_code,
-        aiddata_purpose_name AS purpose_name
+        aiddata_purpose_name AS purpose_name"
+      order = "order by commitment_amount_usd_constant desc"
+      limit = "limit #{pageSize} OFFSET #{pageSize * page}"
+
+
+
+
+    pg.sql "
+      select #{q}
        from aiddata2
       WHERE
         commitment_amount_usd_constant is not null
         #{cond}
-      order by commitment_amount_usd_constant desc
-      limit #{pageSize} OFFSET #{pageSize * page}
+      #{order}
+      #{limit}
     ",
       (err, data) =>
         unless err?


removes some logging

removes some logging

--- src/ui/views/messages.coffee
+++ src/ui/views/messages.coffee
@@ -127,11 +127,6 @@

     conv_id = viewstate?.selectedConv
     c = conv[conv_id]
-    #
-    console.log('conversation', c)
-    console.log('  delivery_medium')
-    console.log('    outer', c?.self_conversation_state?.delivery_medium_option[0])
-    console.log('    inner', c?.self_conversation_state?.delivery_medium_option[0].delivery_medium)
     #
     if c?.current_participant?
         for participant in c.current_participant


Bake inclusion priority into main provider loop

--- lib/provider-manager.coffee
+++ lib/provider-manager.coffee
@@ -86,12 +86,16 @@

     matchingProviders = []
     disableDefaultProvider = false
+    lowestIncludedPriority = 0

     for providerMetadata in @providers
       {provider} = providerMetadata
       if providerMetadata.matchesScopeChain(scopeChain)
         matchingProviders.push(provider)
-        disableDefaultProvider = true if providerMetadata.shouldDisableDefaultProvider(scopeChain)
+        if provider.excludeLowerPriority?
+          lowestIncludedPriority = Math.max(lowestIncludedPriority, provider.inclusionPriority ? 0)
+        if providerMetadata.shouldDisableDefaultProvider(scopeChain)
+          disableDefaultProvider = true

     matchingProviders = _.without(matchingProviders, @fuzzyProvider) if disableDefaultProvider

@@ -105,10 +109,6 @@
       else
         (providerB.suggestionPriority ? 1) - (providerA.suggestionPriority ? 1)

-    lowestIncludedPriority = 0
-    for provider in matchingProviders
-      if provider.excludeLowerPriority?
-        lowestIncludedPriority = Math.max(lowestIncludedPriority, provider.inclusionPriority ? 0)
     (provider for provider in matchingProviders when (provider.inclusionPriority ? 0) >= lowestIncludedPriority)

   toggleFuzzyProvider: (enabled) =>


Removed validation of GCM token #130

Removed validation of GCM token #130

--- lib/pushservices/gcm.coffee
+++ lib/pushservices/gcm.coffee
@@ -1,10 +1,8 @@
 gcm = require 'node-gcm'

 class PushServiceGCM
-    tokenFormat: /^[a-zA-Z0-9_-]+$/
     validateToken: (token) ->
-        if PushServiceGCM::tokenFormat.test(token)
-            return token
+        return token

     constructor: (conf, @logger, tokenResolver) ->
         conf.concurrency ?= 10


Or queries support

Or queries support

--- query.coffee
+++ query.coffee
@@ -51,7 +51,22 @@

                 if _.size(@_constraints) > 0
                     params = _.clone(@_constraints)
-                console.log @_constraints
+
+                    # Check for 'or' queries
+                    #
+                    if @_orWhereConstraints?
+
+                        # Push latest where constraints chain. It is not yet joined, because
+                        # usually the join is computed by `or`.
+                        # However, nobody wants to terminate its query with `or`!
+                        #
+                        if _.size(@_constraints.where)
+                            @_orWhereConstraints.push _.clone(@_constraints.where)
+                            @_constraints.where = {}
+
+                        params.where =
+                            $or: @_orWhereConstraints
+
                 params

@@ -68,6 +83,22 @@
                     get: ->
                         @_constraints.where =  @_constraints.where ? {}
                         @
+
+                # Simple expression-joiner to make the query statement more readable
+                and:
+                    get: -> @
+
+                # Create an $or query.
+                #
+                or: ->
+                    @_orWhereConstraints = @_orWhereConstraints ? [] # Store where constraints as an array
+                    @_orWhereConstraints.push _.clone(@_constraints.where)
+
+                    # Reset
+                    @_constraints.where = {}
+                    @_currentAttr = null
+
+                    @

             # Sets current attribute so that chained comparator can operate on it.
             #


Removed comment outed lines from acefindandreplaceview.

--- client/app/Applications/Ace.kdapplication/acefindandreplaceview.coffee
+++ client/app/Applications/Ace.kdapplication/acefindandreplaceview.coffee
@@ -68,27 +68,10 @@
       @setViewHeight yes

   close: ->
-    # @resizeAceEditor 0
-    # @$().css top: 19
     @hide()
     @resizeAceEditor 0

   setViewHeight: (isReplaceMode) ->
-    # height   = 32
-    # @mode    = "find"
-
-    # if isReplaceMode
-    #   @mode  = "replace"
-    #   height = 60
-
-    # if @isHidden
-    #   @show()
-    #   @isHidden = no
-
-    # @$().css { height }
-    # @resizeAceEditor height
-    # @findInput.setFocus()
-
     height = if isReplaceMode then 60 else 32
     @show()
     @$().css { height }
@@ -139,7 +122,6 @@
     {editor}   = @getDelegate().ace
     methodName = if doReplaceAll then "replaceAll" else "replace"

-    # editor.find findKeyword, @getSearchOptions() if not doReplaceAll
     editor[methodName] replaceKeyword

   pistachio: ->


groups summary: when click is received elsewhere, it will now hide

--- client/app/Applications/Groups.kdapplication/Views/groupsummary.coffee
+++ client/app/Applications/Groups.kdapplication/Views/groupsummary.coffee
@@ -116,6 +116,15 @@
     """

   showSummary:(event)->
+    @getSingleton('windowController').addLayer @
+    @once 'ReceivedClickElsewhere', =>
+      # @getSingleton('windowController').removeLayer @
+      @hideSummary()
+      unless @lazyDomController.isLandingPageVisible()
+        @utils.wait 400, =>
+          @sign.setClass "swing-in"
+
+
     if event
       event.stopPropagation()
       event.preventDefault()
@@ -132,13 +141,14 @@

   hideSummary:(event)->
+
     if event
       event.stopPropagation()
       event.preventDefault()
     if @lazyDomController.isLandingPageVisible()
-      @$().css top : 0
       @once "transitionend", =>
         @summaryNavBar.setClass "in"
+      @$().css top : 0
     else
       @once "transitionend", =>
         @sign.unsetClass "swing-out"


Save comment and add to collection

Save comment and add to collection

--- core/app/backbone/views/facts/auto_complete_fact_relations_view.coffee
+++ core/app/backbone/views/facts/auto_complete_fact_relations_view.coffee
@@ -134,10 +134,10 @@

   submit: ->
     content = @$('.content').val()
-
-    comment = new Comment( content: content )
-    @comments.add comment
-    comment.save()
+    comment = new Comment(content: content, created_by: currentUser)
+
+    @comments.add(comment)
+    comment.save


Tests: IDE: Added refresh selector for toggleInvisibleFiles test.

--- client/test/lib/ide/search.coffee
+++ client/test/lib/ide/search.coffee
@@ -93,6 +93,7 @@
     fileSelector = ".file-container span[title='#{filePath}/#{fileName}']"

     helpers.createFileFromMachineHeader(browser, user, fileName, no)
+    ideHelpers.clickItemInMachineHeaderMenu(browser, '.refresh')

     browser
       .pause   4000 # wait for file create complete


Change selector with show full path feature's changes.

--- client/test/lib/ide/search.coffee
+++ client/test/lib/ide/search.coffee
@@ -8,7 +8,7 @@

   findInFiles: (browser) ->

-    helpers.beginTest(browser)
+    user = helpers.beginTest(browser)
     helpers.waitForVMRunning(browser)

     contentSearchModalSelector  = '.content-search-modal'
@@ -37,7 +37,7 @@

         return browser.end() unless lowercase

-        tabHandleSelector = ".panel-1 div[title='#{fileName}']"
+        tabHandleSelector = ".panel-1 div[title='/home/#{user.username}/Web/#{fileName}']"
         editorSelector    = ".panel-1 .kdtabpaneview.active.#{lowercase} .ace_content"

         browser


compile command tests passing

compile command tests passing

--- test/test.coffee
+++ test/test.coffee
@@ -19,8 +19,7 @@
   describe 'compile', -> # ----------------------------------------------------------------------

     before (done) ->
-      run "cd #{basic_root}; ../../bin/roots compile", (a,b,c) ->
-        console.log a,b,c
+      run "cd #{basic_root}; ../../bin/roots compile", done

     it 'should compile files to /public', ->
       fs.readdirSync(path.join(basic_root, 'public')).should.have.lengthOf(5)
@@ -30,9 +29,9 @@
       js_content.should.not.match /\n/

     it 'should compile all files to public', ->
-      css_content = fs.readFileSync path.join(basic_root, 'public/css/master.css'), 'utf8'
+      css_content = fs.readFileSync path.join(basic_root, 'public/css/example.css'), 'utf8'
       css_content.should.not.match /\n/
-      rimraf.sync path.join(basic_root, 'public')
+      shell.rm '-rf', path.join(basic_root, 'public')

   describe 'new', -> # -------------------------------------------------------------------------


write final edit levels

write final edit levels

--- games/edit.coffee
+++ games/edit.coffee
@@ -9,13 +9,14 @@
 #
 # - Edit / Save Controls
 # - Renaming the Level
+# - Customize Name and Color
 # - Select a Sprite
 # - Click to Create a Sprite
-# - Customize Name and Color
 #
 # TODO:
 # - Changing the Kinds [popup]
 # - Editable and Obstruction [checkbox]
+# - Display and Change Direction [popup] N/S/E/W

 exports.game = {
   name: 'BETA: Use "Edit" to Modify Levels'
@@ -73,5 +74,45 @@
         }
       ]
     }
+    {
+      name: 'Click Blue Pond to Add and Select Logs'
+      comment: '
+      Can create arbitrary sprites and program them if in edit mode.
+      '
+      goal: { clicks: 1, bricks: 2, ticks: 11 }
+      bricks: 1
+      sprites: [
+        { kind: 'log', position: [5,0] }
+        { kind: 'gate', actions: {bump: ['_victory']}, position: [4,4] }
+        {
+          kind: 'turtle'
+          actions: {
+            run: ['forward']
+            bump: ['right']
+          }
+        }
+      ]
+    }
+    {
+      name: 'Create and Share Your Own Level'
+      comment: '
+      Encourage students to express themselves.
+      Track the solution and use that for the new goal metrics.
+      After completion, can share via email or social media.
+      Will run on our app via a custom URL.
+      '
+      goal: { clicks: 0, bricks: 0, ticks: 0 }
+      bricks: 2
+      sprites: [
+        { kind: 'gate', actions: {bump: ['_victory']}, position: [7,7] }
+        {
+          kind: 'turtle'
+          actions: {
+            run: ['forward']
+            bump: ['right']
+          }
+        }
+      ]
+    }
   ]
 }


some code changes that don't actually change anything

--- slideshow.coffee
+++ slideshow.coffee
@@ -94,7 +94,7 @@
       # and go!
       init.call @

-    # default options
+    # private methods and variables

     defaults =
       touchEnabled: true # enable touch events
@@ -160,8 +160,6 @@
           if slideState === 1 then this is the currently visible slide and it must be visible
           ###
           slideElement.style.display = if slideState > 0 then 'block' else 'none'
-
-    # private methods and variables

     init = ->
       initContainer.call @
@@ -343,26 +341,21 @@
       @slides[i]

     # get the currently visible slide
-    getCurrentSlide: ->
-      @slides[@current]
+    getCurrentSlide: -> @slides[@current]

     # get the slide after the currently visible one
-    getNextSlide: ->
-      @getSlide @current + 1
+    getNextSlide: -> @getSlide @current + 1

     # get the slide before the currently visible one
-    getPrevSlide: ->
-      @getSlide @current - 1
+    getPrevSlide: -> @getSlide @current - 1

     # get the first slide
-    getFirstSlide: ->
-      @slides[0]
+    getFirstSlide: -> @slides[0]

     # get the last slide
-    getLastSlide: ->
-      @slides[@slides.length - 1]
-
-    # goTo initiate an animation
+    getLastSlide: -> @slides[@slides.length - 1]
+
+    # goTo* initiates an animation

     # go to the slide at index i
     goTo: (i, cb) ->
@@ -374,20 +367,16 @@
       animateSlides.call @, currentSlide, targetSlide, {direction}, cb

     # go to the next slide
-    goToNext: (cb) ->
-      @goTo @current + 1, cb
+    goToNext: (cb) -> @goTo @current + 1, cb

     # go to the previous slide
-    goToPrev: (cb) ->
-      @goTo @current - 1, cb
+    goToPrev: (cb) -> @goTo @current - 1, cb

     # go to first slide
-    goToFirst: (cb) ->
-      @goTo 0, cb
+    goToFirst: (cb) -> @goTo 0, cb

     # go to last slide
-    goToLast: (cb) ->
-      @goTo @slides.length - 1, cb
+    goToLast: (cb) -> @goTo @slides.length - 1, cb

     # class methods


updated config description

updated config description

--- lib/block-cursor.coffee
+++ lib/block-cursor.coffee
@@ -42,7 +42,7 @@
       maximum: 1
       order: 4
     blinkInterval:
-      description: 'Cursor blinking interval in milliseconds. Set to 0 to disable blinking. Doesn\'t work for mini editors, except for disabling cursor blinking'
+      description: 'Cursor blinking interval in milliseconds. Set to 0 to disable blinking.'
       type: 'integer'
       default: 800
       minimum: 0


Issue #7

Issue #7

Hopefully adds minimum deck size and influence limit to ident pulls

--- src/netrunner.coffee
+++ src/netrunner.coffee
@@ -69,6 +69,10 @@

   if card.type == "Operation" || card.type == "Event"
     text += "*Cost*: #{card.cost}\n"
+
+  if card.type == "Identity"
+    text += "*Minimum Deck Size*: #{card.minimumdecksize}\n"
+    text += "*Influence Limit*: #{card.influencelimit}\n"

   if typeof card.factioncost != 'undefined' || card.factioncost != 0
     text += "*Influence*: #{card.factioncost}\n"


Client AccounPage: update profile, password and email at once

--- client/Account/views/username.coffee
+++ client/Account/views/username.coffee
@@ -53,27 +53,59 @@
           style            : 'solid green fr'
       callback             : @bound 'update'

+
   update:(formData)->

-    {email} = formData
+    {daisy} = Bongo

-    # fixme: if new pass inputs filled do change password
-    # CHANGE PASSWORD
-    # KD.remote.api.JUser.changePassword formData.password,(err,docs)=>
-    #   unless err then do @passwordDidUpdate
+    {email, password, confirmPassword, firstName, lastName, username} = formData

-    # CHANGE EMAIL
+    queue = [
+      =>
+        # update firstname and lastname
+        me = KD.whoami()
+        {profile:{firstName:oldFirstName, lastName:oldLastName}} = me
+        # do not do anything if current firstname and lastname is same
+        return queue.next() if oldFirstName is firstName and oldLastName is lastName

-    KD.remote.api.JUser.changeEmail {email}, (err, result)=>
+        me.modify {
+          "profile.firstName": firstName,
+          "profile.lastName" : lastName
+        }, (err)->
+          return notify err.message  if err
+          queue.next()
+      =>
+        # secondly change user email address
+        KD.remote.api.JUser.changeEmail {email}, (err, result)=>
+          # here we are simply discarding the email is same error
+          # but do not forget to warn users about other errors
+          if err
+            return queue.next() if err.message is "EmailIsSameError"
+            return notify err.message

-      return notify err.message  if err and err.name isnt 'PINExistsError'
+          new VerifyPINModal 'Update E-Mail', (pin)=>
+            KD.remote.api.JUser.changeEmail {email, pin}, (err)=>
+              notify if err then err.message else "E-mail changed!"
+              queue.next()
+      =>
+        # on third turn update password
+        # check for password confirmation
+        return  notify "Passwords did not match" if password isnt confirmPassword
+        # if password is empty than discard operation
+        return queue.next() if password is ""

-      notify err.message  if err and err.name is 'PINExistsError'
-
-      new VerifyPINModal 'Update E-Mail', (pin)=>
-        KD.remote.api.JUser.changeEmail {email, pin}, (err)=>
-          notify if err then err.message else "E-mail changed!"
-          @emit "EmailChangedSuccessfully", email
+        KD.remote.api.JUser.changePassword password, (err,docs)=>
+          log "arguments"
+          log arguments
+          if err
+            return queue.next()  if err.message is "PasswordIsSame"
+            return  notify err.message
+          return queue.next()
+      =>
+        # if everything is OK or didnt change, show profile updated modal
+        notify "Profile Updated"
+    ]
+    daisy queue

   viewAppended:->


changed to tiny arrow

changed to tiny arrow

--- client/app/MainApp/avatararea.coffee
+++ client/app/MainApp/avatararea.coffee
@@ -246,7 +246,7 @@
     @listenTo
       KDEventTypes        : "click"
       listenedToInstance  : redirectLink
-      callback            : ()=>
+      callback            : ->
         appManager.openApplication('Inbox')
         appManager.tell 'Inbox', "goToNotifications"
         @hide()


MainController: isFeatureDisabled: wait until mainController.ready to check features

--- client/app/lib/maincontroller.coffee
+++ client/app/lib/maincontroller.coffee
@@ -145,16 +145,10 @@
     @forwardEvents remote, ['disconnected', 'reconnected']

-  isFeatureDisabled: (name, options = {}) ->
+  isFeatureDisabled: (name, callback) -> @ready => callback do ->

     return no  unless name
-
-    options.godMode ?= yes
-
-    { godMode } = options
-
-    if godMode
-      return no  if checkFlag 'super-admin'
+    return no  if checkFlag 'super-admin'

     {roles}            = globals.config
     {disabledFeatures} = getGroup()
@@ -165,7 +159,7 @@
     role = 'moderator' if 'moderator' in roles
     role = 'admin'     if 'admin'     in roles

-    return no   if !disabledFeatures[role]
+    return no   unless disabledFeatures[role]
     return yes  if disabledFeatures[role] and name in disabledFeatures[role]

     return no


home: update team billing section with react components

--- client/home/lib/billing/index.coffee
+++ client/home/lib/billing/index.coffee
@@ -1,5 +1,23 @@
-kd                  = require 'kd'
-HomeTeamBillingForm = require './hometeambillingform'
+kd                                = require 'kd'
+HomeTeamBillingPaymentInformation = require './hometeambillingpaymentinformation'
+HomeTeamBillingCreditCard         = require './hometeambillingcreditcard'
+HomeTeamBillingBillingInformation = require './hometeambillingbillinginformation'
+
+SECTIONS =
+  'Payment Information' : HomeTeamBillingPaymentInformation
+  'Credit Card'         : HomeTeamBillingCreditCard
+  'Billing Information' : HomeTeamBillingBillingInformation
+
+
+header = (title) ->
+  new kd.CustomHTMLView
+    tagName  : 'header'
+    cssClass : 'HomeAppView--sectionHeader'
+    partial  : title
+
+
+section = (name) -> new SECTIONS[name]
+

 module.exports = class HomeTeamBilling extends kd.CustomScrollView

@@ -9,4 +27,17 @@

     super options, data

-    @wrapper.addSubView new HomeTeamBillingForm+    kd.singletons.mainController.ready =>
+
+      formView = new kd.FormView
+        cssClass: kd.utils.curry 'HomeAppView--billing-form', options.cssClass
+
+      formView.addSubView header 'Payment Information'
+      formView.addSubView section 'Credit Card'
+      formView.addSubView section 'Payment Information'
+
+      formView.addSubView header 'Billing Information'
+      formView.addSubView section 'Billing Information'
+
+      @wrapper.addSubView formView
+


fix(language): default to english if language doesn't exist

--- src/nylas-env.coffee
+++ src/nylas-env.coffee
@@ -217,7 +217,8 @@
         !(require('spellchecker').isMisspelled(text))
     })
     spellchecker = require('spellchecker')
-    spellchecker.setDictionary(navigator.language)
+    lang = navigator.language ? "en-US"
+    spellchecker.setDictionary(lang)

     @subscribe @packages.onDidActivateInitialPackages => @watchThemes()
     @windowEventHandler = new WindowEventHandler


replace test order

replace test order

--- spec/test.coffee
+++ spec/test.coffee
@@ -1,15 +1,15 @@
 expect = chai.expect

 describe "API Existence", ->
-
-  it "_.ready exists", ->
-    expect(!!_.ready).to.equal true

   it "_.qsa exists", ->
     expect(!!_.qsa).to.equal true

   it "_.qs exists", ->
     expect(!!_.qs).to.equal true
+
+  it "_.ready exists", ->
+    expect(!!_.ready).to.equal true

   it "_.bind exists", ->
     expect(!!_.bind).to.equal true


Use proper settings for generated test resources.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -20,25 +20,27 @@

     clean:
       default: "lib"
-      test: "spec"
+      test: "test"

     coffee:
       options:
         bare: true
       default:
-        expand: true
-        flatten: true
-        cwd: "src/coffee"
-        src: ["*.coffee"]
-        dest: "lib"
-        ext: ".js"
+        files: grunt.file.expandMapping(['**/*.coffee'], 'lib/',
+          flatten: false
+          cwd: 'src/coffee'
+          ext: '.js'
+          rename: (dest, matchedSrcPath) ->
+            dest + matchedSrcPath
+          )
       test:
-        expand: true
-        flatten: true
-        cwd: "src/spec"
-        src: ["*.spec.coffee"]
-        dest: "spec"
-        ext: ".spec.js"
+        files: grunt.file.expandMapping(['**/*.spec.coffee'], 'test/',
+          flatten: false
+          cwd: 'src/spec'
+          ext: '.spec.js'
+          rename: (dest, matchedSrcPath) ->
+            dest + matchedSrcPath
+          )

     concat:
       options:
@@ -66,9 +68,9 @@
         stderr: true
         failOnError: true
       coverage:
-        command: "istanbul cover jasmine-node --captureExceptions spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
+        command: 'istanbul cover jasmine-node --captureExceptions test && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage'
       jasmine:
-        command: "jasmine-node --captureExceptions spec"
+        command: 'jasmine-node --verbose --captureExceptions test'

   # load plugins that provide the tasks defined in the config
   grunt.loadNpmTasks "grunt-coffeelint"


Start Selenium in background.

Start Selenium in background.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -29,10 +29,11 @@
           'angular-bootstrap/ui-bootstrap-tpls.js'
           'angular-resource/angular-resource.js'
           'angular-route/angular-route.js'
+          'angular-slider/slider.js'
           'angular-ui-router/release/*.js'
           'angularjs-nvd3-directives/dist/*.js'
           'd3/*.js'
-          'dat.gui/dat.gui.js'
+          'domready/ready.js'
           'lodash/dist/lodash.underscore.js'
           'moment/*.js'
           'nvd3/nv.d3.js'
@@ -104,16 +105,27 @@
       unit:
         configFile: 'test/conf/karma-conf.coffee'

+    exec:
+      selenium:
+        # If selenium is not already running, then start it. Suppress all
+        # output, since there are no documented options to tailor the
+        # selenium log level or log file at the command line. The selenium
+        # server is spawned as a background process that survives the grunt
+        # session. The command pseudo-code is as follows:
+        # * Check for a process which listens on port 4444
+        # * Start the selenium server as a background process
+        # * Suppress all output
+        # The selenium server can be killed by getting the process id
+        # using the netstat command and issuing a kill command on that pid.
+        # This command runs in Mac and Linux, but not Windows.
+        command: 'netstat -lnp tcp 2>/dev/null | grep -q 4444 || ' +
+                 './node_modules/selenium-standalone/bin/start-selenium' +
+                  ' > /dev/null 2>&1 &'
+
     protractor:
-      options:
-        debug: not not grunt.option('debug')
-        keepAlive: not not grunt.option('debug')
-        mochaOpts:
-          debug: true
-          timeout: 4000
       e2e:
         configFile: 'test/conf/protractor-conf.coffee'
-
+
     markdown:
       compile:
         expand: true
@@ -128,7 +140,7 @@
       compile:
         src: ['app/stylesheets/app.styl']
         dest: '_public/stylesheets/app.css'
-
+
     express:
       options:
         script: 'server.js'
@@ -222,8 +234,8 @@

   grunt.registerTask 'test:unit', ['karma:unit']

-  grunt.registerTask 'test:e2e', ['express:test', 'protractor:e2e']
-
-  grunt.registerTask 'test', ['express:test', 'test:unit', 'test:e2e']
+  grunt.registerTask 'test:e2e', ['exec:selenium', 'express:test', 'protractor:e2e']
+
+  grunt.registerTask 'test', ['test:unit', 'test:e2e']

   grunt.registerTask 'release', ['build', 'requirejs', 'cssmin']


chore: omg

chore: omg

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -256,7 +256,7 @@
         class: "leading-indent-fix"
         verbose: true

-      serve:
+      default:
         files: [
           expand: true
           cwd: "<%= config.dist %>"


Gruntfileを修正

Gruntfileを修正

- watchでtest/以下のcoffeeファイルのlinterが動くように修正
- testに無駄なファイルを作らないように修正
- Minifyのオプションを指定
- coffeeのオプションを変更

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -17,20 +17,17 @@
       coffee:
         files: ['<%= config.src %>/{,*/}*.coffee']
         tasks: ['newer:coffeelint:all', 'coffee:dist']
-      jsTest:
+      coffeeTest:
         files: [
           '<%= config.dist %>/<%= pkg.name %>.js'
           '<%= config.test %>/{spec,mock}/{,*/}*.coffee'
         ]
-        tasks: ['karma']
+        tasks: ['newer:coffeelint:test', 'karma']
       gruntfile:
         files: ['Gruntfile.coffee']
         tasks: ['coffeelint:gruntfile']

     coffee:
-      options:
-        bare: true
-
       dist:
         files:
           '<%= config.dist %>/<%= pkg.name %>.js': [
@@ -45,7 +42,15 @@

     # Minify JavaScript code
     uglify:
+      options:
+        compress: true
+        mangle: true
+        preserveComments: 'some'
+        ASCIIOnly: true
       dist:
+        options:
+          sourceMap: true
+          sourceMapName: '<%= config.dist %>/<%= pkg.name %>.min.map'
         files:
           '<%= config.dist %>/<%= pkg.name %>.min.js': [
             '<%= config.dist %>/<%= pkg.name %>.js'
@@ -65,7 +70,6 @@
     # Run some tasks in parallel to speed up the build process
     concurrent:
       test: [
-        'coffee:dist'
       ]
       dist: [
         'coffee:dist'
@@ -79,7 +83,6 @@

   grunt.registerTask 'test', [
-    'clean:dist'
     'concurrent:test'
     'karma'
   ]
@@ -92,7 +95,6 @@

   grunt.registerTask 'default', [
     'newer:coffeelint'
-    'build'
     'test'
     'watch'
   ]


Fix paths

Fix paths

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -3,13 +3,13 @@
     jekyll:
       build:
         options:
-          src: 'web'
-          dest: 'web/_site'
-          config: 'web/_config.yml'
+          src: '.'
+          dest: '_site'
+          config: '_config.yml'

     'gh-pages':
       options:
-        base: 'web/_site',
+        base: '_site',
         repo: 'git@github.com:gregoryjscott/gregoryjscott.github.io.git'
         branch: 'master'
         message: 'Generated by me'
@@ -19,7 +19,7 @@
       server:
         options:
           port: 4000
-          base: 'web/_site'
+          base: '_site'
           keepalive: true

   grunt.loadNpmTasks 'grunt-git'


Change sourcemap

Change sourcemap

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -21,8 +21,8 @@
     copyIgnore: ['!script/**/*.js', '!script/{countries}']

   webpackPlugins = [
+    new webpack.BannerPlugin('front-phone - v'+pkg.version+' - https://vtex.github.io/front.phone/', {entryOnly: true})
     new webpack.optimize.UglifyJsPlugin({compress: { warnings: false }}),
-    new webpack.BannerPlugin('front-phone - v'+pkg.version+' - https://vtex.github.io/front.phone/', {entryOnly: true})
   ]

   # Add custom configuration here as needed
@@ -70,7 +70,7 @@
             { test: /\.png$/, loader: "url-loader?limit=100000", exclude: /node_modules/ }
             { test: /\.jpg$/, loader: "file-loader", exclude: /node_modules/ }
           ]
-        devtool: "source-map"
+        devtool: "eval"
       main:
         entry:
           "main": "./src/script/main.coffee"


Gruntfile: use correct concat order for my scripts

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,7 +6,11 @@
         options:
           join: true
         files:
-          'generated_bundle.js': 'src/*.coffee'
+          'generated_bundle.js': [
+            'src/base/*.coffee',
+            'src/*/*.coffee',
+            'src/main.coffee'
+          ]

   # Load plugins
   grunt.loadNpmTasks 'grunt-contrib-coffee'


Change text on deploy

Change text on deploy

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -154,13 +154,12 @@

 	# Generates version folder
 	grunt.registerTask 'gen-version', ->
+		grunt.log.writeln 'Application root: '.cyan + grunt.config('applicationRoot').green
 		grunt.log.writeln 'Deploying to environmentName: '.cyan + grunt.config('environmentName').green
 		grunt.log.writeln 'Deploying to buildNumber: '.cyan + grunt.config('buildNumber').green
 		grunt.log.writeln 'Deploying to environmentType: '.cyan + grunt.config('environmentType').green
-		grunt.log.writeln 'Directory: '.cyan + grunt.config('versionName')().green
-		grunt.log.writeln 'Version set to: '.cyan + grunt.config('gitCommit').green
-		grunt.log.writeln 'Application root set to: '.cyan + grunt.config('applicationRoot').green
-		grunt.log.writeln 'Deploy folder: '.cyan + grunt.config('deployDirectory').green
+		grunt.log.writeln 'Version name: '.cyan + grunt.config('versionName')().green
+		grunt.log.writeln 'Version deploy directory: '.cyan + (path.resolve grunt.config('deployDirectory'), grunt.config('versionName')()).green
 		grunt.task.run ['copy:version', 'string-replace:deploy']

 	# Deploy - creates deploy folder structure
@@ -168,7 +167,8 @@
 		commit = grunt.config('gitCommit')
 		deployDir = path.resolve grunt.config('deployDirectory'), commit
 		deployExists = false
-		grunt.log.writeln 'Version deploy dir set to: '.cyan + deployDir.green
+		grunt.log.writeln 'Commit: '.cyan + grunt.config('gitCommit').green
+		grunt.log.writeln 'Commit deploy directory: '.cyan + deployDir.green
 		try
 			deployExists = fs.existsSync deployDir
 		catch e


Add a dist rule

Add a dist rule

This cuts down on package sizes by a decent chunk by only copying the
files we need.

Building the Firefox xpi is now done in the dist task.

Fixes #1384

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -71,12 +71,6 @@
   'browserify:firefoxVulcanized'
 ]

-taskManager.add 'build_firefox_xpi', [
-  'build_firefox'
-  'mozilla-addon-sdk'
-  'mozilla-cfx-xpi:dist'
-]
-
 # --- Testing tasks ---
 taskManager.add 'test_core', [
   'base'
@@ -120,6 +114,13 @@
 taskManager.add 'build', [
   'build_chrome'
   'build_firefox'
+]
+
+taskManager.add 'dist', [
+  'build'
+  'copy:dist'
+  'mozilla-addon-sdk'
+  'mozilla-cfx-xpi:dist'
 ]

 taskManager.add 'default', [
@@ -354,16 +355,84 @@
       # Copy releveant files for distribution.
       dist:
         files: [
-          {
-              nonull: true,
-              expand: true,
-              cwd: devBuildPath,
-              src: ['**/*',
-                    '!**/*.spec.js',
-                    '!**/*.spec.*.js',
-                    '!samples/**/*',],
-              dest: 'build/dist/',
-              onlyIf: 'modified'
+          { # Chrome extension
+            expand: true
+            cwd: chromeExtDevPath
+            src: [
+              'manifest.json'
+
+              'bower/webcomponentsjs/webcomponents.min.js'
+              'bower/polymer/polymer.js'
+
+              'generic_ui/*.html'
+              'generic_ui/polymer/vulcanized*.{html,js}'
+              '!generic_ui/polymer/vulcanized*inline.html'
+              '!generic_ui/polymer/vulcanized.js' # vulcanized.html uses vulcanized.static.js
+
+              'generic_ui/scripts/context.static.js'
+              'scripts/background.static.js'
+              '!**/*spec*'
+
+              # extra components we use
+              'generic_ui/fonts/*'
+              'generic_ui/icons/*'
+              'icons/*'
+              '_locales/**'
+            ]
+            dest: 'build/dist/chrome/extension'
+          }
+          { # Chrome app
+            expand: true
+            cwd: chromeAppDevPath
+            src: [
+              'manifest.json'
+              '*.html'
+
+              'bower/webcomponentsjs/webcomponents.min.js'
+
+              # UI for not-connected
+              'polymer/vulcanized.{html,js}'
+
+              # actual scripts that run things
+              'freedom-*/*'
+              '**/freedom-module.json'
+              '**/*.static.js'
+              '!**/*spec*'
+
+              'icons/*'
+              'fonts/*'
+              '_locales/**'
+            ]
+            dest: 'build/dist/chrome/app'
+          }
+          { # Firefox
+            expand: true
+            cwd: firefoxDevPath
+            src: [
+              'package.json'
+
+              # addon sdk scripts
+              'lib/**/*.js'
+
+              'data/freedom-*/*'
+              'data/**/freedom-module.json'
+              'data/**/*.static.js'
+              '!**/*spec*'
+
+              'data/bower/webcomponentsjs/webcomponents.min.js'
+              'data/bower/polymer/polymer.js'
+
+              'data/generic_ui/*.html'
+              'data/generic_ui/polymer/vulcanized*.{html,js}'
+              '!data/generic_ui/polymer/vulcanized*inline.html'
+              '!data/generic_ui/polymer/vulcanized.js' # vulcanized.html uses vulcanized.static.js
+
+              'data/fonts/*'
+              'data/icons/*'
+              'data/generic_ui/fonts/*'
+              'data/generic_ui/icons/*'
+            ]
+            dest: 'build/dist/firefox'
           }
         ]

@@ -646,8 +715,8 @@
       'dist':
         options:
           'mozilla-addon-sdk': 'latest'
-          extension_dir: 'build/dev/uproxy/firefox'
-          dist_dir: 'build/dist/'
+          extension_dir: 'build/dist/firefox'
+          dist_dir: 'build/dist'

     vulcanize:
       chromeExtInline:


Further abbreviating commands in generateApiDocs

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -50,9 +50,9 @@
         command: "node_modules/grunt-protractor-runner/node_modules/.bin/webdriver-manager update"
       generateApiDocs:
         command: [
-          "node_modules/.bin/docha -p '.tmp/elementHelper.spec.js' -o 'docs/elementHelper.md' -e _"
-          "node_modules/.bin/docha -p '.tmp/generalStepDefs.spec.js' -o 'docs/generalStepDefs.md' -e _"
-          "node_modules/.bin/docha -p '.tmp/transform.spec.js' -o 'docs/transform.md' -e _"
+          "docha -p '.tmp/elementHelper.spec.js' -o 'docs/elementHelper.md' -e _"
+          "docha -p '.tmp/generalStepDefs.spec.js' -o 'docs/generalStepDefs.md' -e _"
+          "docha -p '.tmp/transform.spec.js' -o 'docs/transform.md' -e _"
         ].join '&'

     connect:


Automatic update of the CHANGELOG on publish.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -30,6 +30,15 @@
             to: "'<%= pkg.version %>'"
           }
         ]
+      changelog:
+        src: ['CHANGELOG.md']
+        dest: 'CHANGELOG.md'
+        replacements: [
+          {
+          from: "## Master"
+          to: "## Version <%= pkg.version %>, <%= grunt.template.today('mmmm dd, yyyy') %>"
+          }
+        ]
     uglify:
       dist:
         files:
@@ -56,11 +65,16 @@
     'jasmine_node'
   ]

-  grunt.registerTask 'publish', [
-    'jasmine_node'
+  grunt.registerTask 'dist', 'Create the browser distribution', [
     'browserify'
     'replace:version'
     'uglify:dist'
+  ]
+
+  grunt.registerTask 'publish', 'Publish a new version', [
+    'jasmine_node'
+    'dist'
+    'replace:changelog'
     'release'
   ]


Return empty object if aws file doesn't exist

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -4,7 +4,8 @@
   version_tag = ->
     "v#{version()}"
   aws = ->
-    grunt.file.readJSON 'grunt-aws.json'
+    file = 'grunt-aws.json'
+    if grunt.file.exists file then grunt.file.readJSON file else {}

   grunt.initConfig
     pkg: grunt.file.readJSON("package.json")


Adding gruntfile into updates.

Adding gruntfile into updates.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -345,6 +345,7 @@
           cwd: chromeExtDevPath
           src: [
             'manifest.json'
+            'managed_policy_schema.json'
             '_locales/**'

             'generic_ui/scripts/copypaste.js'
@@ -358,6 +359,7 @@
           cwd: chromeAppDevPath
           src: [
             'manifest.json'
+            'managed_policy_schema.json'
             '_locales/**'

             # UI for not-connected


Clean up post url

Clean up post url

--- resources/assets/coffee/_classes/forum.coffee
+++ resources/assets/coffee/_classes/forum.coffee
@@ -204,10 +204,8 @@

   toggleDeleted: =>
-    Turbolinks.visit osu.updateQueryString null,
+    Turbolinks.visit osu.updateQueryString @postUrlN(@currentPostPosition),
       with_deleted: +!@showDeleted()
-      n: @currentPostPosition
-

   initialScrollTo: =>
     return if location.hash != '' ||
@@ -226,7 +224,12 @@

   postUrlN: (postN) ->
-    "#{document.location.pathname}?n=#{postN}"
+    url = "#{document.location.pathname}?n=#{postN}"
+
+    if @showDeleted()
+      url += "&with_deleted=#{+@showDeleted()}"
+
+    url

   showMore: (e) =>


Remove the jiggle() function

Remove the jiggle() function

--- src/panelutil.coffee
+++ src/panelutil.coffee
@@ -376,41 +376,3 @@
             high: if n[i]>0 then means[i] + m*sds[i]/Math.sqrt(n[i]) else means[i]

     ci
-
-# jiggle values horizontally in a deterministic way
-# group = vector of integers 1,2,3... defining categories
-# y = vector of responses (same length as group)
-# radius = radius of points
-# height = height of plot
-jiggle = (group, y, radius, height, width) ->
-
-    n_group = d3.max(group)
-    min_y = d3.min(y)
-    max_y = d3.max(y)
-
-    # number of bins and amounts to jitter
-    vnum = Math.ceil(height/(radius*2))
-    hamount = (radius*2)/(width/n_group)
-    vamount = (max_y - min_y)/vnum
-
-    # categorize the y's into bins
-    ycat = (Math.floor((x - min_y)/vamount) for x in y)
-
-    # count values in each group:bin via hash
-    counts = {}
-    for i of y
-        cat = "#{group[i]}:#{ycat[i]}"
-        counts[cat] = if counts[cat]? then counts[cat]+1 else 1
-
-    # amount of horizontal jitter, based on number of points within category
-    seen = {}
-    jit = []
-    for i of y
-        cat = "#{group[i]}:#{ycat[i]}"
-        seen[cat] = if seen[cat]? then seen[cat]+1 else 1
-        jit.push((seen[cat]-1)*hamount - (counts[cat]-1)*hamount/2)
-
-    # adjust y values to midpoints
-    yout = (yv * vamount + min_y for yv in ycat)
-
-    {jitter:jit, y:yout}


ConfigParser: fix include order bug

ConfigParser: fix include order bug

--- lib/configParser.coffee
+++ lib/configParser.coffee
@@ -28,7 +28,7 @@

     for includePath in config.include
       includePath = @_buildPathToInclude path, includePath
-      config = extend true, {}, @_parse(includePath, included), config
+      config = extend true, {}, config, @_parse(includePath, included)

     delete config.include


pass more array tests

pass more array tests

--- test/arrays.coffee
+++ test/arrays.coffee
@@ -89,32 +89,32 @@
     #    0
     #  ].length

-    #test 'jashkenas/coffee-script#1274: `[] = a()` compiles to `false` instead of `a()`', ->
-    #  a = false
-    #  fn = -> a = true
-    #  [] = fn()
-    #  ok a
+    test 'jashkenas/coffee-script#1274: `[] = a()` compiles to `false` instead of `a()`', ->
+      a = false
+      fn = -> a = true
+      [] = fn()
+      ok a

-    #test 'mixed shorthand objects in array lists', ->
-    #
-    #  arr = [
-    #    a:1
-    #    'b'
-    #    c:1
-    #  ]
-    #  ok arr.length is 3
-    #  ok arr[2].c is 1
-    #
-    #  arr = [b: 1, a: 2, 100]
-    #  eq arr[1], 100
-    #
-    #  arr = [a:0, b:1, (1 + 1)]
-    #  eq arr[1], 2
-    #
-    #  arr = [a:1, 'a', b:1, 'b']
-    #  eq arr.length, 4
-    #  eq arr[2].b, 1
-    #  eq arr[3], 'b'
+    test 'mixed shorthand objects in array lists', ->
+
+      arr = [
+        a:1
+        'b'
+        c:1
+      ]
+      ok arr.length is 3
+      ok arr[2].c is 1
+
+      arr = [b: 1, a: 2, 100]
+      eq arr[1], 100
+
+      arr = [a:0, b:1, (1 + 1)]
+      eq arr[1], 2
+
+      arr = [a:1, 'a', b:1, 'b']
+      eq arr.length, 4
+      eq arr[2].b, 1
+      eq arr[3], 'b'

   suite 'Splats', ->


make test.coffee use a UUID for session

make test.coffee use a UUID for session

--- test.coffee
+++ test.coffee
@@ -35,7 +35,7 @@
 header = JSON.stringify({
             msg_id: 0,
             username: "will",
-            session: 0,
+            session: "00000000-0000-0000-0000-000000000000",
             msg_type: "execute_request",
             version: "5.0"
         })


Disable testing pretro_run.

Disable testing pretro_run.

--- test.coffee
+++ test.coffee
@@ -7,7 +7,7 @@
     can_run: false
   dinothawr_libretro:
     library_name: 'Dinothawr'
-    can_run: true
+    can_run: false
 for name, info of cores
   describe "retro.getCore('#{name}')", ->
     core = null


retrieve ddp url from settings file

retrieve ddp url from settings file

--- client/main.cjsx
+++ client/main.cjsx
@@ -13,7 +13,7 @@

 WrappedWrapper = createContainer (props) ->

-  ddp = DDP.connect 'http://www.dashboard.vib.ictu'
+  ddp = DDP.connect Meteor.settings.public.ddpServer
   Meteor.remoteConnection = ddp
   Accounts.connection = ddp
   eventEmitter = new EventEmitter


Remove unused Editor import

Remove unused Editor import

--- lib/editor-proxy.coffee
+++ lib/editor-proxy.coffee
@@ -1,4 +1,4 @@
-{Editor, Point} = require 'atom'
+{Point} = require 'atom'
 emmet = require '../vendor/emmet-core'
 utils = emmet.require("utils")
 tabStops = emmet.require("tabStops")


Added failing partials test to spec

Added failing partials test to spec

--- spec/hulk.coffee
+++ spec/hulk.coffee
@@ -9,3 +9,6 @@

 		it 'should compile template', ->
 			hulk.compile('Hello {{what}}', {what:'world'})().should.eql 'Hello world'
+
+		it 'should include partials', ->
+			hulk.compile('Hello {{what}}. {{> salute}}', {what:'world'})().should.eql 'Hello world. How are you, world?'


:fire: Remove the message rendering logic from renderPanel

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -179,15 +179,11 @@
     bubble

   renderPanel: ->
-    @panel.innerHTML = ''
     @removeMarkers()
     @removeBubble()
-    if not @messages.size
-      return @setPanelVisibility(false)
-    @setPanelVisibility(true)
     activeEditor = atom.workspace.getActiveTextEditor()
     @messages.forEach (message) =>
-      if @scope is 'File' then return unless message.currentFile
+      return if @scope isnt 'Project' and not message.currentFile
       if message.currentFile and message.range #Add the decorations to the current TextEditor
         @markers.push marker = activeEditor.markBufferRange message.range, {invalidate: 'never'}
         activeEditor.decorateMarker(
@@ -196,12 +192,6 @@
         if @underlineIssues then activeEditor.decorateMarker(
           marker, type: 'highlight', class: "linter-highlight #{message.class}"
         )
-
-      if @scope is 'Line'
-        return if @lineMessages.has(message)
-
-      Element = Message.fromMessage(message, addPath: @scope is 'Project', cloneNode: true)
-      @panel.appendChild Element
     @updateBubble()

   renderPanelMessages: ->
@@ -211,6 +201,7 @@
     else
       messages = @messages
     return @setPanelVisibility(false) unless messages.size
+    @setPanelVisibility(true)
     @panel.innerHTML = ''
     messages.forEach (message) =>
       return if @scope isnt 'Project' and not message.currentFile


add args type handle.

add args type handle.

--- lib/messageProxy.coffee
+++ lib/messageProxy.coffee
@@ -9,6 +9,9 @@
         , false

     emit: (targets, message) ->
+      if arguments.length is 1
+        message = arguments[0]
+        targets = [ '*' ]
       return  unless typeof message is 'string' #todo 应支持任意obj
       if targets = _parseToArray(targets)
         window.postMessage message, target for target in targets


MockClient should extend EventEmitter

MockClient should extend EventEmitter

--- lib/mocks/client.coffee
+++ lib/mocks/client.coffee
@@ -1,4 +1,6 @@
-module.exports = class MockClient
+events = require 'events'
+
+module.exports = class MockClient extends events.EventEmitter
   constructor: ->
     @to_intercept = {}
   intercept: (job_name, job, assertions=(->)) ->


cleaning up subdivide with 4.times

cleaning up subdivide with 4.times

--- src/quad_tree.coffee
+++ src/quad_tree.coffee
@@ -114,38 +114,15 @@
         x_midpoint = x + half_width
         y_midpoint = y + half_height

-        I.nodes[TOP_LEFT] = Node
-          bounds:
-            x: x
-            y: y
-            width: half_width
-            height: half_height
-          depth: increased_depth
-
-        I.nodes[TOP_RIGHT] = Node
-          bounds:
-            x: x_midpoint
-            y: y
-            width: half_width
-            height: half_height
-          depth: increased_depth
-
-        I.nodes[BOTTOM_LEFT] = Node
-          bounds:
-            x: x
-            y: y_midpoint
-            width: half_width
-            height: half_height
-          depth: increased_depth
-
-        I.nodes[BOTTOM_RIGHT] = Node
-          bounds:
-            x: x_midpoint
-            y: y_midpoint
-            width: half_width
-            height: half_height
-          depth: increased_depth
-
+        4.times (n) ->
+          I.nodes[n] = Node
+            bounds:
+              x: (half_width * n) % width
+              y: (half_height * n) % height
+              width: half_width
+              height: half_height
+            depth: increased_depth
+
     self

   window.QuadTree = QuadTree


fix garena installation checks #63

fix garena installation checks #63

--- lib/path_manager.coffee
+++ lib/path_manager.coffee
@@ -53,21 +53,23 @@
       done new Error('Path not found'), selected_path

   else
-    garena_glob = glob.sync(path.join(selected_path, 'GameData/Apps/**/Game/Config/'))
-    garena_path = path.join(selected_path, 'League of Legends.exe')
+    # Riot
     default_path = path.join(selected_path, 'lol.launcher.exe')
+    garena_check_one = path.join(selected_path, 'lol.exe')
+    garena_check_two = glob.sync(path.join(selected_path, 'LoL*Launcher.exe'))[0]

-    # Default install, Garena Check 1
-    if fs.existsSync(default_path) or fs.existsSync(garena_path)
-      executable = if fs.existsSync(garena_path) then 'League of Legends.exe' else 'lol.launcher.exe'
-      done null, selected_path, 'Config/Champions/', executable
+    # Default install,
+    if fs.existsSync(default_path)
+      done null, selected_path, 'Config/Champions/', path.basename(garena_check_one)

-    # Garena Installation Check 2
-    else if fs.existsSync(path.join(selected_path, 'LoLLauncher.exe')) and garena_glob
-      executable = 'LoLLauncher.exe'
-      garena_split = garena_glob[0].split('/')
-      garena_version = garena_split[garena_split.length - 4]
-      done null, selected_path, "GameData/Apps/#{garena_version}/Game/Config/Champions/", executable
+    # Garena Check 1
+    else if fs.existsSync(garena_check_one)
+      done null, selected_path, 'Game/Config/Champions/', path.basename(garena_check_one)
+
+    # Garena Check 2
+    else if garena_check_two
+      garena_version = path.basename glob.sync(path.join(selected_path, 'GameData/Apps/*'))[0]
+      done null, selected_path, "GameData/Apps/#{garena_version}/Game/Config/Champions/", path.basename(garena_check_two)

     else
       done new Error('Path not found'), selected_path


early return in fromBinder

early return in fromBinder

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -20,6 +20,7 @@
         if reply == Bacon.noMore or event.isEnd()
           # defer if binder calls handler in sync before returning unbinder
           if unbinder? then unbinder() else Bacon.scheduler.setTimeout (-> unbinder()), 0
+          return reply
       reply

 # eventTransformer - defaults to returning the first argument to handler


rename

rename

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -253,7 +253,7 @@
       unsubBoth

   takeUntil: (stopper) =>
-    new EventStream(takeUntilSub(this, stopper))
+    new EventStream(takeUntilSubscribe(this, stopper))

   toProperty: (initValue) ->
    @scan(initValue, latter)
@@ -361,7 +361,7 @@
         sink(event)
       else
         Bacon.more
-  takeUntil: (stopper) => new Property(takeUntilSub(this, stopper))
+  takeUntil: (stopper) => new Property(takeUntilSubscribe(this, stopper))
   changes: => new EventStream (sink) =>
     @subscribe (event) =>
       sink event unless event.isInitial()
@@ -438,7 +438,7 @@
 Bacon.Next = Next
 Bacon.End = End

-takeUntilSub = (src, stopper) ->
+takeUntilSubscribe = (src, stopper) ->
   (sink) ->
     unsubSrc = nop
     unsubStopper = nop


smarter and simpler dep caching

smarter and simpler dep caching

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -997,29 +997,24 @@
         [x.obs]
       else
         []
-    depCache = {}
-    dependsOnCached = (b) ->
-      cached = depCache[b.id]
-      if (!cached?)
-        cached = dependsOn.call(this, b)
-        depCache[b.id] = cached
-      cached
+    flatDeps = null
+
+    collectDeps = (o) ->
+      deps = o.internalDeps()
+      _.each deps, (i, dep) ->
+        flatDeps[dep.id] = true
+        collectDeps(dep)

     dependsOn = (b) ->
-      if this == b
-        return false
-      deps = this.internalDeps()
-      for dep in deps
-        if dep == b
-          return true
-        if dep.dependsOn(dep)
-          return true
-      return false
+      if !flatDeps?
+        flatDeps = {}
+        collectDeps this
+      return flatDeps[b.id]

     @apply = (obs) ->
       deps = _.cached (-> findDeps([context].concat(args)))
       obs.internalDeps = obs.internalDeps || deps
-      obs.dependsOn = dependsOnCached
+      obs.dependsOn = dependsOn
       obs.deps = deps
       obs.toString = -> _.toString(context) + "." + _.toString(method) + "(" + _.map(_.toString, args) + ")"
       obs.desc = -> { context, method, args }


Vmath extra: Add vec3.fixAxis.

Vmath extra: Add vec3.fixAxis.

--- engine/math_utils/vmath_extra.coffee
+++ engine/math_utils/vmath_extra.coffee
@@ -243,4 +243,16 @@
             rotateZ out, out, z
     return out

+vmath.vec3.fixAxes = (x_axis, y_axis, z_axis, main_axis, secondary_axis)->
+    third_axis = 3 - (main_axis + secondary_axis)
+    axes = [x_axis, y_axis, z_axis]
+    main = axes[main_axis]
+    vec3.normalize main, main
+    third = axes[third_axis]
+    vec3.cross third, axes[(third_axis+1)%3], axes[(third_axis+2)%3]
+    vec3.normalize third, third
+    secondary = axes[secondary_axis]
+    vec3.cross secondary, axes[(secondary_axis+1)%3], axes[(secondary_axis+2)%3]
+    return
+
 module.exports = vmath


Use w32 builds always.

Use w32 builds always.

--- retro.coffee
+++ retro.coffee
@@ -48,10 +48,7 @@
     else
       platform = switch process.platform
         when 'win32'
-          if process.arch is 'ia32'
-            'win-x86'
-          else
-            'win-x86_64_w32'
+          'win-x86_64_w32'
         when 'darwin'
           if process.arch is 'ia32'
             'osx-x86'


step 2

step 2

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -1080,16 +1080,10 @@

     that = @

-    collectDeps = (o) ->
-      deps = o.internalDeps()
-      for dep in deps
-        that.flatDeps[dep.id] = true
-        collectDeps(dep)
-
     dependsOn = (b) ->
       if !that.flatDeps?
         that.flatDeps = {}
-        collectDeps this
+        that.collectDeps this
       return that.flatDeps[b.id]

     @apply = (obs) ->
@@ -1101,6 +1095,12 @@
       obs.inspect = -> obs.toString()
       obs.desc = -> { @context, @method, @args }
       obs
+
+  collectDeps: (o) ->
+    deps = o.internalDeps()
+    for dep in deps
+      @flatDeps[dep.id] = true
+      @collectDeps(dep)

   findDeps: (x) ->
     if isArray(x)


Refactored hooks to merge given arguments with defaults. Added method for adding hooks and parsing formToModifier options. Prevented using formToModifier incorrectly if inserting. Fixed bug with formType preventing submit.

--- src/Forms.coffee
+++ src/Forms.coffee
@@ -14,13 +14,13 @@
     # HOOKS
     ################################################################################################

-    AutoForm.addHooks name,
+    hooks =
       # Settings should be passed to the autoForm helper to ensure they are available in these
       # callbacks.
       onSubmit: (insertDoc, updateDoc, currentDoc) ->
         args = arguments
         template = getTemplate(@template)
-        onSubmit = formArgs.onSubmit ? formArgs.hooks?.onSubmit
+        onSubmit = formArgs.onSubmit
         result = onSubmit?.apply(@, args)
         callback = => template.settings.onSubmit?.apply(@, args)
         deferCallback(result, callback)
@@ -38,7 +38,7 @@
         template = getTemplate(@template)
         Form.updateDocs(template)
         Form.setUpDocs(template)
-        onSuccess = formArgs.onSuccess ? formArgs.hooks?.onSuccess
+        onSuccess = formArgs.onSuccess
         successResult = onSuccess?.apply(@, args)
         callback = => template.settings.onSuccess?.apply(@, args)
         deferCallback(successResult, callback)
@@ -65,7 +65,7 @@
         unless formArgs.loggerNotify == false || settings.loggerNotify == false
           Logger.error(error.message)
         Logger.error('Form error', operation, error, template, {notify: false})
-        onError = formArgs.onError ? formArgs.hooks?.onError
+        onError = formArgs.onError
         onError?.apply(@, args)
         settings.onError?.apply(@, args)

@@ -79,14 +79,29 @@
         template.isSubmitting = false
         Form.setSubmitButtonDisabled(false, template)

-    hooks = formArgs.hooks
-    if hooks?
+    Form.addHooks = ->
+      if arguments.length == 1
+        formId = name
+        hooks = arguments[0]
+      else if arguments.length == 2
+        formId = arguments[0]
+        hooks = arguments[1]
+      else
+        throw new Error('Invalid arguments')
+
       formToDoc = hooks.formToDoc
       # If `formToDocOnUpdate` is true, the `formToDoc` hook is used for both inserts and updates
       # (as in AutoForm < v5).
       if hooks.formToDocOnUpdate == true && formToDoc?
         hooks.formToModifier ?= (modifier) ->
-          doc = Collections.simulateModifierUpdate(@template.data.doc, modifier)
+          # If no document can be found, then this hook was fired when formType=null and no
+          # doc existed. Hence an empty doc should be used to ensure the modifier can still be
+          # applied and passed to formToDoc. NOTE: formToDoc would also have been called, but
+          # we should not pass this modifier since it may not match the output of formToDoc if
+          # changes were made. Use a temporary ID to ensure simulateModifierUpdate() can be called.
+          doc = @template.data.doc ? {_id: 'tmp'}
+          doc = Collections.simulateModifierUpdate(doc, modifier)
+          delete doc._id
           doc = formToDoc.call(@, doc)
           modifier.$set = Objects.flattenProperties(doc)
           # Ensure keys in $set are not present in $unset.
@@ -94,7 +109,10 @@
             _.each modifier.$set, (value, key) -> delete modifier.$unset[key]
           delete modifier.$set._id
           modifier
-      AutoForm.addHooks name, hooks
+      AutoForm.addHooks formId, hooks
+
+    Setter.merge hooks, formArgs.hooks
+    Form.addHooks(hooks)

     ################################################################################################
     # HELPERS
@@ -118,7 +136,7 @@
         type = Form.getTemplate().settings.formType
         # Allow passing type = null to trigger onSubmit() hook.
         if type == undefined then type = formArgs.type
-        if type == undefined then type = if doc then 'update' else 'insert'
+        if type == undefined then type = (if doc then 'update' else 'insert')
         type
       submitText: -> if Form.getDocs().length > 0 then 'Save' else 'Create'
       hasDoc: -> Form.hasDoc()


Removed Setter.clone() and replaced with _.extend({}, ...)

--- src/Forms.coffee
+++ src/Forms.coffee
@@ -10,7 +10,7 @@
     unless Form
       throw new Error 'No template defined with name ' + name

-    Form.getSettings = -> Setter.clone(formArgs)
+    Form.getSettings = -> _.extend({}, formArgs)

     ################################################################################################
     # HOOKS


Add seed argument; use seedrandom if seed but not prng provided.

--- src/stochator.coffee
+++ src/stochator.coffee
@@ -136,7 +136,11 @@

 createGenerator = (config) ->
     kind = config.kind or "float"
-    prng = config.prng or Math.random
+
+    defaultPrng = if config.seed then seedrandom else Math.random
+    basePrng = config.prng or defaultPrng
+    prng = if config.seed then basePrng(config.seed) else basePrng
+
     generator = switch kind
         when "float"
             { min, max, mean, stdev } = config


Corrected bug in test causing @malko's del() bug fix to fail the tests

--- test/trello.coffee
+++ test/trello.coffee
@@ -28,7 +28,7 @@
       request.put = (options) =>
         @request = options: options
         return new process.EventEmitter()
-      request.delete = (options) =>
+      request.del = (options) =>
         @request = options: options
         return new process.EventEmitter()
       request.verb = (options) =>


Fix the server-side render skip

Fix the server-side render skip

--- server/http/middleware/angular.coffee
+++ server/http/middleware/angular.coffee
@@ -146,7 +146,7 @@
 		body = req.body

 		# Uncomment to bypass the server-side Angular.
-#		return http.renderApp(locals).done (index) -> res.end index
+#		return http.renderApp locals, (error, index) -> res.end index

 		extractCookie = (fn) ->


Update only submitting problem

Update only submitting problem

--- server/testSystems/Informatics.coffee
+++ server/testSystems/Informatics.coffee
@@ -195,7 +195,7 @@
                     informaticsData = await informaticsUser.submit(informaticsProblemId, contentType, data)
                     await sleep(1000)
                 finally
-                    await downloadSubmits.runForUser(user.informaticsId, 5, 1)
+                    await downloadSubmits.runForUserAndProblem(user.informaticsId, problemId)
             catch e
                 logger.info "Error submitting #{user.username}, #{user.informaticsId} #{problemId} attempt #{i}: ", e.message
             newSubmits = await Submit.findByUserAndProblem(user.informaticsId, problemId)


:memo: Add priority recommendation for variable-sized panels

Applies to #4834

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -668,6 +668,16 @@

   ###
   Section: Panels
+
+  Panels are used to display UI related to an editor window. They are placed at one of the four
+  edges of the window: left, right, top or bottom. If there are multiple panels on the same window
+  edge they are stacked in order of priority: higher priority is closer to the center, lower
+  priority towards the edge.
+
+  *Note:* If your panel changes its size throughout its lifetime, consider giving it a higher
+  priority, allowing fixed size panels to be closer to the edge. This allows control targets to
+  remain more static for easier targeting by users that employ mice or trackpads. (See
+  [atom/atom#4834](https://github.com/atom/atom/issues/4834) for discussion.)
   ###

   # Essential: Get an {Array} of all the panel items at the bottom of the editor window.


Require grim

Require grim

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -6,6 +6,7 @@
 Serializable = require 'serializable'
 Delegator = require 'delegato'
 {Emitter, Disposable} = require 'event-kit'
+Grim = require 'grim'
 TextEditor = require './text-editor'
 PaneContainer = require './pane-container'
 Pane = require './pane'


Add Opera 16 data

Add Opera 16 data


Updated Atom config

Updated Atom config

--- atom/.atom/config.cson
+++ atom/.atom/config.cson
@@ -1,10 +1,10 @@
-'global':
-  'exception-reporting':
-    'userId': '953557f2-9a4c-a351-83b6-8f7fbcd89dda'
-  'welcome':
-    'showOnStartup': false
-  'metrics':
-    'userId': '8f9811db1d5fbc710de3a1979f72464717933232'
-  'core': {}
-  'editor':
-    'invisibles': {}
+"*":
+  "exception-reporting":
+    userId: "953557f2-9a4c-a351-83b6-8f7fbcd89dda"
+  welcome:
+    showOnStartup: false
+  metrics:
+    userId: "8f9811db1d5fbc710de3a1979f72464717933232"
+  core: {}
+  editor:
+    invisibles: {}


KodingKey: fix styling

KodingKey: fix styling

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -108,9 +108,7 @@
   {key} = req.params

   {JKodingKey} = koding.models
-  JKodingKey.checkKey
-    key     : key
-  , (err, status)=>
+  JKodingKey.checkKey {key}, (err, status)=>
     return res.send 401, authTemplate "Key doesn't exist" unless status
     res.send 200, {result: 'key is added successfully'}


Fix profile avatar component: Don't render until profile fetch has been attempted

--- lib/assets/javascripts/views/profile_component/avatar.js.coffee
+++ lib/assets/javascripts/views/profile_component/avatar.js.coffee
@@ -6,8 +6,6 @@
     super

     @on 'ready', @checkImageMortality
-
-    @render()

   checkImageMortality: =>
     img = Marbles.DOM.querySelector('img', @el)


a touch of safety from trailing catches falling off the end of the rewriter.

--- rewriter.coffee
+++ rewriter.coffee
@@ -187,7 +187,7 @@
         @tokens.splice i, 0, @indentation(token)...
         return 2
       if token[0] is 'CATCH' and
-          (@tokens[i + 2][0] is 'TERMINATOR' or @tokens[i + 2][0] is 'FINALLY')
+          (@tag(i + 2) is 'TERMINATOR' or @tag(i + 2) is 'FINALLY')
         @tokens.splice i + 2, 0, @indentation(token)...
         return 4
       if include(SINGLE_LINERS, token[0]) and @tag(i + 1) isnt 'INDENT' and