Edit basic javascript for posting suggestions

--- site/js/script.coffee
+++ site/js/script.coffee
@@ -1,6 +1,7 @@
----
----
-api_key = "891c04bb5d3fea14d398fa7d4abd7ae55594f9b8+\\"
+#---
+#---
+
+api_key = "+\\"

 url = "https://wiki.snc.io/w/api.php"

@@ -21,16 +22,16 @@

 getSectionTitle = () -> getDateString() + "-" + getGoogleAnalyticsId()

-settings = (text) ->
+settings = (type, text) ->
   action: "edit"
   format: "json"
   pageid: 9
   section: "new"
-  sectiontitle: getSectionTitle()
+  sectiontitle: type + "-" + getSectionTitle()
   text: text
   summary: "Input from website"
   bot: ""
   token: api_key

-window.post_suggestion = (suggestion_text) ->
-  $.post(url, settings(suggestion_text))
+window.post_suggestion = (type, text) ->
+  $.post(url, settings(type, text))


add the metadata builder

add the metadata builder

--- lib/helpers/metadata_builder.coffee
+++ lib/helpers/metadata_builder.coffee
@@ -5,7 +5,8 @@
   ArgumentNullException = if isInBrowser then NS.Helpers.Exceptions.ArgumentNullException else require("./custom_exception").ArgumentNullException
   # Get the domains definition.
   class MetadataBuilder
-    constructor: (options)->
+    #Initialize the dependencies from the project: the domains and the metadatas.
+    initialize: (options)->
       throw new ArgumentNullException('The metadata builder needs options with domains and metadatas.') if not options?
       throw new ArgumentNullException('The metadata builder needs domains in options.') if not options.domains?
       throw new ArgumentNullException('The metadata builder needs metadatas in options.') if not options.metadatas?
@@ -18,10 +19,7 @@
       #console.log('called')
       return new ArgumentNullException('The model should exists and have a metadatas property.') if not model?
       #Get the metadatas from the model.
-      metadatas = model.metadatas
-      throw new ArgumentNullException('The model should have metadatas.') if (not metadatas?) #or (not tryConstructModelMetaDatasFromModelName(model)?)
-      if (not metadatas?)
-        metadatas = @constructModelMetaDatas()
+      metadatas = @getMetadatas(model)
       #Container for the validation rules of the domain of each property.
       valDomAttrs = {}
       # Lopping through all attributes of th model in order to build their validators.
@@ -30,10 +28,10 @@
         metadata = {}
         md = metadatas[attr] or {}
         if (md.isValidationOff? and md.isValidationOff is false) or (not md.isValidationOff?)
-         _.extend(metadata, md.metadata) if md.metadata?
+         ###_.extend(metadata, md.metadata) if md.metadata?
          (metadata.domain = md.domain) if md.domain?
          (metadata.required = md.required) if md.required?
-
+         ###
          #Container for the validators.
          validators = [];
          #If the required filed is set, add a validator
@@ -43,8 +41,47 @@
          # Set the validators inide the container associated with the field.
          valDomAttrs[attr] = validators;
       return valDomAttrs
-    constructModelMetaDatas: (model)->
-      return require(model.modelName)  if model.modelName?
+    getMetadatas: (model)->
+      # Construct the metadatas obtained with the model name.
+      entityMetadatas = @constructEntityMetaDatas(model)
+      metadatas = _.clone(entityMetadatas)
+      # If the models has metadatas the metadatas given by its definitions will extend them.
+      if model.metadatas?
+        for mdlMetadataAttr in model.metadatas
+          entityAttrMetadata = entityMetadatas[mdlMetadataAttr] # Get the introspected metadatas.
+          mdlMetadata = model.metadatas[mdlMetadataAttr]
+          metadata = {} # Create a container for the metadatas.
+          _.extend(metadata,  entityAttrMetadata) # Inject into metadata the entitydefinitions metadata attributes.
+          _.extend(metadata, _.omit(@domains[metadata.domain], 'validators')) #override the metadata with the metadata inside the domain style, validators,....
+          if mdlMetadata?
+            _.extend(metadata, mdlMetadata.metadata)  if mdlMetadata.metadata?
+            # Extend the "overriden" metadatas
+            # Property that can be overriden: required, label, domain, isValidationOff
+            overridenProperties = {}
+            if mdlMetadata.domain?
+              _.extend(overridenProperties, {domain: mdlMetadata.domain}) # Change the domain.
+              _.extend(overridenProperties, _.omit(@domains[mdlMetadata.domain], 'validators')) # Change the metadatas of the domain.
+            _.extend(overridenProperties, {required: mdlMetadata.required}) if mdlMetadata.required?
+            _.extend(overridenProperties, {label: mdlMetadata.label}) if mdlMetadata.label?
+            _.extend(overridenProperties, {isValidationOff: mdlMetadata.isValidationOff}) if mdlMetadata.isValidationOff? #Turn off the model validations.
+            _.extend(overridenProperties, {style: mdlMetadata.style}) if mdlMetadata.style?
+            _.extend(overridenProperties, {decorator: mdlMetadata.decorator}) if mdlMetadata.decorator?
+            # If at least one property has been defined.
+            _.extend(metadata, overridenProperties) if _.isEmpty(overridenProperties)
+          #Update the global metadatas<
+          metadatas[mdlMetadataAttr] = metadata;
+      return metadatas
+
+    constructEntityMetaDatas: (model)->
+      if model.modelName?
+        if @metadatas[model.modelName]?
+          return @metadatas[model.modelName]
+        else
+          console.warn("The metadatas does not have properties for this model name.")
+          return {}
+      else
+        throw new ArgumentNullException('The model sould have a model name in order to build its metadatas')
+
     proxyDomainValidationAttrs: (model)->
       return getDomainsValidationAttrs(model)
       return proxyValidationContainer[model.modelName] if(model.modelName? and proxyValidationContainer[model.modelName]?)
@@ -55,6 +92,7 @@
   if isInBrowser
     NS.Helpers = NS.Helpers or {}
     NS.Helpers.MetadataBuilder = MetadataBuilder
+    NS.Helpers.metadataBuilder = new MetadataBuilder();
   else
-    module.exports = MetadataBuilder
+    module.exports = {MetadataBuilder: MetadataBuilder, metadataBuilder: new MetadataBuilder()}
 )(if typeof module is 'undefined' and typeof window isnt 'undefined' then window.Fmk else module.exports)


feat(client/send): add deferred for send function


logger: Making it possible to replace default tags


--- site.coffee
+++ site.coffee
@@ -27,7 +27,7 @@
   document.getElementById('description').innerHTML = "<i>This video does not have a description.</i>"
   document.getElementById('permalink').href = data.basedir + '/' + webm.file_name

-  window.location.hash = encodeURI webm.file_name.replace(/\.webm$/, '')
+  window.location.hash = encodeURIComponent webm.file_name.replace(/\.webm$/, '')
   videoplayer.src = data.basedir + '/' + webm.file_name
   videoplayer.loop = true
   videoplayer.controls = true
@@ -44,7 +44,7 @@

 playWebMByFileName = (fileName, event = null) ->
   event.preventDefault() if event?
-  webm = (video for video in data.videos when video.file_name is decodeURI(fileName))
+  webm = (video for video in data.videos when video.file_name is decodeURIComponent(fileName))
   if webm.length == 0
     console.log "WebM not found: #{fileName}"
     return playRandomWebM()


Added early return statement in callback when GitHub API call fails.

--- lib/github-utils.coffee
+++ lib/github-utils.coffee
@@ -36,7 +36,7 @@
         user: repoOwner
         repo: repoName
       , (err, pullRequests) ->
-        console.err err if err
+        return console.error err if err
         urls = getPullRequestURLs pullRequests, branch
         urls.forEach (url) ->
           Shell.openExternal url if url


Fix typos @getDocument/@getDocuments

Fix typos @getDocument/@getDocuments

--- src/controller.coffee
+++ src/controller.coffee
@@ -436,8 +436,6 @@
       noText: (opts.noText ? false)
     }

-    console.log rect
-
     # Draw the new tree on the main context
     layoutResult = @view.getViewNodeFor(@tree).layout 0, @nubbyHeight
     @view.getViewNodeFor(@tree).draw @mainCtx, rect, options
@@ -705,7 +703,7 @@
     if operation instanceof FloatingOperation
       @performFloatingOperation(operation, 'backward')
     else
-      @getDocuments(operation.document).perform(
+      @getDocument(operation.document).perform(
         operation.operation, 'backward', (if operation.document is @cursor.document then [@cursor.location] else [])
       ) unless operation is 'CAPTURE'

@@ -737,7 +735,7 @@
     if operation instanceof FloatingOperation
       @performFloatingOperation(operation, 'forward')
     else
-      @getDocuments(operation.document).perform(
+      @getDocument(operation.document).perform(
         operation.operation, 'forward', (if operation.document is @cursor.document then [@cursor.location] else [])
       ) unless operation is 'CAPTURE'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


spec: Don't reuse port in session spec

spec: Don't reuse port in session spec

--- spec/api-session-spec.coffee
+++ spec/api-session-spec.coffee
@@ -20,8 +20,7 @@
       res.end('finished')
       server.close()

-    port = remote.process.port
-    server.listen port, '127.0.0.1', ->
+    server.listen 0, '127.0.0.1', ->
       {port} = server.address()
       w.loadUrl "#{url}:#{port}"
       w.webContents.on 'did-finish-load', ->


Whitespace trim hack

Whitespace trim hack

--- src/controller.coffee
+++ src/controller.coffee
@@ -2242,9 +2242,21 @@
       editor.tree = @newValue.clone()

       return editor.tree.start
+
+  # Whitespace trimming hack enable/disable
+  # setter
+  hook 'populate', 0, ->
+    @trimWhitespace = false
+
+  Editor::setTrimWhitespace = (trimWhitespace) ->
+    @trimWhitespace = trimWhitespace

   Editor::setValue = (value) ->
     try
+      # Whitespace trimming hack to account
+      # for ACE editor extra line in some applications
+      if @trimWhitespace then value = value.trim()
+
       newParse = coffee.parse value

       if value isnt @tree.stringify()
@@ -2304,7 +2316,7 @@

       @resize()

-  # DRAG CANVS SHOW/HIDE HACK
+  # DRAG CANVAS SHOW/HIDE HACK
   # ================================

   # On mousedown, bring the drag


Make prettier errors above the form

Make prettier errors above the form

--- app/assets/javascripts/hyper_admin/angularjs/controllers/form_ctrl.js.coffee
+++ app/assets/javascripts/hyper_admin/angularjs/controllers/form_ctrl.js.coffee
@@ -24,9 +24,19 @@
       _.remove $scope.resourceClass.attributes, (attr) ->
         attr.key == key

+    prettifyErrors = (errors) ->
+      result = { }
+      for own attr of errors
+        attrSchema = _.find $scope.resourceClass.attributes, (attribute) =>
+          attribute.key == attr
+
+        result[attrSchema.human] = errors[attr]
+
+      result
+
     @submit = =>
       onError = (response) =>
-        @errors = response.data
+        @errors = prettifyErrors response.data

       onSuccess = (resource) =>
         $state.go "^.show", id: resource.id


open or closed empty

open or closed empty

--- test/issues.coffee
+++ test/issues.coffee
@@ -21,4 +21,34 @@
             assert.equal called, 2
             assert.equal open.length, 0
             assert.equal closed.length, 0
+            done.call null
+
+    'open empty': (done) ->
+        called = 0
+        req.all_issues = (opts, cb) ->
+            called += 1
+            cb null, if called is 1 then [] else [
+                { number: 1 }
+            ]
+
+        issues.get_all {}, (err, [ open, closed ]) ->
+            assert.ifError err
+            assert.equal called, 2
+            assert.equal open.length, 0
+            assert.equal closed.length, 1
+            done.call null
+
+    'closed empty': (done) ->
+        called = 0
+        req.all_issues = (opts, cb) ->
+            called += 1
+            cb null, if called is 2 then [] else [
+                { number: 1 }
+            ]
+
+        issues.get_all {}, (err, [ open, closed ]) ->
+            assert.ifError err
+            assert.equal called, 2
+            assert.equal open.length, 1
+            assert.equal closed.length, 0
             done.call null


Access the command name at file lint.

Access the command name at file lint.

This fixes load issues on case-incensitive systems like OS X.
Fixes AtomLinter/linter-clang#16.

--- lib/linter-clang.coffee
+++ lib/linter-clang.coffee
@@ -14,8 +14,12 @@
   # containing the command line (with arguments) used to lint.
   cmd: '-fsyntax-only -fno-caret-diagnostics'
   isCpp: false
+  clangCmd: null
+  clangPlusPlusCmd: null

   executablePath: null
+
+  editor: null

   linterName: 'clang'

@@ -32,6 +36,13 @@
     tmp = @cmd

     if @isCpp
+      @clangPlusPlusCmd = atom.config.get 'linter-clang.clangPlusPlusCommand'
+      @cmd = "#{@clangPlusPlusCmd} #{@cmd} -x c++ -std=c++11 -fcxx-exceptions"
+    else
+      @clangCmd = atom.config.get 'linger-clang.clangCommand'
+      @cmd = "#{@clangCmd} #{@cmd} -x c -std=c11 -fexceptions"
+
+    if @isCpp
       @cmd += ' ' + atom.config.get 'linter-clang.clangDefaultCppFlags'
     else
       @cmd += ' ' + atom.config.get 'linter-clang.clangDefaultCFlags'
@@ -41,8 +52,8 @@
     # read other include paths from file in project
     filename = atom.project.getPaths()[0] + '/.linter-clang-includes'
     if fs.existsSync filename
-        file = fs.readFileSync filename, 'utf8'
-        includepaths = "#{includepaths} #{file.replace('\n', ' ')}"
+      file = fs.readFileSync filename, 'utf8'
+      includepaths = "#{includepaths} #{file.replace('\n', ' ')}"

     split = includepaths.split " "

@@ -91,16 +102,16 @@
     @cmd = tmp;

   constructor: (editor) ->
-    super(editor)
+    @editor = editor
+
     if editor.getGrammar().name == 'C++'
-      @cmd = "#{atom.config.get 'linter-clang.clangPlusPlusCommand'} "  + @cmd + ' -x c++ -std=c++11 -fcxx-exceptions'
       @grammar = '+'
       @isCpp = true
     if editor.getGrammar().name == 'C'
-      @cmd = "#{atom.config.get 'linter-clang.clangCommand'} " + @cmd + ' -x c -std=c11 -fexceptions'
+      @isCpp = false
       @grammar = 'c'

-    # @cmd += ' ' + ClangFlags.getClangFlags(editor.getPath()).join ' '
+    super(editor)

     atom.config.observe 'linter-clang.clangExecutablePath', =>
       @executablePath = atom.config.get 'linter-clang.clangExecutablePath'


remove describe.only

remove describe.only

--- spec/interpreter_spec.coffee
+++ spec/interpreter_spec.coffee
@@ -1,4 +1,4 @@
-describe.only 'SwanKiosk.Interpreter', ->
+describe 'SwanKiosk.Interpreter', ->
   interpreter = SwanKiosk.Interpreter

   it 'creates an empty json object', ->


Uses the outlet property to retrieve the minimap scroll view

--- lib/minimap-view.coffee
+++ lib/minimap-view.coffee
@@ -86,7 +86,7 @@
     unless @paneView.find('.minimap').length
       @miniEditorView = new MinimapEditorView(@editorView)
       @miniEditorView.css width: @scrollView.width()
-      @miniScrollView = @miniEditorView.find('.scroll-view')
+      @miniScrollView = @miniEditorView.scrollView
       @miniOverlayer.before(@miniEditorView)
       @paneView.addClass('with-minimap').append(this)


Removes logs

Removes logs

--- lib/minimap-view.coffee
+++ lib/minimap-view.coffee
@@ -161,7 +161,6 @@
       minimapMaxScroll = (minimapHeight - scrollViewHeight) * @scaleY
       overlayerScroll = overlayY / (editorLinesHeight - scrollViewHeight)
       minimapScroll = -overlayerScroll * minimapMaxScroll
-      console.log minimapHeight, overlayY, overlayY + scrollViewHeight

       @miniScrollView.data('top', minimapScroll)
       @transform @minimapWrapper[0], @minimapScale + @translateY(minimapScroll)


:fire: unused

:fire: unused

--- lib/mode-manager.coffee
+++ lib/mode-manager.coffee
@@ -161,16 +161,15 @@
           bs.restoreCharacterwise()
         @vimState.clearBlockwiseSelections()

-  normalizeSelections: ({preservePreviousSelection, resetProperties}={}) ->
+  normalizeSelections: ({preservePreviousSelection}) ->
     preservePreviousSelection ?= false
-    resetProperties ?= true

     if preservePreviousSelection
       range = @editor.getLastSelection().getBufferRange()
       @vimState.mark.setRange('<', '>', range)
     @selectCharacterwise()

-    swrap.resetProperties(@editor) if resetProperties
+    swrap.resetProperties(@editor)

     if preservePreviousSelection and not @editor.getLastSelection().isEmpty()
       @preservePreviousSelection(@editor.getLastSelection())


curvechart: fix indentation

curvechart: fix indentation

--- src/curvechart.coffee
+++ src/curvechart.coffee
@@ -23,124 +23,124 @@
                                  # x and y both ragged arrays indexed as y[subject][response_index]
                                  # if x has one subject, y's should all have same length, and x is then expanded to match

-            x = data.x
-            y = data.y
+        x = data.x
+        y = data.y

-            n_ind = y.length
-            if x.length == 1 # expand to same length
-                for j in [2..n_ind]
-                    x.push(x[0])
-            if(x.length != n_ind)
-                displayError("data.x.length (#{x.length}) != data.y.length (#{n_ind})")
+        n_ind = y.length
+        if x.length == 1 # expand to same length
+            for j in [2..n_ind]
+                x.push(x[0])
+        if(x.length != n_ind)
+            displayError("data.x.length (#{x.length}) != data.y.length (#{n_ind})")

-            # grab indID if it's there
-            # if no indID, create a vector of them
-            indID = data?.indID ? [1..n_ind]
-            if(indID.length != n_ind)
-                displayError("data.indID.length (#{indID.length}) != data.y.length (#{n_ind})")
+        # grab indID if it's there
+        # if no indID, create a vector of them
+        indID = data?.indID ? [1..n_ind]
+        if(indID.length != n_ind)
+            displayError("data.indID.length (#{indID.length}) != data.y.length (#{n_ind})")

-            # groups of colors
-            group = data?.group ? (1 for i of y)
-            ngroup = d3.max(group)
-            group = (g-1 for g in group) # changed from (1,2,3,...) to (0,1,2,...)
-            if sumArray(g < 0 or g > ngroup-1 for g in group) > 0
-                displayError("group values out of range")
-                console.log("groups:")
-                console.log(g)
-            if(group.length != n_ind)
-                displayError("data.group.length (#{group.length}) != data.y.length (#{n_ind})")
+        # groups of colors
+        group = data?.group ? (1 for i of y)
+        ngroup = d3.max(group)
+        group = (g-1 for g in group) # changed from (1,2,3,...) to (0,1,2,...)
+        if sumArray(g < 0 or g > ngroup-1 for g in group) > 0
+            displayError("group values out of range")
+            console.log("groups:")
+            console.log(g)
+        if(group.length != n_ind)
+            displayError("data.group.length (#{group.length}) != data.y.length (#{n_ind})")

-            # check that x's and y's are all of the same length
-            for i of y
-                if(x[i].length != y[i].length)
-                    displayError("length(x) (#{x[i].length}) != length(y) (#{y[i].length}) for individual #{indID[i]} (index #{i+1})")
+        # check that x's and y's are all of the same length
+        for i of y
+            if(x[i].length != y[i].length)
+                displayError("length(x) (#{x[i].length}) != length(y) (#{y[i].length}) for individual #{indID[i]} (index #{i+1})")

-            # default light stroke colors
-            strokecolor = strokecolor ? selectGroupColors(ngroup, "pastel")
-            strokecolor = expand2vector(strokecolor, ngroup)
+        # default light stroke colors
+        strokecolor = strokecolor ? selectGroupColors(ngroup, "pastel")
+        strokecolor = expand2vector(strokecolor, ngroup)

-            # default dark stroke colors
-            strokecolorhilit = strokecolorhilit ? selectGroupColors(ngroup, "dark")
-            strokecolorhilit = expand2vector(strokecolorhilit, ngroup)
+        # default dark stroke colors
+        strokecolorhilit = strokecolorhilit ? selectGroupColors(ngroup, "dark")
+        strokecolorhilit = expand2vector(strokecolorhilit, ngroup)

-            # x- and y-axis limits
-            xlim = xlim ? matrixExtent(x)
-            ylim = ylim ? matrixExtent(y)
-            chartOpts.xlim = xlim
-            chartOpts.ylim = ylim
+        # x- and y-axis limits
+        xlim = xlim ? matrixExtent(x)
+        ylim = ylim ? matrixExtent(y)
+        chartOpts.xlim = xlim
+        chartOpts.ylim = ylim

-            # convert NAs to null
-            for i of x
-                x[i] = missing2null(x[i])
-                y[i] = missing2null(y[i])
+        # convert NAs to null
+        for i of x
+            x[i] = missing2null(x[i])
+            y[i] = missing2null(y[i])

-            # reorganize data
-            dataByPoint = []
-            for i of y
-                dataByPoint.push({x:x[i][j], y:y[i][j]} for j of y[i] when x[i][j]? and y[i][j]?)
+        # reorganize data
+        dataByPoint = []
+        for i of y
+            dataByPoint.push({x:x[i][j], y:y[i][j]} for j of y[i] when x[i][j]? and y[i][j]?)

-            # don't allow NA boxes
-            chartOpts.xNA = false
-            chartOpts.yNA = false
+        # don't allow NA boxes
+        chartOpts.xNA = false
+        chartOpts.yNA = false

-            # set up frame
-            myframe = panelframe(chartOpts)
+        # set up frame
+        myframe = panelframe(chartOpts)

-            # create SVG
-            myframe(selection)
-            svg = myframe.svg()
+        # create SVG
+        myframe(selection)
+        svg = myframe.svg()

-            # scales
-            xscale = myframe.xscale()
-            yscale = myframe.yscale()
+        # scales
+        xscale = myframe.xscale()
+        yscale = myframe.yscale()

-            indtip = d3.tip()
-                       .attr('class', "d3-tip #{tipclass}")
-                       .html((d) -> indID[d])
-                       .direction('e')
-                       .offset([0,10])
-            svg.call(indtip)
+        indtip = d3.tip()
+                   .attr('class', "d3-tip #{tipclass}")
+                   .html((d) -> indID[d])
+                   .direction('e')
+                   .offset([0,10])
+        svg.call(indtip)

-            curve = d3.svg.line()
-                     .x((d) -> xscale(d.x))
-                     .y((d) -> yscale(d.y))
+        curve = d3.svg.line()
+                 .x((d) -> xscale(d.x))
+                 .y((d) -> yscale(d.y))

-            curves = svg.append("g").attr("id", "curves")
-            curvesSelect =
-               curves.selectAll("empty")
-                     .data(d3.range(n_ind))
-                     .enter()
-                     .append("path")
-                     .datum((d) -> dataByPoint[d])
-                     .attr("d", curve)
-                     .attr("class", (d,i) -> "path#{i}")
-                     .attr("fill", "none")
-                     .attr("stroke", (d,i) -> strokecolor[group[i]])
-                     .attr("stroke-width", strokewidth)
-                     .on "mouseover.panel", (d,i) ->
-                                               d3.select(this).attr("stroke", strokecolorhilit[group[i]]).moveToFront()
-                                               circle = d3.select("circle#hiddenpoint#{i}")
-                                               indtip.show(i, circle.node())
-                     .on "mouseout.panel", (d,i) ->
-                                               d3.select(this).attr("stroke", strokecolor[group[i]]).moveToBack()
-                                               indtip.hide()
+        curves = svg.append("g").attr("id", "curves")
+        curvesSelect =
+           curves.selectAll("empty")
+                 .data(d3.range(n_ind))
+                 .enter()
+                 .append("path")
+                 .datum((d) -> dataByPoint[d])
+                 .attr("d", curve)
+                 .attr("class", (d,i) -> "path#{i}")
+                 .attr("fill", "none")
+                 .attr("stroke", (d,i) -> strokecolor[group[i]])
+                 .attr("stroke-width", strokewidth)
+                 .on "mouseover.panel", (d,i) ->
+                                           d3.select(this).attr("stroke", strokecolorhilit[group[i]]).moveToFront()
+                                           circle = d3.select("circle#hiddenpoint#{i}")
+                                           indtip.show(i, circle.node())
+                 .on "mouseout.panel", (d,i) ->
+                                           d3.select(this).attr("stroke", strokecolor[group[i]]).moveToBack()
+                                           indtip.hide()

-            # grab the last non-null point from each curve
-            lastpoint = ({x:null, y:null} for i of data)
-            for i of dataByPoint
-                for v in dataByPoint[i]
-                    lastpoint[i] = v if v.x? and v.y?
+        # grab the last non-null point from each curve
+        lastpoint = ({x:null, y:null} for i of data)
+        for i of dataByPoint
+            for v in dataByPoint[i]
+                lastpoint[i] = v if v.x? and v.y?

-            pointsg = svg.append("g").attr("id", "invisiblepoints")
-            points = pointsg.selectAll("empty")
-                            .data(lastpoint)
-                            .enter()
-                            .append("circle")
-                            .attr("id", (d,i) -> "hiddenpoint#{i}")
-                            .attr("cx", (d) -> xscale(d.x))
-                            .attr("cy", (d) -> yscale(d.y))
-                            .attr("r", 1)
-                            .attr("opacity", 0)
+        pointsg = svg.append("g").attr("id", "invisiblepoints")
+        points = pointsg.selectAll("empty")
+                        .data(lastpoint)
+                        .enter()
+                        .append("circle")
+                        .attr("id", (d,i) -> "hiddenpoint#{i}")
+                        .attr("cx", (d) -> xscale(d.x))
+                        .attr("cy", (d) -> yscale(d.y))
+                        .attr("r", 1)
+                        .attr("opacity", 0)

     # functions to grab stuff
     chart.xscale = () -> xscale


Synchronous prop onChange at component init time

When a component initializes, the order of events shall be:
1. Evaluate state field defaults
2. Run prop onChange handlers with (undefined, null) or (undefined,
[value from parent]). These may synchronously call state setters before
state initialization is complete.
3. Render

--- components.coffee
+++ components.coffee
@@ -215,18 +215,24 @@
         for propName, propSpec of propSpecs
             if propName of @props and @props[propName] is undefined
                 throw new Meteor.Error "Can't pass undefined #{@}.props.#{propName}"
-            @_props[propName] = J.Var J.makeValueNotReadyObject(),
+
+            initialValue = J.util.withoutUndefined @props[propName]
+            @_props[propName] = J.Var initialValue,
                 tag:
                     component: @
                     propName: propName
                     tag: "#{@toString()}.prop.#{propName}"
                 onChange: if propSpec.onChange? then do (propName, propSpec) =>
                     (oldValue, newValue) =>
+                        _pushDebugFlag propSpec.debug ? componentSpec.debug
                         if componentDebug
-                            console.debug _getDebugPrefix(@), "props.#{propName}.onChange!"
+                            console.debug _getDebugPrefix(@), "prop.#{propName}.onChange!"
                             console.debug "        old:", J.util.consolify oldValue
                             console.debug "        new:", J.util.consolify newValue
+
                         propSpec.onChange.call @, oldValue, newValue
+
+                        _popDebugFlag()

             @prop[propName] = do (propName, propSpec) => =>
                 _pushDebugFlag propSpec.debug ? componentSpec.debug
@@ -238,10 +244,6 @@
                 _popDebugFlag()

                 ret
-
-            # Trigger onChange for the initial value
-            @_props[propName].set J.util.withoutUndefined @props[propName]
-

         # Set up @state
         initialState = {}
@@ -276,11 +278,15 @@
                     tag: "#{@toString()}.state.#{stateFieldName}"
                 onChange: if stateFieldSpec.onChange? then do (stateFieldName, stateFieldSpec) =>
                     (oldValue, newValue) =>
+                        _pushDebugFlag stateFieldSpec.debug ? componentSpec.debug
                         if componentDebug
                             console.debug _getDebugPrefix(@), "state.#{stateFieldName}.onChange!"
                             console.debug "        old:", J.util.consolify oldValue
                             console.debug "        new:", J.util.consolify newValue
+
                         stateFieldSpec.onChange.call @, oldValue, newValue
+
+                        _popDebugFlag()

         # Set up @reactives
         @reactives = {} # reactiveName: autoVar
@@ -310,15 +316,28 @@
                         retValue

                     if _.isFunction reactiveSpec.onChange then (oldValue, newValue) =>
+                        _pushDebugFlag reactiveSpec.debug ? componentSpec.debug
                         if componentDebug
                             console.debug "    #{@toString()}.#{reactiveName}.onChange!"
                             console.debug "        old:", J.util.consolify oldValue
                             console.debug "        new:", J.util.consolify newValue
+
                         reactiveSpec.onChange.call @, oldValue, newValue
+
+                        _popDebugFlag()
                     else reactiveSpec.onChange ? null

                     component: @
                 )
+
+        # This is what React is going to do after this function returns, but we need
+        # to do it early because of the synchronous prop onChanges.
+        @state = initialState
+
+        # Call all the prop onChange handlers synchronously because they might initialize
+        # the state during a cascading React render thread.
+        for propName, propVar of @_props
+            propVar.onChange? undefined, propVar._value

         # Return initialState to React
         initialState
@@ -434,7 +453,7 @@

     reactSpec.render = ->
         if Tracker.active
-            throw "Can't call render inside a reactive computation: #{Tracker.currentComputation._id}"
+            throw new Error "Can't call render inside a reactive computation: #{Tracker.currentComputation._id}"

         if @_elementVar? and not @_elementVar.invalidated
             throw new Meteor.Error "Called #{@toString()}.forceUpdate() - J.components


added tcp parser test

added tcp parser test

--- test/parser.coffee
+++ test/parser.coffee
@@ -1,6 +1,7 @@
 assert = require('assert')
 sip = require('../sip')
 fs = require('fs')
+net = require('net')

 # check correct parsing of most of specifically parsed headers
 # ie contact is parsed to array, to parsed to single valued headers etc
@@ -80,4 +81,30 @@

   success()

-exports.tests = [test1, test2]
+tcp = (success) ->
+  messages = ['wsinv', 'esc01', 'escnull', 'esc02', 'lwsdisp', 'longreq', 'semiuri', 'transports', 'mpart01', 'noreason', 'intmeth', 'unreason']
+  parsed = [];
+
+  transport = sip.makeTransport {}, (m) ->
+    m.headers.via[0].params.received = undefined
+    parsed.push m
+
+  blob = Buffer.concat messages.map (name) ->
+    m = fs.readFileSync "#{__dirname}/messages/#{ name }.dat"
+
+  jsons = messages.map (name) ->
+    JSON.parse fs.readFileSync "#{__dirname}/messages/#{ name }.json", 'binary'
+
+  (net.connect 5060).write(blob)
+
+  setTimeout () ->
+    assert parsed.length is messages.length
+    for i in [0 ... messages.length]
+      assert.deepEqual (JSON.parse JSON.stringify parsed[i]), jsons[i]
+
+    transport.destroy()
+
+    success()
+  , 1000
+
+exports.tests = [test1, test2, tcp]


minor test change

minor test change

--- test/render.coffee
+++ test/render.coffee
@@ -36,7 +36,7 @@

   describe '(html)', ->

-    it 'should render
 tags correctly', (done) ->
+    it 'should render
 tags correctly', (done) ->
       str = "
"
       html(str).should.equal '
'
       done()


Fix test

Fix test

--- test/routes.coffee
+++ test/routes.coffee
@@ -7,7 +7,8 @@
 describe "passing one argument of", ->
   describe "a number", ->
     it "should throw an exception", ->
-      -> solid.routes.build(1).should.throw()
+      -> solid.routes.build(1)
+      .should.throw()

   describe "a string", ->
     it "should serve the file denoted by that string", ->


Fix context size bugs

Fix context size bugs

--- framer/Context.coffee
+++ framer/Context.coffee
@@ -267,8 +267,8 @@
 			return window.innerHeight

 	@define "frame", get: -> {x:0, y:0, width:@width, height:@height}
-	@define "size",  get: -> _.pluck(@frame, ["x", "y"])
-	@define "point", get: -> _.pluck(@frame, ["width", "height"])
+	@define "size",  get: -> _.pick(@frame, ["width", "height"])
+	@define "point", get: -> _.pick(@frame, ["x", "y"])
 	@define "canvasFrame",
 		get: ->
 			return @frame if not parent


nothing

nothing

--- src/server/app.coffee
+++ src/server/app.coffee
@@ -13,7 +13,7 @@

 # Define Port & Environment.
 app.port = process.env.PORT or process.env.VMC_APP_PORT or 3000
-env = process.env.NODE_ENV or 'prod'
+env = 'prod'
 config.setEnvironment env

 # Config mongo db.


Forgot to remove a require statement.

Forgot to remove a require statement.

--- src/exceptions.coffee
+++ src/exceptions.coffee
@@ -2,7 +2,6 @@
 # pull in external modules
 _ = require '../vendor/_.js'
 {trace,vtrace,error,debug} = require './logging'
-{JavaObject} = require './java_object'

 "use strict"


adding slack formatting

adding slack formatting

--- src/github-tfs.coffee
+++ src/github-tfs.coffee
@@ -152,6 +152,13 @@

   asciiTable = new AsciiTable()

+  tableWrapper = ""
+  if robot.adapterName ? "slack"
+    tableWrapper = "```"
+
+  robot.logger.debug robot.adapterName
+
+
   # Check for required config
   missingEnvironmentForTFSBuildApi = (res) ->
     missingAnything = false
@@ -221,7 +228,7 @@
             res.reply "I found #{result.count} results for #{tfsProject} in #{tfsCollection}"
           else
             res.reply "I found #{result.count} results for #{tfsProject}"
-          tableResult = asciiTable.buildTable(tableDefinition, result.value)
+          tableResult = "#{tableWrapper}#{asciiTable.buildTable(tableDefinition, result.value)}#{tableWrapper}"
           res.reply tableResult

   ########################################
@@ -258,7 +265,7 @@
           result = []
           result.push JSON.parse body

-          tableResult = asciiTable.buildTable(tableDefinition, result)
+          tableResult = "#{tableWrapper}#{asciiTable.buildTable(tableDefinition, result)}#{tableWrapper}"
           res.reply tableResult

   ###############################################################


Oops.

Oops.

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -1,6 +1,6 @@
 BrowserWindow = null # Defer require until actually used
 # TODO: Remove the catch once Electron 0.35.0 is bundled in Atom
-try require {ipcRenderer} = 'electron' catch then ipcRenderer = require 'ipc'
+try {ipcRenderer} = require 'electron' catch then ipcRenderer = require 'ipc'

 {matches, closest, indexOf} = require './html-helpers'
 {CompositeDisposable} = require 'atom'
@@ -405,7 +405,7 @@
   browserWindowForId: (id) ->
     try
       BrowserWindow ?= require('electron').remote.BrowserWindow
-    catch e # TODO: Remove once Electron 0.35.0 is bundled in Atom
+    catch # TODO: Remove once Electron 0.35.0 is bundled in Atom
       BrowserWindow ?= require('remote').require('browser-window')

     BrowserWindow.fromId id


More Logging.

More Logging.

--- src/adapters/campfire.coffee
+++ src/adapters/campfire.coffee
@@ -50,10 +50,10 @@
       unless bot.info.id == author.id
         self.receive new Robot.LeaveMessage(author)

-    bot.on "TimestampMessage", ->
+    bot.on "SoundMessage", ->
       author = bot.Me
       console.log "Timestamp"
-      console.log author
+      console.log author.roomId

     bot.Me (err, data) ->


Base clue generation off puzzle size

Base clue generation off puzzle size

Remove hardcoded references to a 10x10 puzzle

--- src/views/game.coffee
+++ src/views/game.coffee
@@ -29,7 +29,7 @@
   GRID_SIZE_RATIO: 0.75
   GRID_BACKGROUND_SIZE_RATIO: 0.97

-  difficulty: "easy"
+  difficulty: "beginner"
   level: 0

   timerId: null
@@ -121,7 +121,7 @@
         horizontalClues.eq(row).addClass('highlight')

         # Try to mark or fill
-        @doAction row, col
+        @doAction(row, col)

       # Reset the "previous" values
       @previousRow = row
@@ -147,9 +147,9 @@

   # Try to either mark or fill a grid cell
   doAction: (row, col) ->
-    index = row * 10 + col
-
-    # Determine if block is empty, filled, or marked
+    index = row * @cellCount + col
+
+    # TODO: need to only find visible cells here
     block = @grid.find('.blank').eq(index)

     valid = @clues[index] == 1
@@ -224,9 +224,9 @@

     blocks = @grid.find('.blank')

-    for i in [0..9]
-      rowIndex = row * 10 + i
-      columnIndex = i * 10 + col
+    for i in [0 ... @cellCount]
+      rowIndex = row * @cellCount + i
+      columnIndex = i * @cellCount + col

       if blocks.eq(rowIndex).hasClass('filled') then completedRowTotal++
       if blocks.eq(columnIndex).hasClass('filled') then completedColumnTotal++
@@ -461,16 +461,18 @@

   resizeGrid: ->
     smallestDimension = if @orientation is 'landscape' then @height else @width
-    gridBackgroundSize = Math.round(smallestDimension * @GRID_BACKGROUND_SIZE_RATIO / 10) * 10
+    # gridBackgroundSize = Math.round(smallestDimension * @GRID_BACKGROUND_SIZE_RATIO / 10) * 10
+    gridBackgroundSize = Math.round(smallestDimension * @GRID_BACKGROUND_SIZE_RATIO)
+    borderWidth = parseInt(@gridBackground.css('border-width'), 10) * 2

     @gridBackground.width(gridBackgroundSize)
     @gridBackground.height(gridBackgroundSize)

     if @orientation is 'landscape'
-      horizontalMargin = (@height - @gridBackground.width() - 10) / 2
+      horizontalMargin = (@height - @gridBackground.width() - borderWidth) / 2
       verticalMargin = (gridBackgroundSize - @gridBackground.height()) / 2
     else if @orientation is 'portrait'
-      verticalMargin = (@width - @gridBackground.height() - 10) / 2
+      verticalMargin = (@width - @gridBackground.height() - borderWidth) / 2
       horizontalMargin = (gridBackgroundSize - @gridBackground.width()) / 2

     @gridBackground.css
@@ -588,17 +590,17 @@
     @cellCount = Math.sqrt(@clues.length)

     # Load level/parse clues
-    for i in [0..9]
-      horizontalClue = ""
-      verticalClue = ""
+    for i in [0 ... @cellCount]
+      horizontalClue = ''
+      verticalClue = ''
       horizontalCounter = 0
       verticalCounter = 0
       previousVertical = false
       previousHorizontal = false

       # Create horizontal clues
-      for j in [0..9]
-        index = i * 10 + j
+      for j in [0 ... @cellCount]
+        index = i * @cellCount + j
         if @clues[index] is 1
           horizontalCounter++
           @totalHits++
@@ -609,8 +611,8 @@
           previousHorizontal = false

       # Create vertical clues
-      for j in [0..9]
-        index = j * 10 + i
+      for j in [0 ... @cellCount]
+        index = j * @cellCount + i
         if @clues[index] is 1
           verticalCounter++
           previousVertical = true
@@ -623,8 +625,8 @@
       if previousHorizontal then horizontalClue += "#{horizontalCounter}"
       if previousVertical then verticalClue += "#{verticalCounter}
"

-      if horizontalClue == "" then horizontalClue = "0"
-      if verticalClue == "" then verticalClue = "0
"
+      if horizontalClue == '' then horizontalClue = '0'
+      if verticalClue == '' then verticalClue = '0
'

       match = verticalClue.match(/
/g)
       length = if match? then match.length else 0
@@ -634,9 +636,8 @@

       # Add some manual padding for vertical clues so they are bottom aligned
       if length < 5
-        for [length..4]
+        for [length .. 4]
           verticalClue = "
#{verticalClue}"
-

       @$('.horizontal.clue', @elem).eq(i).html(horizontalClue)
       @$('.vertical.clue', @elem).eq(i).html(verticalClue)


Added touch events

Added touch events

--- source/runtime.coffee
+++ source/runtime.coffee
@@ -30,6 +30,12 @@
   scroll
   select
   submit
+  touchcancel
+  touchend
+  touchenter
+  touchleave
+  touchmove
+  touchstart
   unload
 """.split("\n")


Shorten output call in spec

Shorten output call in spec

--- spec/Component.coffee
+++ spec/Component.coffee
@@ -245,9 +245,8 @@
       c.inPorts.in.attach s1
       c.outPorts.out.attach s2

-      i = new IP 'data', 'some-data'
-      i.groups = ['foo']
-      s1.post i
+      s1.post new IP 'data', 'some-data',
+        groups: ['foo']

     it 'should support substreams', (done) ->
       c = new component.Component


Added test for broken cloning spec

Added test for broken cloning spec

--- spec/ModelSpec.coffee
+++ spec/ModelSpec.coffee
@@ -288,7 +288,28 @@
 			expect(serial.bar[0]).toBe 1
 			expect(serial.bar[1]).toBe 2

-		it "should clone"
+		it "should clone object references", ->
+			m1 = new Model id:1
+			m2 = new Model id:2
+			m3 = new Model id:3
+			model.set
+				foo: m1
+				bar: [m2, m3]
+			clone = model.clone()
+			expect(clone.get "foo").toBe m1
+			bar = clone.get("bar").toJSON()
+			expect(bar).toContain m2
+			expect(bar).toContain m3
+
+		it "should clone object IDs", ->
+			model.set
+				foo: 1
+				bar: [2, 3]
+			clone = model.clone()
+			expect(clone.get "foo").toBe 1
+			bar = clone.get("bar").toJSON()
+			expect(bar).toContain 2
+			expect(bar).toContain 3

 		describe "persistence", ->
 			m1 = new Model id:1


Disable debugging mode of Universal Analytics

--- src/beautifiers/index.coffee
+++ src/beautifiers/index.coffee
@@ -218,7 +218,7 @@
           atom.config.set "atom-beautify.general._analyticsUserId", uuid.v4()
         # Setup Analytics User Id
         userId = atom.config.get("atom-beautify.general._analyticsUserId")
-        @analytics ?= new ua(trackingId, userId).debug()
+        @analytics ?= new ua(trackingId, userId)
         @analytics[type](payload).send()
     catch error
       logger.error(error)


no need to actually create dom elements

no need to actually create dom elements

--- js/outer/docs.coffee
+++ js/outer/docs.coffee
@@ -57,9 +57,10 @@
       try
         # extract the metadata, which is actually in the file, writing into the context
         context = {include_article: => }
-        node = $(window.HAML[slug](context))
+        # we're just calling the template for the side effects on context
+        window.HAML[slug](context)
         if context.title
-          @articles[slug] = @article_info(slug, node, context)
+          @articles[slug] = @article_info(slug, context)
           if context.category
             @categories[slug] = @articles[slug]

@@ -85,7 +86,7 @@

-  article_info: (slug, node, cx) =>
+  article_info: (slug, cx) =>
     uriFragment = slug.replace(/_/g, '-')
     children = cx.children or []
     result =


don't try to intercept clicks if pushState not available

--- shared/base/app_view.coffee
+++ shared/base/app_view.coffee
@@ -1,4 +1,6 @@
 BaseView = require('./view')
+
+hasPushState = window.pushState?

 module.exports = class AppView extends BaseView
   el: 'body'
@@ -31,7 +33,7 @@
       @app.router.redirectTo(href)

   shouldInterceptClick: (href, el) ->
-    return false unless href
+    return false unless href && hasPushState
     hashParts = href.split('#')
     isHashClick = hashParts.length > 1 && hashParts[0] == window.location.pathname
     !isHashClick && href.slice(0, 1) == '/' && href.slice(0, 2) != '//'


changed resethandlerhelper require path

changed resethandlerhelper require path

--- servers/lib/server/handlers/reset.test.coffee
+++ servers/lib/server/handlers/reset.test.coffee
@@ -1,22 +1,21 @@
-Bongo                                     = require 'bongo'
-koding                                    = require './../bongo'
-request                                   = require 'request'
-querystring                               = require 'querystring'
-
-{ daisy }                                 = Bongo
-{ expect }                                = require 'chai'
+Bongo                             = require 'bongo'
+koding                            = require './../bongo'
+request                           = require 'request'
+querystring                       = require 'querystring'
+
+{ daisy }                         = Bongo
+{ expect }                        = require 'chai'
 { generateRandomEmail
   generateRandomString
   generateRandomUsername

-  ResetHandlerHelper
-  RegisterHandlerHelper }                 = require '../../../testhelper'
-
-{ generateResetRequestParams }           = ResetHandlerHelper
-{ generateRegisterRequestParams }        = RegisterHandlerHelper
-
-JUser                                    = null
-JPasswordRecovery                        = null
+  RegisterHandlerHelper }         = require '../../../testhelper'
+{ defaultExpiryPeriod
+  generateResetRequestParams }    = require '../../../testhelper/resethandlerhelper'
+{ generateRegisterRequestParams } = RegisterHandlerHelper
+
+JUser                             = null
+JPasswordRecovery                 = null

 # here we have actual tests
 runTests = -> describe 'server.handlers.reset', ->
@@ -101,7 +100,7 @@
     token        = generateRandomString()
     username     = generateRandomUsername()
     certificate  = null
-    expiryPeriod = ResetHandlerHelper.defaultExpiryPeriod
+    expiryPeriod = defaultExpiryPeriod
     expectedBody = 'This password recovery certificate cannot be redeemed.'

     resetRequestParams = generateResetRequestParams
@@ -165,7 +164,7 @@
     token        = generateRandomString()
     username     = generateRandomUsername()
     certificate  = null
-    expiryPeriod = ResetHandlerHelper.defaultExpiryPeriod
+    expiryPeriod = defaultExpiryPeriod

     resetRequestParams = generateResetRequestParams
       body            :
@@ -209,7 +208,7 @@
     token               = generateRandomString()
     username            = generateRandomUsername()
     certificate         = null
-    expiryPeriod        = ResetHandlerHelper.defaultExpiryPeriod
+    expiryPeriod        = defaultExpiryPeriod
     passwordBeforeReset = null

     resetRequestParams = generateResetRequestParams
@@ -280,7 +279,7 @@
     username            = generateRandomUsername()
     certificate1        = null
     certificate2        = null
-    expiryPeriod        = ResetHandlerHelper.defaultExpiryPeriod
+    expiryPeriod        = defaultExpiryPeriod

     resetRequestParams = generateResetRequestParams
       body            :


move loading promise logic

move loading promise logic

--- lib/connection/ipc.coffee
+++ lib/connection/ipc.coffee
@@ -38,14 +38,11 @@
   msg: (type, args...) -> @writeMsg [type, args...]

   rpc: (type, args...) ->
-    x = new Promise (resolve, reject) =>
+    p = new Promise (resolve, reject) =>
       @id += 1
       @callbacks[@id] = {resolve, reject}
       @msg {type, callback: @id}, args...
-    @loading?.working()
-    done = => @loading?.done()
-    x.then done, done
-    x
+    @loading?.monitor p

   flush: ->
     @writeMsg msg for msg in @queue


redraw collisions after buildings

redraw collisions after buildings


Add empty message

Add empty message

--- lib/diff-list-view.coffee
+++ lib/diff-list-view.coffee
@@ -5,6 +5,9 @@
   initialize: ->
     super
     @addClass('symbols-view overlay from-top')
+
+  getEmptyMessage: ->
+    'No diffs'

   attach: ->
     @storeFocusedElement()


:bug: fixes InvariantViolation when switching screens

--- src/components/loadable.cjsx
+++ src/components/loadable.cjsx
@@ -16,12 +16,20 @@
     isLoaded: React.PropTypes.func.isRequired
     isFailed: React.PropTypes.func.isRequired

-  componentWillMount: ->
+  _addListener: ->
     {store} = @props
     store.addChangeListener(@_update)
-  componentWillUnmount: ->
+
+  _removeListener: ->
     {store} = @props
     store.removeChangeListener(@_update)
+
+  componentWillMount:   -> @_addListener()
+  componentWillUnmount: -> @_removeListener()
+
+  # The following fixs an invariant violation when switching screens
+  componentWillUpdate: -> @_removeListener()
+  componentDidUpdate:  -> @_addListener()

   _update: -> @props.update?() or @setState({})


Close CC on window.popstate event

Close CC on window.popstate event

--- src/concept-coach/index.cjsx
+++ src/concept-coach/index.cjsx
@@ -97,6 +97,11 @@
     User.channel.once 'logout.received', ->
       props.close()

+    onPopStateClose = ->
+      props.close()
+      window.removeEventListener 'popstate', onPopStateClose
+    window.addEventListener 'popstate', onPopStateClose
+
     @component = modalCoachWrapped.render(modalNode, props)
     @close = props.close


trim out the middle of long artifact paths

--- js/inner/build.coffee
+++ js/inner/build.coffee
@@ -474,6 +474,7 @@
         @clean_artifacts()
         data = for artifact in data
                  artifact.pretty_path = artifact.pretty_path.replace "$CIRCLE_ARTIFACTS/", ""
+                 artifact.pretty_path = CI.stringHelpers.trimMiddle artifact.pretty_path, 80
                  artifact
         @artifacts(data)
       complete: () =>


passing all tests

passing all tests

--- matrix-utilities.coffee
+++ matrix-utilities.coffee
@@ -39,28 +39,16 @@

 	to2d: (matrix) ->

-		if matrix.length < 2
-			throw new Error 'Matrix should have at least 2 rows'
-
-		if matrix[0].length < 4
-			throw new Error 'Matrix should have at least 4 columns'
-
 		[
-			[matrix[0][0], matrix[0][1], matrix[0][3]]
-			[matrix[1][0], matrix[1][1], matrix[1][3]]
+			[matrix[0][0] or 1, matrix[0][1] or 0, matrix[0][3] or 0]
+			[matrix[1][0] or 0, matrix[1][1] or 1, matrix[1][3] or 0]
 		]

 	to3d: (matrix) ->

-		if matrix.length < 2
-			throw new Error 'Matrix should have at least 2 rows'
-
-		if matrix[0].length < 3
-			throw new Error 'Matrix should have at least 3 columns'
-
 		[
-			[matrix[0][0], matrix[0][1], 0, matrix[0][2]]
-			[matrix[1][0], matrix[1][1], 0, matrix[1][2]]
+			[matrix[0][0] or 1, matrix[0][1] or 0, 0, matrix[0][2] or 0]
+			[matrix[1][0] or 0, matrix[1][1] or 1, 0, matrix[1][2] or 0]
 			[0, 0, 1, 0]
 			[0, 0, 0, 1]
 		]


use defaultzoom level for zoombar

use defaultzoom level for zoombar

--- js/msa/zoombar.coffee
+++ js/msa/zoombar.coffee
@@ -7,7 +7,11 @@
       zoomSlider.type = "range"
       zoomSlider.name = "points"
       zoomSlider.min = 1
-      zoomSlider.max = 15
+      zoomSlider.max = 30
+      zoomSlider.style.width = "60%"
+
+      zoomSlider.value = @msa.zoomer.level
+
       zoomForm.appendChild zoomSlider
       @locked = false

@@ -29,7 +33,7 @@
     _reDraw: =>
       console.log @zoomSlider.value
       value = @zoomSlider.value
-      @msa.zoomer.setZoomLevel(value * 2)
+      @msa.zoomer.setZoomLevel(value)
       @msa.config.autofit = false
       @msa.redraw('stage')
       @msa.redraw('marker')


david changed his name

david changed his name

--- js/outer/about.coffee
+++ js/outer/about.coffee
@@ -21,7 +21,7 @@
       bio: ["The answer to any question posed to Allen is \"Clojure\". He <b>loves</b> Clojure! He's been working with Clojure since before it was officially released, and his name appears in the Changelog of dozens of libraries, including Clojure core. Allen's favourite philosopher is Rich Hickey, and his hobbies include Ultimate Frisbee, persistent data structures, and immutability.",
             "Allen started Circle to make Continuous Deployment available to the masses. He's greatly influenced by the work of IMVU, and his goal is their infrastructure as a generalized service. He currently deploys six times a day, and more on weekends."]
     ,
-      name: "David Lowe"
+      name: "Dracula Lowe"
       visible: true
       role: "Backend Developer"
       github: "dlowe"


Handle error on JSON.parse

Handle error on JSON.parse

--- lib/linter/rubocop.coffee
+++ lib/linter/rubocop.coffee
@@ -7,11 +7,10 @@
   constructor: (@filePath) ->

   run: (callback) ->
-    @runRubocop (stdout, error) =>
+    @runRubocop (result, error) =>
       if error
         callback(null, error)
       else
-        result = JSON.parse(stdout)
         file = result.files[0]
         offenses = file.violations || file.offences
         violations = offenses.map(@createViolationFromOffense)
@@ -41,9 +40,13 @@

     rubocop.on 'close', (exitCode) ->
       if exitCode == 0 || exitCode == 1
-        callback(stdout, null)
+        try
+          result = JSON.parse(stdout)
+          callback(result, null)
+        catch error
+          callback(null, error)
       else
-        callback(stdout, "Process exited with code #{exitCode}")
+        callback(null, "Process exited with code #{exitCode}")

     rubocop.on 'error', (error) ->
       callback(stdout, error)


support pandoc_args

support pandoc_args

--- lib/pandoc-wrapper.coffee
+++ lib/pandoc-wrapper.coffee
@@ -24,12 +24,27 @@
     args.push('--toc-depth='+config['toc_depth'])
   if config['highlight']
     args.push('--highlight-style='+config['highlight'])
+  if config['pandoc_args']
+    for arg in config['pandoc_args']
+      args.push(arg)

-loadOutputYAML = (md)->
+loadOutputYAML = (md, config)->
   yamlPath = path.resolve(path.dirname(md.editor.getPath()), '_output.yaml')
   yaml = fs.readFileSync yamlPath
   data = matter('---\n'+yaml+'---\n').data
-  data || {}
+  data = data || {}
+
+  if config['output']
+    if typeof(config['output']) == 'string' && data[config['output']]
+      format = config['output']
+      config['output'] = {}
+      config['output'][format] = data[format]
+    else
+      format = Object.keys(config['output'])[0]
+      if data[format]
+        config['output'][format] = Object.assign({}, data[format], config['output'][format])
+
+  Object.assign({}, data, config)

 ###
 title
@@ -39,9 +54,11 @@
 output:
 ###
 pandocConvert = (text, md, config={})->
-  config = Object.assign({}, loadOutputYAML(md), config)
+  config = loadOutputYAML md, config
   text = matter.stringify(text, config)
   args = []
+
+  console.log config

   extension = null
   outputConfig = null


naming tweaks

naming tweaks

--- lib/server.storage.coffee
+++ lib/server.storage.coffee
@@ -26,16 +26,16 @@
   _redisClient.sadd("#{APP_NS}:authenticated_users", userKey)
   _redisClient.set("#{APP_NS}:#{userKey}:properties", JSON.stringify(user))

-storeUserSlideEventsOnRedis = (user) ->
+mkUserSlideEventConsumerForRedis = (user) ->
   userKey = getUserKey(user)
   (slideEvent) ->
     _redisClient.lpush("#{APP_NS}:#{userKey}:slide_events", JSON.stringify(slideEvent))

-registerToUserSlideEvent = (user) ->
+subscribeToUserSlideEvents = (user) ->
   userKey  = getUserKey(user, "/")
-  subscribe("#{channels.slideEvents}/#{userKey}", storeUserSlideEventsOnRedis(user))
+  subscribe("#{channels.slideEvents}/#{userKey}", mkUserSlideEventConsumerForRedis(user))

 subscribe(channels.clientAuthenticated, storeAuthenticatedUserOnRedis)
-subscribe(channels.clientAuthenticated, registerToUserSlideEvent)
+subscribe(channels.clientAuthenticated, subscribeToUserSlideEvents)

 ################################################################################


Function to resolve types of multiple elements.

--- lib/typeresolution.coffee
+++ lib/typeresolution.coffee
@@ -158,6 +158,13 @@
     ensureImplicitNestedTypes elementNode, simpleTypeSpec, cb

+resolveTypes = (elementNodes, inheritedTypeName, cb) ->
+  async.map elementNodes, (elementNode, next) ->
+    resolveType elementNode, inheritedTypeName, next
+  , cb
+
+
 module.exports = {
   resolveType
+  resolveTypes
 }


Handle apm process errors

Handle apm process errors

--- lib/user-utilities.coffee
+++ lib/user-utilities.coffee
@@ -87,7 +87,7 @@
   getInstalledPackages: ->
     new Promise (resolve, reject) =>
       data = []
-      new BufferedProcess
+      apmProcess = new BufferedProcess
         command: atom.packages.getApmPath()
         args: ['ls', '--json', '--no-color']
         stdout: (oneLine) -> data.push(oneLine)
@@ -100,6 +100,12 @@
           resolve
             dev: @filterActivePackages(packages.dev)
             user: @filterActivePackages(packages.user)
+
+      apmProcess.onWillThrowError ({handle}) ->
+        handle()
+        resolve
+          dev: []
+          user: []

   filterActivePackages: (packages) ->
     "#{pack.name}, v#{pack.version}" for pack in (packages ? []) when atom.packages.getActivePackage(pack.name)?


set vehicle icon and rotate when rotation changes

--- lib/vehicle_marker.coffee
+++ lib/vehicle_marker.coffee
@@ -24,10 +24,8 @@
   init: ->
     @set('movingIcon', new SM.VehicleMovingIcon())
     @set('stoppedIcon', new SM.VehicleStoppedIcon())
-    # TODO: Leaflet won't let allow setIcon to be used on a marker object until it has
-    # been added to the map, hence I need to set all vehicles to display the stoppedIcon
-    # to begin with
-    @mapObject = new L.Marker(new L.LatLng(@get('lat'),@get('lon')), icon: @get('stoppedIcon'))
+    state = @getPath('vehicle.state')
+    @mapObject = new L.Marker(new L.LatLng(@get('lat'),@get('lon')), icon: @get("#{state}Icon"))
   bounds: ->
     latlng = @get('mapObject')._latlng
     new L.LatLngBounds(latlng,latlng)
@@ -48,8 +46,8 @@
   ).observes('icon')
   _rotationDidChange: ( ->
     # TODO: rotation changes are lost when the map is zoomed
-    # img = @get('mapObject')._icon
-    # rotation = @getPath('vehicle.heading') || 0
-    # value = img.style.getPropertyValue('-webkit-transform')
-    # img.style.setProperty('-webkit-transform',value + " rotate(#{rotation}deg)")
+    img = @get('mapObject')._icon
+    rotation = @getPath('vehicle.heading') || 0
+    value = img.style.getPropertyValue('-webkit-transform')
+    img.style.setProperty('-webkit-transform',value + " rotate(#{rotation}deg)")
   ).observes('vehicle.heading')


Make sure filters can access route params

--- src/net/router.coffee
+++ src/net/router.coffee
@@ -104,14 +104,15 @@

     handler = @findRoute(path)

-    @beforeFilters.forEach (filter) => filter(path, this)

     if handler?
       handler(path)
     else
+      @beforeFilters.forEach (filter) => filter({path}, this)
       @notFoundHandle?({path})
+      @afterFilters.forEach (filter) => filter({path}, this)

-    @afterFilters.forEach (filter) => filter(path, this)
+

     document.dispatchEvent agt.domEvent('route:changed', {path}) if document?

@@ -150,6 +151,7 @@
         pathRe.push part

     re = new RegExp('^/' + pathRe.join('/') + '$')
+    self = this

     {
       options
@@ -161,5 +163,7 @@
           for pname,i in pathParams
             params[pname] = decodeURI(res[i+1])

+        self.beforeFilters.forEach (filter) => filter(params, self)
         handle(params)
+        self.afterFilters.forEach (filter) => filter(params, self)
     }


Actually use defined variables

Actually use defined variables

--- lib/views/terminal.coffee
+++ lib/views/terminal.coffee
@@ -81,8 +81,8 @@
             ipc.send 'terminal-data', Clipboard.readText().replace(/\n/g, "\r")
           else if event.altKey
             console.log 'Saved from alt key disaster!'
-          else if (e.which == 187 || e.which == 189) && e.ctrlKey
-            @adjustTermFontSize(e.which)
+          else if (event.which == 187 || event.which == 189) && event.ctrlKey
+            @adjustTermFontSize(event.which)
           else
             ipc.send 'terminal-data', data
         else


Add setting if emojis should be dowwnloaded from local or Emoji Cheat Sheet site

--- lib/broadcast.coffee
+++ lib/broadcast.coffee
@@ -9,6 +9,9 @@
   configDefaults:
     hostname: 'localhost'
     port: 8000
+    getEmojisFromCheatSheetSite: false
+
+  urlToCheatSheetSite: 'https://raw.githubusercontent.com/arvida/emoji-cheat-sheet.com/master/public/graphics/emojis'

   activate: ->
     atom.workspaceView.command "broadcast:start", => @start()
@@ -25,11 +28,13 @@
     editor = atom.workspace.activePaneItem
     if editor[0]?
       content = editor[0].outerHTML
+      if atom.config.get 'broadcast.getEmojisFromCheatSheetSite'
+        content = content.replace /[\w-\.\/]+pngs/g, @urlToCheatSheetSite
+      else
+        content = content.replace /[\w-\.\/]+node_modules\/roaster/g, ''
     else
       content = '<pre>' + editor.getText?() + '</pre>'
     template = template.replace '{CONTENT}', content
-
-    template = template.replace /[\w-\.\/]+node_modules\/roaster/g, ''

     fileServer = new nodeStatic.Server filePath


updated tests so they work now

updated tests so they work now

--- test.coffee
+++ test.coffee
@@ -10,8 +10,8 @@
 describe 'unshrtn', ->

   it 'unshortens', (done) ->
-    u 'http://www.inkdroid.org', (err, long) ->
-      assert.equal long, 'http://inkdroid.org/'
+    u 'http://inkdroid.org', (err, long) ->
+      assert.equal long, 'https://inkdroid.org/'
       assert.equal err, null
       done()

@@ -28,12 +28,12 @@

   it 'handles connection refused', (done) ->
     u 'http://inkdroid.org:666/', (err, long) ->
-      assert.equal err, 'Error: connect ECONNREFUSED'
+      assert.match err, /Error: connect ECONNREFUSED/
       assert.equal long, null
       done()

   it 'handles fw.to', (done) ->
     u 'http://fw.to/ixsPPtP', (err, long) ->
-      assert.equal long, 'http://www.theglobeandmail.com/globe-debate/fifty-years-in-canada-and-now-i-feel-like-a-second-class-citizen/article26691065/'
+      assert.equal long, 'http://www.theglobeandmail.com/opinion/fifty-years-in-canada-and-now-i-feel-like-a-second-class-citizen/article26691065/'
       done()


fixed tests

fixed tests

--- test.coffee
+++ test.coffee
@@ -68,7 +68,7 @@
     template: 'h1 "harcoded: " + obj.foo'
     run: ->
       obj = {foo: 'bar'}
-      @compiled = ck.compile(@template, hardcode: {obj})
+      @compiled = cc.compile(@template, hardcode: {obj})
       @expected = '
harcoded: bar
'
       @result = @compiled()
       @success = @result is @expected
@@ -167,8 +167,8 @@
     template: "p \"This text could use \#{yield -> strong -> a href: '/', 'a link'}.\""
     expected: '
This text could use a link.
'

-ck = require './src/coffeekup'
-render = ck.render
+cc = require './src/coffeecup'
+render = cc.render

 @run = ->
   {print} = require 'sys'
@@ -185,7 +185,7 @@
       if test.run
         test.run()
       else
-        test.result = ck.render(test.template, test.params)
+        test.result = cc.render(test.template, test.params)
         test.success = test.result is test.expected

       if test.success


sanitize rendered HTML to prevent XSS attack


Ease: improve description

Ease: improve description

--- components/Ease.coffee
+++ components/Ease.coffee
@@ -6,7 +6,8 @@
   ease = require 'ease'

 class Ease extends noflo.Component
-  description: 'Easing function component'
+  description: 'Easing function component that takes a normalized value between 0
+   and 1 and outputs eased value between from and to inputs'
   icon: 'cogs'
   constructor: ->
     @inPorts =


JS: Add tests for filter.updateURL function

--- tests_javascript/test_filter.coffee
+++ tests_javascript/test_filter.coffee
@@ -107,7 +107,6 @@
         # Manipulating the history is the easiest and simpliest method of
         # testing these functions I've found.

-        test 'Should not update the URL for an unchanged filter'
         suiteSetup () ->
             # Save the current location
             @originalLocation = window.location.href
@@ -126,9 +125,27 @@
             window.history.replaceState null, '', '/requests/all/page/10'
             assert.deepEqual filter.getFilters(),
                 (_.defaults {page: 10}, defaultFilters)
-        test 'Should reset the page to page 1 when changing the filters'

-        test 'Should not reset the page when the page number is changing'
+        test 'Should not update the URL for an unchanged filter', () ->
+            startingPath = '/requests/all/page/10'
+            startingFilter = {page: 10}
+            window.history.replaceState startingFilter, '', startingPath
+            filter.updateURL {page: 10}
+            assert.strictEqual window.location.pathname, startingPath
+            assert.deepEqual history.state, startingFilter
+
+        test 'Should reset the page to page 1 when changing the filters', () ->
+            window.history.replaceState {page: 10}, '', '/requests/all/page/10'
+            filter.updateURL {pilot: ['Paxswill'], page: 10}
+            assert.deepEqual history.state, {pilot: ['Paxswill'], page: 1}
+            assert.strictEqual window.location.pathname,
+                '/requests/all/page/1/pilot/Paxswill'
+
+        test 'Should not reset the page when the page number is changing', () ->
+            window.history.replaceState {page: 10}, '', '/requests/all/page/10'
+            filter.updateURL {page: 11}
+            assert.deepEqual history.state, {page: 11}
+            assert.strictEqual window.location.pathname, '/requests/all/page/11'

     test 'Should create a translated Selectize option'


Restires the timeout to 30s

Restires the timeout to 30s

--- src/client/api.coffee
+++ src/client/api.coffee
@@ -268,20 +268,20 @@

         initialized = core.data.deferred()

-        onRemoteMessage = (e)->
+        onRemoteMessage = (e)->
           if e.error
             # Get out of the easyXDM try/catch jail
             setTimeout(
               -> initialized.reject(e.error)
             , 0)
-          else
+          else
             console.warn("RPC Message", arguments)

         #TODO Probably useless now
         timeout = setTimeout(
           ()->
             initialized.reject('Remote loading has failed. Please check "orgUrl" and "appId" in your configuration. This may also be about connectivity.')
-          , 3000)
+          , 30000)

         onRemoteReady = (remoteConfig)->
           data = remoteConfig.data


Handled if user toggles code peek twice on same function

Code peek toggles off

--- lib/code-peek.coffee
+++ lib/code-peek.coffee
@@ -12,6 +12,8 @@
     @codePeekView = new CodePeekView()
     @panel = atom.workspace.addBottomPanel(item: @codePeekView)
     @panel.hide()
+
+    @previousFunctionName = null

     # Events subscribed to in atom's system can be easily cleaned up with a
     # CompositeDisposable
@@ -28,23 +30,31 @@

   peekFunction: ->

+    textEditorParser = new TextEditorParser(
+      atom.workspace.getActiveTextEditor())
+
+    functionName = textEditorParser.getWordContainingCursor()
+    if not functionName?
+      atom.notifications.addError("Unable to get word containing cursor")
+      return
+
+    if @previousFunctionName is functionName
+      # user selected the same function as last time, so just toggle off
+      @codePeekView.detachTextEditorView()
+      @panel.hide()
+      @previousFunctionName = null
+      return
+
     if @panel.isVisible()
       @codePeekView.detachTextEditorView()
       @panel.hide()

-    textEditorParser = new TextEditorParser(
-      atom.workspace.getActiveTextEditor())
+    @previousFunctionName = functionName
     fileType = textEditorParser.getFileType()

     if not SupportedFiles.isSupported(fileType)
       atom.notifications.addWarning("Peek function does not support \
         #{fileType} files")
-      return
-
-    functionName = textEditorParser.getWordContainingCursor()
-
-    if not functionName?
-      atom.notifications.addError("Unable to get word containing cursor")
       return

     regExp = SupportedFiles.getFunctionRegExpForFileType(fileType, functionName)


Only update editor when pattern changes

Only update editor when pattern changes

This prevents contents-modified from being constant emitted by the editor
causing typing and cursor blink to be affected as the text was constantly
reset.

Closes #121

--- lib/find-view.coffee
+++ lib/find-view.coffee
@@ -214,7 +214,8 @@
     @updateOptionButtons()
     @updateDescription()
     @findResultsView.attach() if @isVisible()
-    @findEditor.setText(@findModel.pattern)
+    if @findModel.pattern isnt @findEditor.getText()
+      @findEditor.setText(@findModel.pattern)
     @findHistory.store()
     @replaceHistory.store()


Bug fix with palette scrolling

Bug fix with palette scrolling

--- src/controller.coffee
+++ src/controller.coffee
@@ -145,6 +145,7 @@

       # Scroll offset
       @scrollOffset = new draw.Point 0, 0
+      @paletteScrollOffset = new draw.Point 0, 0

       # Touchscreen scrolling fields
       @touchScrollAnchor = null
@@ -212,6 +213,9 @@

       @clear = =>
         @mainCtx.clearRect @scrollOffset.x, @scrollOffset.y, @main.width, @main.height
+
+      @clearPalette = =>
+        @paletteCtx.clearRect @paletteScrollOffset.x, @paletteScrollOffset.y, @palette.width, @palette.height

       # ## Redraw ##
       # redraw does three main things: redraws the root tree (@tree)
@@ -910,7 +914,12 @@
       hitTestLasso = (point) => if @lassoSegment? and @_lassoBounds.contains point then @lassoSegment else null

       hitTestPalette = (point) =>
-        point = new draw.Point point.x + PALETTE_WIDTH, point.y - @scrollOffset.y
+        # The point was given in relation to the main canvas,
+        # but we want it in relation to the palette canvas;
+        # translate it.
+        point = new draw.Point point.x + PALETTE_WIDTH, point.y - @scrollOffset.y + @paletteScrollOffset.y
+
+        # Hit test as per normal.
         for block in @paletteBlocks
           if hitTest(point, block.start)? then return block
         return null
@@ -919,12 +928,13 @@

       # ### getPointFromEvent ###
       # This is a conveneince function, which will contain compatability layers for getting
-      # the offset coordinates of a mouse click point
+      # the offset coordinates of a mouse click point. This is returned in relation to the origin
+      # of the main canvas; things in relation to other canvases must translate from here.

       getPointFromEvent = (event) =>
         switch
           when event.offsetX? then new draw.Point event.offsetX - PALETTE_WIDTH, event.offsetY + @scrollOffset.y
-          when event.layerX then new draw.Point event.layerX - PALETTE_WIDTH, event.layerY + @scrollOffset.y
+          when event.layerX? then new draw.Point event.layerX - PALETTE_WIDTH, event.layerY + @scrollOffset.y

       performNormalMouseDown = (point, isTouchEvent) =>

@@ -984,7 +994,9 @@
           # If we have clicked on a block or segment, then NORMAL DRAG, indicated by (@ephemeralSelection?)

           if @ephemeralSelection in @paletteBlocks
-            @ephemeralPoint = new draw.Point point.x - @scrollOffset.x, point.y - @scrollOffset.y
+
+            # It's in the palette, so we need to translate it.
+            @ephemeralPoint = new draw.Point point.x - @scrollOffset.x + @paletteScrollOffset.x, point.y - @scrollOffset.y + @paletteScrollOffset.y
           else
             @ephemeralPoint = new draw.Point point.x, point.y

@@ -1571,21 +1583,41 @@
       # ## Mouse events for SCROLLING ##

       track.addEventListener 'mousewheel', (event) =>
-        @clear()
-
-        if event.wheelDelta > 0
-          if @scrollOffset.y >= SCROLL_INTERVAL
-            @scrollOffset.add 0, -SCROLL_INTERVAL
-            @mainCtx.translate 0, SCROLL_INTERVAL
+        # Mousewheel can either scroll the palette
+        # or the main canvas.
+        if event.offsetX < PALETTE_WIDTH
+          @clearPalette()
+
+          if event.wheelDelta > 0
+            if @paletteScrollOffset.y >= SCROLL_INTERVAL
+              @paletteScrollOffset.add 0, -SCROLL_INTERVAL
+              @paletteCtx.translate 0, SCROLL_INTERVAL
+            else
+              # If we would go past the top of the file, just scroll to exactly the top of the file.
+              @paletteCtx.translate 0, @paletteScrollOffset.y
+              @paletteScrollOffset.y = 0
           else
-            # If we would go past the top of the file, just scroll to exactly the top of the file.
-            @mainCtx.translate 0, @scrollOffset.y
-            @scrollOffset.y = 0
+            @paletteScrollOffset.add 0, SCROLL_INTERVAL
+            @paletteCtx.translate 0, -SCROLL_INTERVAL
+
+          @redrawPalette()
+
         else
-          @scrollOffset.add 0, SCROLL_INTERVAL
-          @mainCtx.translate 0, -SCROLL_INTERVAL
-
-        @redraw()
+          @clear()
+
+          if event.wheelDelta > 0
+            if @scrollOffset.y >= SCROLL_INTERVAL
+              @scrollOffset.add 0, -SCROLL_INTERVAL
+              @mainCtx.translate 0, SCROLL_INTERVAL
+            else
+              # If we would go past the top of the file, just scroll to exactly the top of the file.
+              @mainCtx.translate 0, @scrollOffset.y
+              @scrollOffset.y = 0
+          else
+            @scrollOffset.add 0, SCROLL_INTERVAL
+            @mainCtx.translate 0, -SCROLL_INTERVAL
+
+          @redraw()

     setValue: (value) ->
       try


sgconfigure css

sgconfigure css

--- lib/languages.coffee
+++ lib/languages.coffee
@@ -22,9 +22,8 @@

   CSS:
     nameMatchers:      ['.css']
-    pygmentsLexer:     'css'
-    multiLineComment:  ['/*', '*', '*/']
-    singleLineComment: ['/*']
+    pygmentsLexer:     'CSS'
+    multiLineComment:  ['/*', '*', '*/']
     ignorePrefix:      '}'
     foldPrefix:        '^'


Restore saved state when adding folder to empty project via open command

--- src/atom-environment.coffee
+++ src/atom-environment.coffee
@@ -1008,6 +1008,10 @@
       unless fs.isDirectorySync(pathToOpen)
         @workspace?.open(pathToOpen, {initialLine, initialColumn})

+    if needsProjectPaths
+      @loadState(@getStateKey(@project.getPaths())).then (state) =>
+        @restoreStateIntoEnvironment(state) if state
+
     return

 # Preserve this deprecation until 2.0. Sorry. Should have removed Q sooner.


Remove the @currentBreakDecoration that returned after rebase

--- lib/php-debug.coffee
+++ lib/php-debug.coffee
@@ -137,10 +137,6 @@
       if @currentCodePointDecoration
         @currentCodePointDecoration.destroy()

-    @GlobalContext.onRunning () =>
-      if @currentBreakDecoration
-        @currentBreakDecoration.destroy()
-
     @GlobalContext.onWatchpointsChange () =>
       if @GlobalContext.getCurrentDebugContext()
         @GlobalContext.getCurrentDebugContext().syncCurrentContext(0)
@@ -257,9 +253,6 @@
     if @currentCodePointDecoration
       @currentCodePointDecoration.destroy()

-    if @currentBreakDecoration
-      @currentBreakDecoration.destroy()
-
     if @settingsView
       @settingsView.close()
       @settingsView.destroy()


adjust the menu

adjust the menu

--- menus/atom-chameleon.cson
+++ menus/atom-chameleon.cson
@@ -2,7 +2,7 @@
 'context-menu':
   '.tree-view.full-menu': [
     {'type': 'separator'}
-    {label: 'Chameleon Package Manager', command: 'atom-chameleon:gitCreatePackage'}
+    {label: 'Share Chameleon Package', command: 'atom-chameleon:gitCreatePackage'}
     {label: 'Install Chameleon Package', command: 'atom-chameleon:installChameleonPackage'}
     {'type': 'separator'}
   ]


subprocess: Add some more debug

subprocess: Add some more debug

--- src/subprocess.coffee
+++ src/subprocess.coffee
@@ -24,6 +24,7 @@
   child.stdout.on 'data', (data) ->
     stdout += data.toString()
     if not started
+      debug 'got output, transitioning to started'
       started = true
       # give process some time to open port
       setTimeout callback, 100
@@ -31,6 +32,7 @@
   child.stderr.on 'data', (data) ->
     stderr += data.toString()
     if not started
+      debug 'got stderr, failing'
       started = true
       return callback new Error "Subprocess wrote on stderr: '#{stderr}'"


Make handling of packet received event a little easier to read.

--- lib/connection.coffee
+++ lib/connection.coffee
@@ -65,23 +65,28 @@
   eventPacket: (packet) =>
     switch @state
       when STATE.SENT_PRELOGIN
-        if packet.isLast()
-          preloginPayload = new PreloginPayload(packet.data())
-          @debug.payload(preloginPayload.toString('  '))
+        @processPreloginResponsePacket(packet)
+      when STATE.SENT_LOGIN7
+        @processLogin7ResponsePacket(packet)

-          @sendLogin7Packet()
-        else
-          # Naive, but reasonable, assumption not valid.
-          fatalError("Expected only a single packet response from PRELOGIN message")
+  processPreloginResponsePacket: (packet) ->
+    if packet.isLast()
+      preloginPayload = new PreloginPayload(packet.data())
+      @debug.payload(preloginPayload.toString('  '))

-      when STATE.SENT_LOGIN7
-        if packet.isLast()
-          console.log('LOGIN7 response')
-          console.log(packet.data())
-          @activeRequest.callback(undefined, true)
-        else
-          # Naive, but reasonable, assumption not valid.
-          fatalError("Expected only a single packet response from LOGIN7 message")
+      @sendLogin7Packet()
+    else
+      # Naive, but reasonable, assumption not valid.
+      fatalError("Expected only a single packet response from PRELOGIN message")
+
+  processLogin7ResponsePacket: (packet) ->
+    if packet.isLast()
+      console.log('LOGIN7 response')
+      console.log(packet.data())
+      @activeRequest.callback(undefined, true)
+    else
+      # Naive, but reasonable, assumption not valid.
+      fatalError("Expected only a single packet response from LOGIN7 message")

   startRequest: (requestName, callback) =>
     @activeRequest =


add same aliases for all backbone event subscription methods

--- src/chaplin/mediator.coffee
+++ src/chaplin/mediator.coffee
@@ -27,10 +27,10 @@

 # Mixin event methods from Backbone.Events,
 # create Publish/Subscribe aliases.
-mediator.subscribe     = Backbone.Events.on
-mediator.subscribeOnce = Backbone.Events.once
-mediator.unsubscribe   = Backbone.Events.off
-mediator.publish       = Backbone.Events.trigger
+mediator.subscribe     = mediator.on      = Backbone.Events.on
+mediator.subscribeOnce = mediator.once    = Backbone.Events.once
+mediator.unsubscribe   = mediator.off     = Backbone.Events.off
+mediator.publish       = mediator.trigger = Backbone.Events.trigger

 # Initialize an empty callback list so we might seal the mediator later.
 mediator._callbacks = null


server-side of single-instance mts deployment

--- lib/deployment.coffee
+++ lib/deployment.coffee
@@ -28,6 +28,7 @@

     cls::deploy_at_server = ->
         @deployment ?= {}
+        @deployment.instance ?= 'run'
         switch
             when fs.existsSync "#{@root}/.bzr"
                 shell.pushd @root
@@ -55,10 +56,15 @@
             console.log "meteor bundle #{@config.deployment.target}/_bundles/#{@deployment.revision}.tar.gz"
             # async because shelljs' docs tell us to use async for long-running
             # processes, or it uses too much cpu waiting
-            shell.exec "meteor bundle #{@config.deployment.target}/_bundles/#{@deployment.revision}.tar.gz", (code, output) ->
+            shell.exec "meteor bundle #{@config.deployment.target}/_bundles/#{@deployment.revision}.tar.gz", (code, output) =>
+                shell.popd()
                 if code
                     throw new RunError "meteor bundle failed with error code #{code}"

-
-
+                shell.mkdir '-p', "#{@config.deployment.target}/_tree/#{@deployment.revision}"
+                shell.pushd "#{@config.deployment.target}"
+                shell.exec "tar -C _tree/#{@deployment.revision} --strip-components=1 -xf _bundles/#{@deployment.revision}.tar.gz"
+                # shelljs master has ln, but no release yet does
+                # shell.ln '-fs', "_tree/#{@deployment.revision}", @deployment.instance
+                shell.exec "ln -fs _tree/#{@deployment.revision} #{@deployment.instance}"
                 process.exit 0


Extract partTypes

Extract partTypes

--- lib/routes/v2.coffee
+++ lib/routes/v2.coffee
@@ -4,6 +4,7 @@
 common     = require('./common.coffee')
 imager     = require('../imager.coffee')
 potato     = require('../potato.coffee')
+partTypes  = ['eyes', 'nose', 'mouth']

 router.param 'id', (req, res, next, id) ->
   faceParts = potato.parts(id)
@@ -23,7 +24,7 @@
 router.get '/face/:eyes/:nose/:mouth/:color', (req, res, next) ->
   faceParts = color: "##{req.params.color}"

-  ['eyes', 'nose', 'mouth'].forEach (type) ->
+  partTypes.forEach (type) ->
     possibleFileNames = ImageFiles.allNames(type)
     requestedFileName = req.params[type]


:heavy_plus_sign: add another error gif

:heavy_plus_sign: add another error gif

--- js/load-error-image.coffee
+++ js/load-error-image.coffee
@@ -2,6 +2,6 @@
 ---

 this.loadErrorImage = ->
-    imgCnt = 7
+    imgCnt = 8
     rnd = 1 + Math.floor(Math.random() * imgCnt)
     document.body.style.backgroundImage = "url({{ site.baseurl }}/images/errors/" + rnd + ".gif"


Respond to code review - eliminate `hasAccumulatorAncestor` - restore `isOwnGrandpa`

--- src/code/models/node.coffee
+++ src/code/models/node.coffee
@@ -53,8 +53,7 @@

     @color ?= Colors.choices[0].value

-    @isInCycle = false      # we always initalize with no links, so we can't be in cycle
-    @hasAccumulatorAncestor = false
+    @isInDependentCycle = false   # we always initalize with no links, so we can't be in cycle

     @_collectorImageProps = null

@@ -132,23 +131,21 @@
   checkIsInIndependentCycle: ->
     visitedNodes = []
     original = this
-    hasAccumulatorAncestor = false
-    isOwnAncestor = false
+    isOwnGrandpa = false

     visit = (node) ->
       visitedNodes.push node
       for link in node.inLinks() when link.relation?.isDefined
         upstreamNode = link.sourceNode
-        return true unless upstreamNode.isDependent(true)      # fast exit if we have an independent ancestor
-        if upstreamNode is original then isOwnAncestor = true
-        if upstreamNode.isAccumulator then hasAccumulatorAncestor = true
+        return true if upstreamNode.isAccumulator         # fast exit if we have a collector ancestor
+        return true unless upstreamNode.isDependent(true) # or an independent ancestor
+        if upstreamNode is original then isOwnGrandpa = true
         unless _.contains visitedNodes, upstreamNode
           return true if visit upstreamNode

     hasIndependentAncestor = visit @

-    @isInCycle = not hasIndependentAncestor and isOwnAncestor
-    @hasAccumulatorAncestor = hasAccumulatorAncestor
+    @isInDependentCycle = not hasIndependentAncestor and isOwnGrandpa

   infoString: ->
     linkNamer = (link) ->
@@ -224,7 +221,7 @@
       frames: _.clone @frames

   canEditInitialValue: ->
-    not @isDependent(true) or @isAccumulator or (@isInCycle and not @hasAccumulatorAncestor)
+    not @isDependent(true) or @isAccumulator or @isInDependentCycle

   canEditValueWhileRunning: ->
     not @isDependent(true)


added a api for status

added a api for status

--- src/coffee/ScoreView.coffee
+++ src/coffee/ScoreView.coffee
@@ -38,7 +38,9 @@
    * @param {String} status
   ###
   setStatus:( status )->
+    {ehAttrUser, ehAttrType, ehAttrTarget} = @el.dataset
     @data_.status = status
+    api.putComment ehAttrUser, ehAttrType, ehAttrTarget, @data_

   ###
    * @type String


e -> event

e -> event

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -588,7 +588,7 @@
        (e.button is 2 or e.ctrlKey && process.platform is 'darwin')
       return

-    entryToSelect = e.currentTarget
+    entryToSelect = event.currentTarget

     if e.shiftKey
       @selectContinuousEntries(entryToSelect)


Set attribute on the element directly

Set attribute on the element directly

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -596,7 +596,7 @@
   onSideToggled: (newValue) ->
     @detach()
     @attach()
-    @attr('data-show-on-right-side', newValue)
+    @[0].setAttribute('data-show-on-right-side', newValue)

   # Public: Return an array of paths from all selected items
   #


fix unselect multiple entiries on right click

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -803,7 +803,7 @@

       # return early if we're opening a contextual menu (right click) during multi-select mode
       if @multiSelectEnabled() and
-         e.target.classList.contains('selected') and
+         entryToSelect.classList.contains('selected') and
          # mouse right click or ctrl click as right click on darwin platforms
          (e.button is 2 or e.ctrlKey and process.platform is 'darwin')
         return


Use atom.styles to observe style sheets

Use atom.styles to observe style sheets

Signed-off-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -39,7 +39,10 @@

     process.nextTick =>
       @onStylesheetsChanged()
-      @subscribe atom.themes, 'stylesheets-changed', _.debounce(@onStylesheetsChanged, 100)
+      onStylesheetsChanged = _.debounce(@onStylesheetsChanged, 100)
+      @subscribe atom.styles.onDidAddStyleElement(onStylesheetsChanged)
+      @subscribe atom.styles.onDidRemoveStyleElement(onStylesheetsChanged)
+      @subscribe atom.styles.onDidUpdateStyleElement(onStylesheetsChanged)

     @updateRoot(state.directoryExpansionStates)
     @selectEntry(@root) if @root?


:art:

:art:

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -461,27 +461,27 @@
     isFile = entry instanceof FileView
     {command, args, label} = @fileManagerCommandForPath(entry.getPath(), isFile)

-    handleError = (error) ->
+    handleError = (errorMessage) ->
       atom.notifications.addError "Opening #{if isFile then 'file' else 'folder'} in #{label} failed",
-        detail: error.message
+        detail: errorMessage
         dismissable: true

     errorLines = []
     stderr = (lines) -> errorLines.push(lines)
     exit = (code) ->
       failed = code isnt 0
-      error = errorLines.join('\n')
+      errorMessage = errorLines.join('\n')

       # Windows 8 seems to return a 1 with no error output even on success
       if process.platform is 'win32' and code is 1 and not error
         failed = false

-      handleError(message: error) if failed
+      handleError(errorMessage) if failed

     showProcess = new BufferedProcess({command, args, stderr, exit})
     showProcess.onWillThrowError ({error, handle}) ->
       handle()
-      handleError(error)
+      handleError(error?.message)

   openSelectedEntryInNewWindow: ->
     if pathToOpen = @selectedEntry()?.getPath()


Unused method

Unused method


Fix sending token by API

Fix sending token by API

--- app/api/article.coffee
+++ app/api/article.coffee
@@ -33,6 +33,7 @@
     if not group?
       return res.sendStatus 404
     Q.all group.users.map (user) ->
+      user = user.toJSON()
       return db.collections.article.find
         where:
           group: group.id
@@ -81,7 +82,7 @@
   .then (article) ->
     # TODO add voteentry / tagged handling
     obj = article.toJSON()
-    obj.author = req.user
+    obj.author = req.user.toJSON()
     res.json obj
   .catch (e) ->
     res.sendStatus 400


Added a view questions command.

Added a view questions command.

--- scripts/chat.coffee
+++ scripts/chat.coffee
@@ -44,7 +44,6 @@
 		robot.brain.data.questions ?= []

 		robot.brain.data.questions = question_bank unless robot.brain.data.questions.length
-		robot.logger.info robot.brain.data.questions
 		current_question = null

 		# Fisher-Yates shuffle in Coffeescript https://gist.github.com/smrchy/3098096
@@ -107,6 +106,11 @@
 			robot.brain.data.questions.splice(question_cull, 1)
 			msg.send "OK, I've removed question ID \"#{question_cull}\" from the question bank"

+		# see questions
+		robot.hear /view4 (.*)$/i, (msg) ->
+			robot.logger.info robot.brain.data.questions
+			msg.send "Questions displayed in log."
+


Add temporary hour of code redirects. (#129)

--- app/core/Router.coffee
+++ app/core/Router.coffee
@@ -154,7 +154,7 @@

     'github/*path': 'routeToServer'

-    'hoc': -> @navigate "/play/hoc-2018", {trigger: true, replace: true}
+    'hoc': redirect('/play/prologue-sky-mountain')
     'home': go('HomeView')

     'i18n': go('i18n/I18NHomeView')
@@ -269,7 +269,7 @@
     'teachers/course-solution/:courseID/:language': go('teachers/TeacherCourseSolutionView', { redirectStudents: true })
     'teachers/demo': redirect('/teachers/quote')
     'teachers/enrollments': redirect('/teachers/licenses')
-    'teachers/hour-of-code': go('special_event/HoC2018View')
+    'teachers/hour-of-code': redirect('/teachers/resources/1fhLessonPlan')
     'teachers/licenses': go('courses/EnrollmentsView', { redirectStudents: true, teachersOnly: true })
     'teachers/freetrial': go('teachers/RequestQuoteView', { redirectStudents: true })
     'teachers/quote': go('teachers/RequestQuoteView', { redirectStudents: true })


Render map view after everything else to make mobile a bit faster.

--- web/js/main.coffee
+++ web/js/main.coffee
@@ -73,10 +73,10 @@

   $ ->

-    new MapView().render()
     new SearchView().render()
     new BlankSlateView().render()
     new RoutesView().render()
+    new MapView().render()

     if Reitti.Settings.get('canWatchPosition')
       Reitti.Position.startWatching()


jupyter: cell tag style

jupyter: cell tag style

--- src/smc-webapp/jupyter/cell-toolbar-tags.cjsx
+++ src/smc-webapp/jupyter/cell-toolbar-tags.cjsx
@@ -14,6 +14,7 @@
     background   : '#5bc0de'
     borderRadius : '3px'
     color        : 'white'
+    display      : 'inline-block'

 exports.TagsToolbar = rclass
     propTypes :
@@ -40,7 +41,9 @@
         t = @props.cell.get('tags')?.toJS()
         if not t?
             return
-        (@render_tag(tag) for tag in misc.keys(t).sort())
+        <div style={flex:1}>
+            {(@render_tag(tag) for tag in misc.keys(t).sort())}
+        </div>

     render_tag_input: ->
         <FormControl
@@ -50,7 +53,7 @@
             type        = 'text'
             value       = {@state.input}
             onChange    = {=>@setState(input : ReactDOM.findDOMNode(@refs.input).value)}
-            style       = {flex:1}
+            style       = {height:'34px'}
             bsSize      = {'small'}
             onKeyDown   = {(e) => if e.which == 13 then @add_tags(); return}
             />
@@ -67,6 +70,7 @@
             disabled = {@state.input.length == 0}
             title    = 'Add tag or tags (separate by spaces)'
             onClick  = {@add_tags}
+            style    = {height:'34px'}
         >
             Add
         </Button>


Defer the initial call to updateActions

Defer the initial call to updateActions

--- src/trix/controllers/editor_controller.coffee
+++ src/trix/controllers/editor_controller.coffee
@@ -269,7 +269,7 @@
     unless @toolbarController
       @toolbarController = new Trix.ToolbarController @toolbarElement
       @toolbarController.delegate = this
-    @toolbarController.updateActions()
+    defer => @toolbarController.updateActions()

   createDocumentController: ->
     delete @documentController?.delegate


inline -> text to better match Trix domain

--- src/trix/elements/trix_toolbar_element.coffee
+++ src/trix/elements/trix_toolbar_element.coffee
@@ -21,7 +21,7 @@

   @defaultHTML: """

-
+
         <button type="button" class="bold" data-attribute="bold" data-key="b">Bold</button>
         <button type="button" class="italic" data-attribute="italic" data-key="i">Italic</button>
         <button type="button" class="strike" data-attribute="strike">Strike</button>


Cleanup

Cleanup

--- scripts/main.coffee
+++ scripts/main.coffee
@@ -1,49 +1,45 @@
 $ = require 'jQuery'

-CreaturePicker = require 'controllers/CreaturePicker'
-Classifier = require 'controllers/Classifier'
-Subject = require 'models/Subject'
+Pager = require 'controllers/Pager'
+window.pagers = (new Pager el: parent for parent in $('[data-page]').parent())

-# TODO: Make this an external resource.
-# They've been copied over from Bat Detective for now.
-Pager = require 'controllers/Pager'
 NestedRoute = require 'NestedRoute'
+NestedRoute.setup()

 ScrollMatcher = require 'controllers/ScrollMatcher'
+window.scrollMatchers = (new ScrollMatcher el : parent for parent in $('[data-scroll-name]').parent())

 layout = require 'layout'
-
-window.pagers = $('[data-page]').parent().map -> new Pager el: @
-NestedRoute.setup()
-
-window.scrollMatchers = $('[data-scroll-name]').parent().map -> new ScrollMatcher el : @
-
-$(window).resize layout
+$(window).on 'resize', layout
 $(document).ready layout

+# Prevent scrolling the page on iPads.
 $(document).on 'touchmove', (e) -> e.preventDefault()

 # From http://habcam.whoi.edu/habcam2.html
 sampleImages = [
-	'UNQ.20060928.010920609.jpg'
-	'UNQ.20070726.073105937.jpg'
-	'UNQ.20080731.043833900.jpg'
-	'UNQ.20080811.062244403.jpg'
-	'UNQ.20090626.113420984.jpg'
-	'UNQ.20090627.090015906.jpg'
-	'UNQ.20090629.000241062.jpg'
-	'UNQ.20090629.145917171.jpg'
-	'UNQ.20090714.222541046.jpg'
-	'UNQ.20091201.023850359.jpg'
+	'sample-images/UNQ.20060928.010920609.jpg'
+	'sample-images/UNQ.20070726.073105937.jpg'
+	'sample-images/UNQ.20080731.043833900.jpg'
+	'sample-images/UNQ.20080811.062244403.jpg'
+	'sample-images/UNQ.20090626.113420984.jpg'
+	'sample-images/UNQ.20090627.090015906.jpg'
+	'sample-images/UNQ.20090629.000241062.jpg'
+	'sample-images/UNQ.20090629.145917171.jpg'
+	'sample-images/UNQ.20090714.222541046.jpg'
+	'sample-images/UNQ.20091201.023850359.jpg'
 ]

+Subject = require 'models/Subject'
 for sampleImage, i in sampleImages
 	Subject.create
-		image: "sample-images/#{sampleImage}"
+		image: sampleImage
 		latitude: parseFloat String(Math.random() * 180)[0..4]
 		longitude: parseFloat String(Math.random() * 180)[0..4]
 		depth: i * 10

+Classifier = require 'controllers/Classifier'
+CreaturePicker = require 'controllers/CreaturePicker'
 window.classifier = new Classifier
 	el: $('#classifier')
 	picker: new CreaturePicker


fix choices not defined

fix choices not defined

--- lib/pegg-parse.coffee
+++ lib/pegg-parse.coffee
@@ -148,12 +148,12 @@
                 @create type: 'Choice', object: choice
             )
           .then (parseChoices) =>
-            for choice, i in choices
+            for choice, i in post.choices
               choice.id = parseChoices[i].id
               choice.cardId = cardId
               choice.card = undefined
               choice.ACL = undefined
-            card.choices = _.keyBy choices, 'id'
+            card.choices = _.keyBy post.choices, 'id'
             parseCard.set 'choices', card.choices
             parseCard.save null, { useMasterKey: true }
           .then =>


fix: print correct seqs

fix: print correct seqs

Former-commit-id: 4fb8f274d05b38b60dc4623d9021175d7352ac14 [formerly 50ab6fc7d8e043b60006fe916cbce81a7e06e0e5]
Former-commit-id: ccb085fb651ed42eedd82a64e6f8bbd8f3a1203f

--- src/utils/tree.coffee
+++ src/utils/tree.coffee
@@ -22,7 +22,7 @@
       treeDiv = document.createElement "div"
       document.body.appendChild treeDiv

-      console.log seqs
+      console.log @msa.seqs.models
       console.log tree

       nodes = mt.app


Clean up a minor syntax issue raised in the previous PR.

--- scripts/vote.coffee
+++ scripts/vote.coffee
@@ -130,8 +130,7 @@
   # Election driver
   robot.respond /(?:vote|freedom|oppressed) (\d+ )?([^\s]+)\s?(.*)?$/, (msg) ->
     timeout = 1 # default timeout is 1 minute
-    if msg.match[1] and msg.match[1].length > 0
-      timeout = parseInt(msg.match[1].trim())
+    timeout = parseInt(msg.match[1].trim()) if msg.match[1]?.length > 0

     action = msg.match[2].trim().toLowerCase()
     arg = msg.match[3]?.trim()


:bug: Fix deprecation on commands handling

--- lib/table-view.coffee
+++ lib/table-view.coffee
@@ -1,9 +1,12 @@
-{Point, Range, TextEditorView, View} = require 'atom'
+{Point, Range} = require 'atom'
+{View} = require 'space-pen'
+{TextEditorView} = require 'atom-space-pen-views'
 {CompositeDisposable, Disposable} = require 'event-kit'
 PropertyAccessors = require 'property-accessors'
 React = require 'react-atom-fork'
 TableComponent = require './table-component'
 TableHeaderComponent = require './table-header-component'
+

 module.exports =
 class TableView extends View
@@ -34,7 +37,7 @@
           @startEdit()
           @editView.setText(e.originalEvent.data)

-    @subscribeTo this,
+    @subscriptions.add atom.commands.add '.table-edit',
       'core:confirm': => @startEdit()
       'core:undo': => @table.undo()
       'core:redo': => @table.redo()
@@ -54,6 +57,8 @@
       'table-edit:select-to-beginning-of-line': => @expandSelectionToBeginningOfLine()
       'table-edit:select-to-end-of-table': => @expandSelectionToEndOfTable()
       'table-edit:select-to-beginning-of-table': => @expandSelectionToBeginningOfTable()
+
+    @subscribeTo this,
       'mousedown': (e) =>
         e.stopPropagation()
         e.preventDefault()
@@ -449,7 +454,7 @@
   #     ######   #######  ##    ##    ##    ##     ##  #######  ########

   focus: ->
-    @hiddenInput.focus() unless document.activeElement is @hiddenInput.element
+    @hiddenInput.focus() unless document.activeElement is @hiddenInput[0]

   moveRight: ->
     if @activeCellPosition.column + 1 < @table.getColumnsCount()
@@ -557,7 +562,7 @@
     .height(activeCellRect.height)
     .show()

-    @editView.find('.hidden-input').focus()
+    @editView.focus()

     @editView.setText(activeCell.getValue().toString())

@@ -581,23 +586,22 @@
     @append(@editView)

   subscribeToTextEditor: (editorView) ->
-    @subscriptions.add @asDisposable editorView.on 'table-edit:move-right', (e) =>
-      @confirmEdit()
-      @moveRight()
-
-    @subscriptions.add @asDisposable editorView.on 'table-edit:move-left', (e) =>
-      @confirmEdit()
-      @moveLeft()
-
-    @subscriptions.add @asDisposable editorView.on 'core:cancel', (e) =>
-      @stopEdit()
-      e.stopImmediatePropagation()
-      return false
-
-    @subscriptions.add @asDisposable editorView.on 'core:confirm', (e) =>
-      @confirmEdit()
-      e.stopImmediatePropagation()
-      return false
+    @subscriptions.add atom.commands.add '.table-edit atom-text-editor',
+      'table-edit:move-right': (e) =>
+        @confirmEdit()
+        @moveRight()
+      'table-edit:move-left': (e) =>
+        @confirmEdit()
+        @moveLeft()
+      'core:cancel': (e) =>
+        @stopEdit()
+        e.stopImmediatePropagation()
+        return false
+      'core:confirm': (e) =>
+        console.log 'core:confirm'
+        @confirmEdit()
+        e.stopImmediatePropagation()
+        return false

   #     ######  ######## ##       ########  ######  ########
   #    ##    ## ##       ##       ##       ##    ##    ##


Remeasure on resize

Remeasure on resize

--- src/domains/Document.coffee
+++ src/domains/Document.coffee
@@ -68,6 +68,7 @@
         @solve id + ' resized', ->
           @intrinsic.verify(id, "width")
           @intrinsic.verify(id, "height")
+          @intrinsic.solve([])
       , 20

     scroll: (e = '::window') ->