Update file counts

Update file counts

--- spec/path-scanner-spec.coffee
+++ spec/path-scanner-spec.coffee
@@ -29,9 +29,9 @@
       runs ->
         # symlink-to-file1.txt is a file on windows
         if process.platform is 'win32'
+          expect(paths.length).toBe 18
+        else
           expect(paths.length).toBe 17
-        else
-          expect(paths.length).toBe 16

         expect(paths).toContain path.join(rootPath, 'file1.txt')
         expect(paths).toContain path.join(rootPath, 'dir', 'file7_ignorable.rb')


#364 range-marker have no behavior diff of inner and a

--- spec/range-marker-spec.coffee
+++ spec/range-marker-spec.coffee
@@ -55,22 +55,7 @@
           ensureRangeMarker length: 1, text: ['ooo']
           keystroke('j .')
           ensureRangeMarker length: 2, text: ['ooo', 'xxx']
-      describe "inner-range-marker", ->
-        it "apply operator only to cursor contained range-marker", ->
-          keystroke('g m i w j . 2 j g m i p') # Mark 2 inner-word and 1 inner-paragraph
-          ensureRangeMarker length: 3, text: ['ooo', 'xxx', "ooo xxx ooo\nxxx ooo xxx\n"]
-          ensure 'g U i r',
-            text: """
-            ooo xxx ooo
-            xxx ooo xxx
-
-            OOO XXX OOO
-            XXX OOO XXX
-
-            ooo xxx ooo
-            xxx ooo xxx\n
-            """
-      describe "a-range-marker", ->
+      describe "[No behavior diff currently] inner-range-marker and a-range-marker", ->
         it "apply operator to across all range-markers", ->
           keystroke('g m i w j . 2 j g m i p') # Mark 2 inner-word and 1 inner-paragraph
           ensureRangeMarker length: 3, text: ['ooo', 'xxx', "ooo xxx ooo\nxxx ooo xxx\n"]


closes #149

closes #149

--- src/quill.coffee
+++ src/quill.coffee
@@ -133,7 +133,10 @@
     return @editor.getDelta().slice(start, end)

   getHTML: ->
-    return @root.innerHTML
+    html = @root.innerHTML
+    # Preserve spaces between tags
+    html = html.replace(/\>\s+\</g, '> <')
+    return html

   getLength: ->
     return @editor.getDelta().length()


Removed unnecesary test

Removed unnecesary test

--- spec/scratch-view-spec.coffee
+++ spec/scratch-view-spec.coffee
@@ -1,5 +0,0 @@
-ScratchView = require '../lib/scratch-view'
-
-describe "ScratchView", ->
-  it "has one valid test", ->
-    expect("life").toBe "easy"


:shirt: Fix linter errors

:shirt: Fix linter errors

--- spec/symbols-view-spec.coffee
+++ spec/symbols-view-spec.coffee
@@ -123,7 +123,7 @@

     it "moves the cursor to the selected function", ->
       runs ->
-        expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [0,0]
+        expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [0, 0]
         expect($(getWorkspaceView()).find('.symbols-view')).not.toExist()
         atom.commands.dispatch(getEditorView(), "symbols-view:toggle-file-symbols")

@@ -132,7 +132,7 @@

       runs ->
         $(getWorkspaceView()).find('.symbols-view').find('li:eq(1)').mousedown().mouseup()
-        expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [1,2]
+        expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [1, 2]

   describe "when tags can't be generated for a file", ->
     beforeEach ->
@@ -191,14 +191,14 @@

       runs ->
         editor = atom.workspace.getActiveTextEditor()
-        editor.setCursorBufferPosition([0,2])
+        editor.setCursorBufferPosition([0, 2])
         atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration')

       waitsForPromise ->
         activationPromise

       runs ->
-        expect(editor.getCursorBufferPosition()).toEqual [0,2]
+        expect(editor.getCursorBufferPosition()).toEqual [0, 2]

     it "moves the cursor to the declaration when there is a single matching declaration", ->
       waitsForPromise ->
@@ -206,7 +206,7 @@

       runs ->
         editor = atom.workspace.getActiveTextEditor()
-        editor.setCursorBufferPosition([6,24])
+        editor.setCursorBufferPosition([6, 24])
         spyOn(SymbolsView.prototype, "moveToPosition").andCallThrough()
         atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration')

@@ -214,7 +214,7 @@
         SymbolsView::moveToPosition.callCount is 1

       runs ->
-        expect(editor.getCursorBufferPosition()).toEqual [2,0]
+        expect(editor.getCursorBufferPosition()).toEqual [2, 0]

     it "displays matches when more than one exists and opens the selected match", ->
       waitsForPromise ->
@@ -222,7 +222,7 @@

       runs ->
         editor = atom.workspace.getActiveTextEditor()
-        editor.setCursorBufferPosition([8,14])
+        editor.setCursorBufferPosition([8, 14])
         atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration')

       waitsFor ->
@@ -240,7 +240,7 @@

       runs ->
         expect(atom.workspace.getActiveTextEditor().getPath()).toBe directory.resolve("tagged-duplicate.js")
-        expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [0,4]
+        expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [0, 4]

     it "includes ? and ! characters in ruby symbols", ->
       atom.project.setPaths([temp.mkdirSync("atom-symbols-view-ruby-")])
@@ -254,7 +254,7 @@

       runs ->
         spyOn(SymbolsView.prototype, "moveToPosition").andCallThrough()
-        atom.workspace.getActiveTextEditor().setCursorBufferPosition([13,4])
+        atom.workspace.getActiveTextEditor().setCursorBufferPosition([13, 4])
         atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration')

       waitsForPromise ->
@@ -264,25 +264,25 @@
         SymbolsView::moveToPosition.callCount is 1

       runs ->
-        expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [5,2]
+        expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [5, 2]
         SymbolsView::moveToPosition.reset()
-        atom.workspace.getActiveTextEditor().setCursorBufferPosition([14,2])
+        atom.workspace.getActiveTextEditor().setCursorBufferPosition([14, 2])
         atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration')

       waitsFor ->
         SymbolsView::moveToPosition.callCount is 1

       runs ->
-        expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [9,2]
+        expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [9, 2]
         SymbolsView::moveToPosition.reset()
-        atom.workspace.getActiveTextEditor().setCursorBufferPosition([15,5])
+        atom.workspace.getActiveTextEditor().setCursorBufferPosition([15, 5])
         atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration')

       waitsFor ->
         SymbolsView::moveToPosition.callCount is 1

       runs ->
-        expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [1,2]
+        expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [1, 2]

     describe "return from declaration", ->
       it "doesn't do anything when no go-to have been triggered", ->
@@ -291,14 +291,14 @@

         runs ->
           editor = atom.workspace.getActiveTextEditor()
-          editor.setCursorBufferPosition([6,0])
+          editor.setCursorBufferPosition([6, 0])
           atom.commands.dispatch(getEditorView(), 'symbols-view:return-from-declaration')

         waitsForPromise ->
           activationPromise

         runs ->
-          expect(editor.getCursorBufferPosition()).toEqual [6,0]
+          expect(editor.getCursorBufferPosition()).toEqual [6, 0]

       it "returns to previous row and column", ->
         waitsForPromise ->
@@ -306,7 +306,7 @@

         runs ->
           editor = atom.workspace.getActiveTextEditor()
-          editor.setCursorBufferPosition([6,24])
+          editor.setCursorBufferPosition([6, 24])
           spyOn(SymbolsView.prototype, "moveToPosition").andCallThrough()
           atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration')

@@ -317,14 +317,14 @@
           SymbolsView::moveToPosition.callCount is 1

         runs ->
-          expect(editor.getCursorBufferPosition()).toEqual [2,0]
+          expect(editor.getCursorBufferPosition()).toEqual [2, 0]
           atom.commands.dispatch(getEditorView(), 'symbols-view:return-from-declaration')

         waitsFor ->
           SymbolsView::moveToPosition.callCount is 2

         runs ->
-          expect(editor.getCursorBufferPosition()).toEqual [6,24]
+          expect(editor.getCursorBufferPosition()).toEqual [6, 24]

     describe "when the tag is in a file that doesn't exist", ->
       it "doesn't display the tag", ->
@@ -335,7 +335,7 @@

         runs ->
           editor = atom.workspace.getActiveTextEditor()
-          editor.setCursorBufferPosition([8,14])
+          editor.setCursorBufferPosition([8, 14])
           spyOn(SymbolsView.prototype, "moveToPosition").andCallThrough()
           atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration')

@@ -343,7 +343,7 @@
           SymbolsView::moveToPosition.callCount is 1

         runs ->
-          expect(editor.getCursorBufferPosition()).toEqual [8,0]
+          expect(editor.getCursorBufferPosition()).toEqual [8, 0]

   describe "project symbols", ->
     it "displays all tags", ->


Rename editSession to editor

Rename editSession to editor

--- spec/symbols-view-spec.coffee
+++ spec/symbols-view-spec.coffee
@@ -41,7 +41,7 @@
         expect(symbolsView.error).not.toBeVisible()

     it "caches tags until the buffer changes", ->
-      editSession = atom.workspaceView.openSync('sample.js')
+      editor = atom.workspaceView.openSync('sample.js')
       atom.workspaceView.getActiveView().trigger "symbols-view:toggle-file-symbols"
       symbolsView = atom.workspaceView.find('.symbols-view').view()

@@ -61,7 +61,7 @@
         expect(symbolsView.loading).toBeEmpty()
         expect(symbolsView.list.children('li').length).toBe 2
         expect(symbolsView.generateTags).not.toHaveBeenCalled()
-        editSession.getBuffer().emit 'saved'
+        editor.getBuffer().emit 'saved'
         setArraySpy.reset()
         symbolsView.cancel()
         atom.workspaceView.getActiveView().trigger "symbols-view:toggle-file-symbols"
@@ -73,7 +73,7 @@
         expect(symbolsView.loading).toBeEmpty()
         expect(symbolsView.list.children('li').length).toBe 2
         expect(symbolsView.generateTags).toHaveBeenCalled()
-        editSession.destroy()
+        editor.destroy()
         expect(symbolsView.cachedTags).toEqual {}

     it "displays error when no tags match text in mini-editor", ->


Make nicer.

Make nicer.

--- js/flight.coffee
+++ js/flight.coffee
@@ -27,8 +27,7 @@

     @currentPanel = document.getElementsByClassName('visible')[0]

-    if @currentPanel is undefined
-      throw new Error "Current panel not set"
+    if @currentPanel is undefined then throw new Error "Current panel not set"

     @fitHeightToContent()
     @detectUA()


:bug: Fix focus keymaps for empty panes

:bug: Fix focus keymaps for empty panes

Previously, focus on an empty pane would break the focus pane keymaps,
as the keymaps were scoped to
`atom-text-editor.vim-mode-plus:not(.insert-mode)`.

--- keymaps/vim-mode-plus.cson
+++ keymaps/vim-mode-plus.cson
@@ -5,6 +5,23 @@
   # conflicts insert-mode ctrl-w and ctr-w in mini editor, so comment out by default
   # 'ctrl-w z': 'vim-mode-plus:maximize-pane'
   'cmd-enter': 'vim-mode-plus:maximize-pane'
+
+  'ctrl-w ctrl-h': 'window:focus-pane-on-left'
+  'ctrl-w h': 'window:focus-pane-on-left'
+  'ctrl-w left': 'window:focus-pane-on-left'
+  'ctrl-w ctrl-l': 'window:focus-pane-on-right'
+  'ctrl-w l': 'window:focus-pane-on-right'
+  'ctrl-w right': 'window:focus-pane-on-right'
+  'ctrl-w ctrl-k': 'window:focus-pane-above'
+  'ctrl-w k': 'window:focus-pane-above'
+  'ctrl-w up': 'window:focus-pane-above'
+  'ctrl-w ctrl-j': 'window:focus-pane-below'
+  'ctrl-w j': 'window:focus-pane-below'
+  'ctrl-w down': 'window:focus-pane-below'
+  'ctrl-w ctrl-w': 'window:focus-next-pane'
+  'ctrl-w w': 'window:focus-next-pane'
+  'ctrl-w ctrl-p': 'window:focus-previous-pane'
+  'ctrl-w p': 'window:focus-previous-pane'

 # all
 # -------------------------
@@ -148,23 +165,6 @@
   'ctrl-x': 'vim-mode-plus:decrease'
   'g ctrl-a': 'vim-mode-plus:increment-number' # experimental
   'g ctrl-x': 'vim-mode-plus:decrement-number' # experimental
-
-  'ctrl-w ctrl-h': 'window:focus-pane-on-left'
-  'ctrl-w h': 'window:focus-pane-on-left'
-  'ctrl-w left': 'window:focus-pane-on-left'
-  'ctrl-w ctrl-l': 'window:focus-pane-on-right'
-  'ctrl-w l': 'window:focus-pane-on-right'
-  'ctrl-w right': 'window:focus-pane-on-right'
-  'ctrl-w ctrl-k': 'window:focus-pane-above'
-  'ctrl-w k': 'window:focus-pane-above'
-  'ctrl-w up': 'window:focus-pane-above'
-  'ctrl-w ctrl-j': 'window:focus-pane-below'
-  'ctrl-w j': 'window:focus-pane-below'
-  'ctrl-w down': 'window:focus-pane-below'
-  'ctrl-w ctrl-w': 'window:focus-next-pane'
-  'ctrl-w w': 'window:focus-next-pane'
-  'ctrl-w ctrl-p': 'window:focus-previous-pane'
-  'ctrl-w p': 'window:focus-previous-pane'

   # From v1.6.0
   'ctrl-w ctrl-v': 'pane:split-right-and-copy-active-item'


add todo

add todo

--- lib/editor/results.coffee
+++ lib/editor/results.coffee
@@ -1,6 +1,7 @@
 # TODO: invalid results don't fade out
 # In general this is weird and needs a refactor
 # TODO: fade results when the cursor is underneath
+# TODO: better scrolling behaviour

 {CompositeDisposable} = require 'atom'


Set col if undefined since computeRange will try to create a point regardless

--- lib/linter-liferay.coffee
+++ lib/linter-liferay.coffee
@@ -51,6 +51,8 @@
                 match.lineStart = lineA
                 match.lineEnd = lineB.substr 1

+            match.col ?= 0
+
             msg = {
                 col: match.col
                 level: @defaultLevel


Catch only JSON parsing error in RuboCop class

--- lib/linter/rubocop.coffee
+++ lib/linter/rubocop.coffee
@@ -44,10 +44,12 @@

       if result.exitCode == 0 || result.exitCode == 1
         try
-          callback(null, JSON.parse(result.stdout))
+          rubocopResult = JSON.parse(result.stdout)
         catch error
           escapedStdout = JSON.stringify(result.stdout)
-          callback(new LinterError("Failed parsing RuboCop's JSON output #{escapedStdout}", result))
+          error = new LinterError("Failed parsing RuboCop's JSON output #{escapedStdout}", result)
+
+        callback(error, rubocopResult)
       else
         callback(new LinterError("rubocop exited with code #{result.exitCode}", result))


добавлена команда call

добавлена команда call

--- lib/nbuild/builder.coffee
+++ lib/nbuild/builder.coffee
@@ -123,10 +123,11 @@
     ###
     @types =
       "batch":    _.bind(@_batch,    this)
-      "rollback": _.bind(@_rollback, this)
       "define":   _.bind(@_define,   this)
       "default":  _.bind(@_default,  this)
-
+      "rollback": _.bind(@_rollback, this)
+      "call":     _.bind(@_call,     this)
+
     hasLoad = no
     for configFile in options.configFiles
       continue unless configFile? and _.isString(configFile) and existsSync(configFile)
@@ -337,7 +338,35 @@
     @defines = @definesStack.pop()
     delete @defaults
     @defaults = @defaultsStack.pop()
-
+
+  ###*
+  * Parse define node
+  *
+  * @private
+  * @param name    {String}
+  * @param config  {Object}
+  ###
+
+  _define: (name, config) ->
+    return if config["@environment"] and config["@environment"] isnt @environment
+    for key, val of config
+      continue if key[0] is '@'
+      @defines[key] = @_parseVars(val)
+
+  ###*
+  * Parse default node
+  *
+  * @private
+  * @param name    {String}
+  * @param config  {Object}
+  ###
+
+  _default: (name, config) ->
+    return if config["@environment"] and config["@environment"] isnt @environment
+    for key, val of config
+      continue if key[0] is '@'
+      @defaults[key] = @_parseVars(val)
+
   ###*
   * Rollback step
   *
@@ -361,35 +390,30 @@
           console.log "rmdir #{entry.path}" if @verbose
         catch err
           console.warn "Warning: can't delete dir #{entry.path}".yellow
-
-  ###*
-  * Parse define node
+
+  ###*
+  * call node
   *
   * @private
   * @param name    {String}
   * @param config  {Object}
   ###

-  _define: (name, config) ->
-    return if config["@environment"] and config["@environment"] isnt @environment
-    for key, val of config
-      continue if key[0] is '@'
-      @defines[key] = @_parseVars(val)
-
-  ###*
-  * Parse default node
-  *
-  * @private
-  * @param name    {String}
-  * @param config  {Object}
-  ###
-
-  _default: (name, config) ->
-    return if config["@environment"] and config["@environment"] isnt @environment
-    for key, val of config
-      continue if key[0] is '@'
-      @defaults[key] = @_parseVars(val)
-
+  _call: (name, config) ->
+    if _.isArray(config.command)
+      for cmd in config.command
+        @_exec(cmd)
+    else
+      @_exec(config.command)
+
+
+  ###*
+  * Parse vars of each config entry
+  *
+  * @private
+  * @param config  {Object}
+  ###
+
   _expandConfig: (config) ->
     result = _.clone(@defaults)
     for key, val of config


EDX-316: Fix XSS vulnerability in inline discussion

--- common/static/coffee/src/discussion/views/discussion_thread_view_inline.coffee
+++ common/static/coffee/src/discussion/views/discussion_thread_view_inline.coffee
@@ -45,7 +45,7 @@

     expandPost: (event) =>
       @$el.addClass('expanded')
-      @$el.find('.post-body').html(@model.get('body'))
+      @$el.find('.post-body').html(_.escape(@model.get('body')))
       @showView.convertMath()
       @$el.find('.expand-post').css('display', 'none')
       @$el.find('.collapse-post').css('display', 'block')
@@ -60,7 +60,7 @@
       if postTop < curScroll
         $('html, body').animate({scrollTop: postTop})
       @$el.removeClass('expanded')
-      @$el.find('.post-body').html(@model.get('abbreviatedBody'))
+      @$el.find('.post-body').html(_.escape(@model.get('abbreviatedBody')))
       @showView.convertMath()
       @$el.find('.expand-post').css('display', 'block')
       @$el.find('.collapse-post').css('display', 'none')


Changed to in-line "if"

Changed to in-line "if"

--- lib/wercker_status.coffee
+++ lib/wercker_status.coffee
@@ -32,8 +32,7 @@
             if apps.error?.message == 'Unknown token'
                 return atom.config.set('wercker-status.Token', null)
             app = _.first(_.where(apps, {'url': gitparams.ssh}))
-            if !app
-                app = _.first(_.where(apps, {'url': gitparams.https}))
+            app = _.first(_.where(apps, {'url': gitparams.https})) if !app
             return cb('App not found') if !app # When don't match with wercker apps

             wercker.get_builds app.id, (err, builds) ->


pushState/replaceState should not fail if called without URL

--- lib/zombie/history.coffee
+++ lib/zombie/history.coffee
@@ -252,6 +252,7 @@

   # This method is available from Location.
   pushState: (state, title, url)->
+    url ||= @current.window.location.href
     url = HTML.resourceLoader.resolve(@current.window.document, url)
     # TODO: check same origin
     @addEntry(@current.window, url, state || {})
@@ -259,6 +260,7 @@

   # This method is available from Location.
   replaceState: (state, title, url)->
+    url ||= @current.window.location.href
     url = HTML.resourceLoader.resolve(@current.window.document, url)
     # TODO: check same origin
     @replaceEntry(@current.window, url, state || {})


Revert "Revert "Allow pasting white space when `autoIndentOnPaste` is enabled""

This reverts commit 5c03894227dca96e6a630acb07da6c9596f910c9.

--- src/selection.coffee
+++ src/selection.coffee
@@ -378,7 +378,7 @@
       indentAdjustment = @editor.indentLevelForLine(precedingText) - options.indentBasis
       @adjustIndent(remainingLines, indentAdjustment)

-    if options.autoIndent and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0
+    if options.autoIndent and NonWhitespaceRegExp.test(text) and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0
       autoIndentFirstLine = true
       firstLine = precedingText + firstInsertedLine
       desiredIndentLevel = @editor.languageMode.suggestedIndentForLineAtBufferRow(oldBufferRange.start.row, firstLine)


Fix variable conflict Fix https://github.com/yudachi/poi/issues/116

--- views/env.coffee
+++ views/env.coffee
@@ -248,11 +248,11 @@
           delete _slotitems[parseInt(itemId)]
       when '/kcsapi/api_req_kousyou/destroyship'
         decks = window._decks
-        idx = parseInt(postBody.api_ship_id)
+        removeId = parseInt(postBody.api_ship_id)
         [x, y] = [-1, -1]
         for deck, i in decks
           for shipId, j in deck.api_ship
-            if shipId == idx
+            if shipId == removeId
               [x, y] = [i, j]
               break
         if x != -1 && y != -1
@@ -262,10 +262,10 @@
             for idx in [y..4]
               decks[x].api_ship[idx] = decks[x].api_ship[idx + 1]
             decks[x].api_ship[5] = -1
-        for itemId in _ships[idx].api_slot
+        for itemId in _ships[removeId].api_slot
           continue if itemId == -1
           delete _slotitems[itemId]
-        delete _ships[idx]
+        delete _ships[removeId]
       when '/kcsapi/api_req_kousyou/getship'
         _ships[body.api_ship.api_id] = extendShip body.api_ship
         if body.api_slotitem?


FinderController: update ::cleanup

FinderController: update ::cleanup

--- client/Finder/filetree/controllers/findercontroller.coffee
+++ client/Finder/filetree/controllers/findercontroller.coffee
@@ -199,11 +199,6 @@
       vm.path = path
       @mountVm vm

-  cleanup:->
-    @treeController.removeAllNodes()
-    FSHelper.resetRegistry()
-    @stopAllWatchers()
-    @vms = []

   setRecentFile:({path})->
     recentFiles = @appStorage.getValue('recentFiles')
@@ -307,3 +302,12 @@
         return machineItem  if machineItem.data.uid is uid

+  # Basics
+  #
+
+  cleanup:->
+    @treeController.removeAllNodes()
+    FSHelper.resetRegistry()
+    @stopAllWatchers()
+    @machines = []
+


syntax error fixed

syntax error fixed

--- client/Framework/components/tree/treeviewcontroller.coffee
+++ client/Framework/components/tree/treeviewcontroller.coffee
@@ -294,9 +294,8 @@
       scrollView         : no
       selection          : no
       view               : new options.listViewClass
-        type             : options.type
         tagName          : "ul"
-        type             : "jtree"
+        type             : options.type or "jtree"
         itemClass        : options.treeItemClass
         itemChildClass   : options.itemChildClass
         itemChildOptions : options.itemChildOptions


Client: Activity: Test: Fixed merge conflict problem with updated comment input widget path

--- client/activity/lib/components/commentlistitem/view.coffee
+++ client/activity/lib/components/commentlistitem/view.coffee
@@ -10,7 +10,7 @@
 ProfileText          = require 'app/components/profile/profiletext'
 ActivityLikeLink     = require 'activity/components/chatlistitem/activitylikelink'
 MessageItemMenu      = require 'activity/components/messageitemmenu'
-CommentInputWidget   = require 'activity/components/comments/commentinputwidget'
+CommentInputWidget   = require 'activity/components/commentinputwidget'
 ProfileLinkContainer = require 'app/components/profile/profilelinkcontainer'

 module.exports = class CommentListItemView extends React.Component


Landing: Home page random image order bug fixed

--- client/landing/site.boilerplate/coffee/home/AppView.coffee
+++ client/landing/site.boilerplate/coffee/home/AppView.coffee
@@ -67,7 +67,7 @@

       slide

-    INDEX = KD.utils.getRandomNumber(COLORS.length - 1)
+    INDEX = KD.utils.getRandomNumber(COLORS.length) - 1
     @changeColor()

     @once 'viewAppended', ->


Only execute collection lookup via App.Model.subscribe min. callback exists.

--- app/assets/javascripts/app/models/_application_model.coffee
+++ app/assets/javascripts/app/models/_application_model.coffee
@@ -233,7 +233,7 @@
     # subscribe and reload data / fetch new data if triggered
     subscribeId = undefined
     if bind
-      subscribeId = App[ @className ].subscribe_item(id, callback)
+      subscribeId = App[ @className ].subscribeItem(id, callback)

     # execute if object already exists
     if !force && App[ @className ].exists(id)
@@ -345,6 +345,7 @@
       App.Event.bind(
         events
         =>
+          return if _.isEmpty(@SUBSCRIPTION_COLLECTION)
           App.Log.debug('Model', "server notify collection change #{@className}")
           @fetchFull(
             ->
@@ -394,13 +395,13 @@
   subscribe: (callback, type) ->

     # remember record id and callback
-    App[ @constructor.className ].subscribe_item(@id, callback)
-
-  @subscribe_item: (id, callback) ->
+    App[ @constructor.className ].subscribeItem(@id, callback)
+
+  @subscribeItem: (id, callback) ->

     # init bind
-    if !@_subscribe_item_bindDone
-      @_subscribe_item_bindDone = true
+    if !@_subscribeItemBindDone
+      @_subscribeItemBindDone = true

       # subscribe and render data after local change
       @bind(
@@ -454,16 +455,16 @@
       App.Event.bind(
         events
         (item) =>
-          if @SUBSCRIPTION_ITEM && @SUBSCRIPTION_ITEM[ item.id ]
-            App.Log.debug('Model', "server change on #{@className}.find(#{item.id}) #{item.updated_at}")
-            callback = =>
-              genericObject = undefined
-              if App[ @className ].exists(item.id)
-                genericObject = App[ @className ].find(item.id)
-              if !genericObject || new Date(item.updated_at) > new Date(genericObject.updated_at)
-                App.Log.debug('Model', "request #{@className}.find(#{item.id}) from server")
-                @full(item.id, false, true)
-            App.Delay.set(callback, 600, item.id, "full-#{@className}-#{item.id}")
+          return if !@SUBSCRIPTION_ITEM || !@SUBSCRIPTION_ITEM[ item.id ]
+          App.Log.debug('Model', "server change on #{@className}.find(#{item.id}) #{item.updated_at}")
+          callback = =>
+            genericObject = undefined
+            if App[ @className ].exists(item.id)
+              genericObject = App[ @className ].find(item.id)
+            if !genericObject || new Date(item.updated_at) > new Date(genericObject.updated_at)
+              App.Log.debug('Model', "request #{@className}.find(#{item.id}) from server")
+              @full(item.id, false, true)
+          App.Delay.set(callback, 600, item.id, "full-#{@className}-#{item.id}")
         "Item::Subscribe::#{@className}"
       )

@@ -471,13 +472,13 @@
       App.Event.bind(
         events
         (item) =>
-          if @SUBSCRIPTION_ITEM && @SUBSCRIPTION_ITEM[ item.id ]
-            return if !App[ @className ].exists(item.id)
-            genericObject = App[ @className ].find(item.id)
-            App.Log.debug('Model', "server delete on #{@className}.find(#{item.id}) #{item.updated_at}")
-            callback = ->
-              genericObject.trigger('destroy', genericObject)
-            App.Delay.set(callback, 500, item.id, "delete-#{@className}-#{item.id}")
+          return if !@SUBSCRIPTION_ITEM || !@SUBSCRIPTION_ITEM[ item.id ]
+          return if !App[ @className ].exists(item.id)
+          genericObject = App[ @className ].find(item.id)
+          App.Log.debug('Model', "server delete on #{@className}.find(#{item.id}) #{item.updated_at}")
+          callback = ->
+            genericObject.trigger('destroy', genericObject)
+          App.Delay.set(callback, 500, item.id, "delete-#{@className}-#{item.id}")
         "Item::SubscribeDelete::#{@className}"
       )


Added ignoreLoadingIndicator to typeahead search

--- app/assets/javascripts/sprangular/controllers/header.coffee
+++ app/assets/javascripts/sprangular/controllers/header.coffee
@@ -27,4 +27,7 @@
     $location.path "/products"

   $scope.getProducts = (search) ->
-    Catalog.products(search)
+    return [] unless search
+    Catalog.products(search, 1, ignoreLoadingIndicator: true)
+      .then (products) ->
+        $scope.lastSearch = products


Hide custom_fields without value in node panel info

--- app/frontend/javascripts/views/visualization-info.js.coffee
+++ app/frontend/javascripts/views/visualization-info.js.coffee
@@ -42,8 +42,9 @@
       if @node_custom_fields and @node_custom_fields.length > 0
         templateAttr.custom_fields = []
         @node_custom_fields.forEach (field) =>
-          if @model.get(field.name) != null
-            templateAttr.custom_fields.push {key: field.name.replace(/_+/g, ' '), value: @model.get(field.name)}
+          val = @model.get(field.name)
+          unless val == null or val == undefined
+            templateAttr.custom_fields.push {key: field.name.replace(/_+/g, ' '), value: val}
       # Compile the template using Handlebars
       template = HandlebarsTemplate templateAttr
       @$el.find('.panel-body').html template


Add $.fn.containingView and restore previous $.fn.view implementation

Having the view always traverse upward was causing problems in Atom
when unsubscribing from views in cleanData. We now use
$.fn.containingView in the $.fn.targetView method, which is the only
place the new semantics was really needed.

--- src/space-pen.coffee
+++ src/space-pen.coffee
@@ -286,14 +286,17 @@
 # jQuery extensions

 $.fn.view = ->
+  @data('view')
+
+$.fn.views = -> @toArray().map (elt) ->
+  $elt = $(elt)
+  $elt.view() ? $elt
+
+$.fn.containingView = ->
   element = this
   while element.length > 0
     return view if view = element.data('view')
     element = element.parent()
-
-$.fn.views = -> @toArray().map (elt) ->
-  $elt = $(elt)
-  $elt.view() ? $elt

 $.fn.scrollBottom = (newValue) ->
   if newValue?
@@ -445,8 +448,8 @@
   parseInt(@text())

 $.Event.prototype.abortKeyBinding = ->
-$.Event.prototype.currentTargetView = -> $(@currentTarget).view()
-$.Event.prototype.targetView = -> $(@target).view()
+$.Event.prototype.currentTargetView = -> $(@currentTarget).containingView()
+$.Event.prototype.targetView = -> $(@target).containingView()

 # Exports


Avoid memory leak caused by reuse of `elements` constant as local

The `elements` variable was used as a constant at the closure level, but
then it was also being assigned in functions attempting to use it as a
local. This capitalizes all closure-scoped be constants to avoid this in
the future.

--- src/space-pen.coffee
+++ src/space-pen.coffee
@@ -3,7 +3,7 @@
 else
   $ = jQuery = window.jQuery

-elements =
+Tags =
   'a abbr address article aside audio b bdi bdo blockquote body button
    canvas caption cite code colgroup datalist dd del details dfn div dl dt em
    fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup
@@ -13,11 +13,11 @@
    th thead time title tr u ul video area base br col command embed hr img input
    keygen link meta param source track wbrk'.split /\s+/

-voidElements =
+SelfClosingTags =
   'area base br col command embed hr img input keygen link meta param
    source track wbr'.split /\s+/

-events =
+Events =
   'blur change click dblclick error focus input keydown
    keypress keyup load mousedown mousemove mouseout mouseover
    mouseup resize scroll select submit unload'.split /\s+/
@@ -27,7 +27,7 @@
 class View extends jQuery
   @builderStack: null

-  elements.forEach (tagName) ->
+  Tags.forEach (tagName) ->
     View[tagName] = (args...) -> @currentBuilder.tag(tagName, args...)

   @subview: (name, view) ->
@@ -88,7 +88,7 @@
       element.attr('outlet', null)

   bindEventHandlers: (view) ->
-    for eventName in events
+    for eventName in Events
       selector = "[#{eventName}]"
       elements = view.find(selector).add(view.filter(selector))
       elements.each ->
@@ -121,7 +121,7 @@

     @openTag(name, options.attributes)

-    if name in voidElements
+    if name in SelfClosingTags
       if (options.text? or options.content?)
         throw new Error("Self-closing tag #{name} cannot have text or content")
     else


apply param change

apply param change

--- lib/SeriouslyEffect.coffee
+++ lib/SeriouslyEffect.coffee
@@ -27,7 +27,7 @@
         @inPorts[key].on 'data', @setSource
       else
         #TODO key
-        @inPorts[key].on 'data', @setParam
+        @inPorts[key].on 'data', @setParam.bind(@,key)

   syncGraph: (event) =>
     # Connect another effect to this effect
@@ -39,11 +39,9 @@

   setSource: (data) =>
     # Connect DOM element to this effect
-    console.log "source", data
     @seriouslyNode.source = data
     if @outPorts.filter.isAttached() and !@outPorts.filter.isConnected()
       @outPorts.filter.connect();

-  setParam: (key, data) =>
-    # TODO key?
-    # console.log "param", data
+  setParam: (key, data) -> # this is bound, so use -> not =>
+    @seriouslyNode[key] = data


Fix BasicAuth for internal HTTP server

Fix BasicAuth for internal HTTP server

When using the `CONNECT_USER` and `CONNECT_PASSWORD` environment
variables, the following error was being raised:

    ReferenceError: path is not defined

--- src/robot.coffee
+++ src/robot.coffee
@@ -170,7 +170,7 @@

     @connect = Connect()

-    @connect.use Connect.basicAuth(user, path) if user and pass
+    @connect.use Connect.basicAuth(user, pass) if user and pass
     @connect.use Connect.bodyParser()
     @connect.use Connect.router (app) =>


remove chktex error

remove chktex error

too many false positives from 'unable to execute latex command'

--- services/clsi/app/coffee/CompileManager.coffee
+++ services/clsi/app/coffee/CompileManager.coffee
@@ -45,7 +45,7 @@
 			# set up environment variables for chktex
 			env = {}
 			if request.check?
-				env['CHKTEX_OPTIONS'] =  '-nall -e9 -e10 -w15 -w16 -w27'
+				env['CHKTEX_OPTIONS'] = '-nall -e9 -e10 -w15 -w16'
 				if request.check is 'error'
 					env['CHKTEX_EXIT_ON_ERROR'] =  1
 				if request.check is 'validate'


take care if previous document

take care if previous document

--- lib/computed-fields.coffee
+++ lib/computed-fields.coffee
@@ -24,6 +24,7 @@
     isUpdate: type is 'update'
     isInsert: type is 'insert'
     isRemove: type is 'remove'
+    previous: hook.transform hook.previous
     set: (value) =>
       field = {}
       field[@name] = value
@@ -39,9 +40,14 @@
       _id = options.findId(doc)
       field.collection.findOne _id: _id
     addHooks collection, (type, userId, doc, fieldNames) ->
-      fieldDoc = findDoc doc
-      thisValue = field._getThis this, fieldDoc, userId, fieldNames, type
-      options.update.call thisValue, fieldDoc, @transform()
+      callUpdate = (fieldDoc) ->
+        thisValue = field._getThis this, fieldDoc, userId, fieldNames, type
+        options.update.call thisValue, fieldDoc, @transform()
+
+      if doc? and currentDoc = findDoc @transform()
+        callUpdate.call this, currentDoc
+      if @previous? and previousDoc = findDoc @transform(@previous)
+        callUpdate.call this, previousDoc

 addHooks = (collection, method) ->
   callMethod = (type) -> (userId, doc, fieldNames) ->


couple fixes

couple fixes

--- lib/console/console.coffee
+++ lib/console/console.coffee
@@ -21,12 +21,11 @@
     @evalCmd.dispose()

   constructor: ->
-    @view.getModel = -> c
+    @view = new ConsoleView
+    @view.getModel = -> @
     @observeInput (cell) =>
       @watchModes cell
     @onEval => @logInput()
-
-  view: new ConsoleView

   isInput: false


new method

new method

--- lib/console/console.coffee
+++ lib/console/console.coffee
@@ -105,6 +105,8 @@

   onDidClear: (f) -> @emitter.on 'did-clear', f

+  onceDidClear: (f) -> d = @onDidClear (x) -> (d.dispose(); f(x))
+
   getInput: ->
     last = @items[@items.length-1]
     if last?.input then last


remove reference to blog in router

remove reference to blog in router


Got the select all coffeescript working, but it needs css work, and redirection stuff.

--- filemanager/static/script/selectAll.coffee
+++ filemanager/static/script/selectAll.coffee
@@ -4,46 +4,41 @@

 # (by this, I mean the writing of a script for pretty selection)

-setElementsSelected = (array,select) ->
-    console.log("hi")
-    console.log(array)
-    if select
-        for element in array
-            element.checked=true
-    else
-        for element in array
-            element.checked=false

-toggleGlobalSelect: (array,targetboxes, value) ->
-        console.log("got here")
-        if selected
-            @selected=false
-            setElementsSelected(array,targetboxes, true)
+class window.selectAllTool
+
+    oldbox: null
+    selected: null
+    targetboxes: []
+    newbox: null
+
+    selectBoxes: () ->
+        console.log("hi")
+        console.log(this.targetboxes)
+        console.log("hi")
+        if this.selected
+            console.log("hi")
+            this.selected=false
+            for element in this.targetboxes
+                element.checked=true
         else
-            @selected=true
-            setElementsSelected(array,targetboxes, false)
-
-class window.selectAllTool
-    oldbox=null
-    selected=null
-    targetboxes=null
-    newbox=null
-
-    constructor: () ->
+            this.selected=true
+            for element in this.targetboxes
+                element.checked=false

     startup: (allbox, targetboxes) ->
-        @selected=false
-        @targetboxes = document.getElementsByClassName(targetboxes)
-        console.log(@targetboxes)
+        this.selected=true
+        this.targetboxes = document.getElementsByClassName(targetboxes)
+        console.log(this.targetboxes)
         div = $ "
BleeBloo
"
         div.addClass "coffee-selectAllBox"
-        @oldbox = $(allbox)
-        console.log(@oldbox)
-        @oldbox.hide()
+        this.oldbox = $(allbox)
+        console.log(this.oldbox)
+        this.oldbox.hide()
         div.click ->
           console.log("MURB")
-          setElementsSelected(@targetboxes,@selected)
-          @selected.toggle
-        @newbox = div
-        @oldbox.after(@newbox)
+          allBox.selectBoxes()
+          this.selectBoxes()
+        this.newbox = div
+        this.oldbox.after(this.newbox)


Documentation: workspace.open( ) with no URI

I just learned that [calling open on workspace](https://discuss.atom.io/t/how-can-we-help-you-write-packages/4268/67) without specifying an uri is the way to go for getting a new tab with a new document.

I can't find anything about this in either the docs or the [api documentation.](https://atom.io/docs/api/v0.187.0/Workspace#instance-open)

Should `uri` be marked optional, like the `options` params? And should it be added a line stating that no uri opens a blank editor?

(and there's an extra `a` in the title..)

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -371,9 +371,11 @@
   Section: Opening
   ###

-  # Essential: Open a given a URI in Atom asynchronously.
-  #
-  # * `uri` A {String} containing a URI.
+  # Essential: Open a given URI in Atom asynchronously.
+  # If no URI is given, or URI does not resolve to an existing file,
+  # a new empty text edtior is created.
+  #
+  # * `uri` (optional) A {String} containing a URI.
   # * `options` (optional) {Object}
   #   * `initialLine` A {Number} indicating which row to move the cursor to
   #     initially. Defaults to `0`.


Use new Model superclass in Workspace

Use new Model superclass in Workspace

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -9,6 +9,7 @@
 Grim = require 'grim'
 fs = require 'fs-plus'
 StackTraceParser = require 'stacktrace-parser'
+Model = require './model'
 TextEditor = require './text-editor'
 PaneContainer = require './pane-container'
 Pane = require './pane'
@@ -33,13 +34,13 @@
   atom.deserializers.add(this)
   Serializable.includeInto(this)

-  @properties
-    paneContainer: null
-    fullScreen: false
-    destroyedItemURIs: -> []
-
   constructor: (params) ->
     super
+
+    unless Grim.includeDeprecatedAPIs
+      @paneContainer = params?.paneContainer
+      @fullScreen = params?.fullScreen ? false
+      @destroyedItemURIs = params?.destroyedItemURIs ? []

     @emitter = new Emitter
     @openers = []
@@ -892,6 +893,11 @@
     deferred.promise

 if includeDeprecatedAPIs
+  Workspace.properties
+    paneContainer: null
+    fullScreen: false
+    destroyedItemURIs: -> []
+
   Object.defineProperty Workspace::, 'activePaneItem',
     get: ->
       Grim.deprecate "Use ::getActivePaneItem() instead of the ::activePaneItem property"


Reorg Event Subscription section

Reorg Event Subscription section

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -91,6 +91,33 @@
   Section: Event Subscription
   ###

+  # Essential: Invoke the given callback with all current and future text
+  # editors in the workspace.
+  #
+  # * `callback` {Function} to be called with current and future text editors.
+  #   * `editor` An {Editor} that is present in {::getTextEditors} at the time
+  #     of subscription or that is added at some later time.
+  #
+  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
+  observeTextEditors: (callback) ->
+    callback(textEditor) for textEditor in @getTextEditors()
+    @onDidAddTextEditor ({textEditor}) -> callback(textEditor)
+
+  # Essential: Invoke the given callback whenever an item is opened. Unlike
+  # ::onDidAddPaneItem, observers will be notified for items that are already
+  # present in the workspace when they are reopened.
+  #
+  # * `callback` {Function} to be called whenever an item is opened.
+  #   * `event` {Object} with the following keys:
+  #     * `uri` {String} representing the opened URI. Could be `undefined`.
+  #     * `item` The opened item.
+  #     * `pane` The pane in which the item was opened.
+  #     * `index` The index of the opened item on its pane.
+  #
+  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
+  onDidOpen: (callback) ->
+    @emitter.on 'did-open', callback
+
   # Extended: Invoke the given callback when a pane is added to the workspace.
   #
   # * `callback` {Function} to be called panes are added.
@@ -173,33 +200,6 @@
   onDidAddTextEditor: (callback) ->
     @onDidAddPaneItem ({item, pane, index}) ->
       callback({textEditor: item, pane, index}) if item instanceof Editor
-
-  # Essential: Invoke the given callback with all current and future text
-  # editors in the workspace.
-  #
-  # * `callback` {Function} to be called with current and future text editors.
-  #   * `editor` An {Editor} that is present in {::getTextEditors} at the time
-  #     of subscription or that is added at some later time.
-  #
-  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
-  observeTextEditors: (callback) ->
-    callback(textEditor) for textEditor in @getTextEditors()
-    @onDidAddTextEditor ({textEditor}) -> callback(textEditor)
-
-  # Essential: Invoke the given callback whenever an item is opened. Unlike
-  # ::onDidAddPaneItem, observers will be notified for items that are already
-  # present in the workspace when they are reopened.
-  #
-  # * `callback` {Function} to be called whenever an item is opened.
-  #   * `event` {Object} with the following keys:
-  #     * `uri` {String} representing the opened URI. Could be `undefined`.
-  #     * `item` The opened item.
-  #     * `pane` The pane in which the item was opened.
-  #     * `index` The index of the opened item on its pane.
-  #
-  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
-  onDidOpen: (callback) ->
-    @emitter.on 'did-open', callback

   eachEditor: (callback) ->
     deprecate("Use Workspace::observeTextEditors instead")


Add date string formatter

Add date string formatter

--- _source/lib/formatters.coffee
+++ _source/lib/formatters.coffee
@@ -1,4 +1,30 @@
+monthNames = [
+  "January",
+  "February",
+  "March",
+  "April",
+  "May",
+  "June",
+  "July",
+  "August",
+  "September",
+  "October",
+  "November",
+  "December"
+]
+
 Apollos.formatters =

   toCurrency: (num) ->
     "$#{num.toFixed(2).replace /(\d)(?=(\d{3})+\.)/g, '$1,'}"
+
+  toDateString: (dateObj, abbreviated) ->
+    year = dateObj.getFullYear()
+    date = dateObj.getDate()
+    month = dateObj.getMonth()
+    monthName = monthNames[month]
+
+    if abbreviated
+      monthName = monthName.substring 0, 3
+
+    return "#{monthName} #{date}, #{year}"


Added to_firebase_dict method

Added to_firebase_dict method

--- src/lib/client/opened_apps.coffee
+++ src/lib/client/opened_apps.coffee
@@ -51,7 +51,10 @@
 class RunningApp
   constructor: (@name) ->

-  activate: () ->
+  get_firebase_key: () ->
+    return @name
+
+  get_firebase_value: () ->
     throw new Error('Not implemented')

   equals: (o) -> o? and @name == o.name
@@ -60,26 +63,15 @@
   constructor: (@window_index, @tab_index, @name) ->
     super(@name)

-  activate: () ->
-    console.log(@name)
+  get_firebase_value: () ->
+    return {@window_index, @tab_index}

 class NativeRunningApp extends RunningApp
   constructor: (@name) ->
     super(@name)

-  activate: () ->
-    console.log(@name)
-
-# The AppState only considers a list of names, e.g. ['Youtube', 'Spotify', 'Reddit']
-# When we scan the list of currently running apps, filter them to get the names and
-# then pass the list of currently running app names to the app state's update_app_state
-# method. It will make db inserts that will cause the mobile app to update.
-class AppState
-  update_app_state: (current_apps) ->
-
-  _on_app_inserted: (app) ->
-
-  _on_app_removed: (app) ->
+  get_firebase_value: () ->
+    return 1

 module.exports =
@@ -89,3 +81,9 @@
     tab_apps = _filter_and_fetch_tabs(tab_objects)

     return native_apps.concat(tab_apps)
+
+  to_firebase_dict: (apps) ->
+    dict = {}
+    for app in apps
+      dict[app.get_firebase_key()] = app.get_firebase_value()
+    return dict


comments

comments

--- lib/patterns/coding.coffee
+++ lib/patterns/coding.coffee
@@ -1,3 +1,49 @@
-module.exports =
-  #block_comment: /(?s)/\*.+?\*/
-  html_color: /^#?(([fFcC0369])\2){3}$/
+patterns =
+  comment:
+    single: /(?:\/\/)(.+)/i
+    block:/\/\*((?:.|[\n\r])+)\*\//i
+  color:
+    hex: /(?:#)?([fFcC0369]{6})/i
+    rgb: /(?:rgb\()([0-255]{0,3}(?:,)?){3}(?:\))/i
+
+patterns.comment.single.test =
+  exec:
+    idx: 1
+    sample: '//test'
+    expected: 'test'
+  test:
+    shouldntMatch: ['i','','-','2.1','/ /yo']
+    shouldMatch: ['//yo']
+
+patterns.comment.block.test =
+  exec:
+    idx: 1
+    sample: '/* yo\r\nwazgud */'
+    expected: ' yo\r\nwazgud '
+  test:
+    shouldntMatch: ['i','','-','2.1','/* yo']
+    shouldMatch: ['/* yo */','/* yo\r\nwazgud */']
+
+patterns.color.hex.test =
+  exec:
+    idx: 1
+    sample: 'ayo #C0C0C0 waz'
+    expected: 'C0C0C0'
+  match:
+    flags: 'g'
+    sample: '#C0C0C0 #00FFFF #ff00ff #ffffff ffffff #FFFFF #ZZAAZ'
+    expected: ['#C0C0C0','#00FFFF','#ff00ff','#ffffff','ffffff']
+  test:
+    shouldntMatch: ['i','','-','2.1','#FFFFF','#ZZAAZ']
+    shouldMatch: ['#C0C0C0','#00FFFF','#ff00ff','#ffffff','ffffff']
+
+patterns.color.rgb.test =
+  match:
+    flags: 'g'
+    sample: 'rgb(255,0,255) rgb(255,255,255) rgb(255,255,256) rgb(0,0,0)'
+    expected: ['rgb(255,0,255)','rgb(255,255,255)','rgb(0,0,0)']
+  test:
+    shouldntMatch: ['i','','-','2.1','rgb(255,255,256)']
+    shouldMatch: ['rgb(255,0,255)','rgb(255,255,255)','rgb(0,0,0)']
+
+module.exports = patterns


Added the individual node information to the elasticsearch command odule

--- src/scripts/elasticsearch.coffee
+++ src/scripts/elasticsearch.coffee
@@ -5,11 +5,12 @@
 #   None
 #
 # Commands:
-#   hubot: elasticsearch cluster health [server] - Gets the cluster information for the given server
+#   hubot: elasticsearch cluster [server] - Gets the cluster information for the given server
+#   hubot: elasticsearch node [server] - Gets the node information for the given server
 #   hubot: elasticsearch query [server] [query details] - Runs a specific query against an ElasticSearch cluster
 #
 # Notes:
-#   The server must be a fqdn to get to the elasticsearch cluster
+#   The server must be a fqdn (with the port!) to get to the elasticsearch cluster
 #
 # Author:
 #  Paul Stack
@@ -17,10 +18,33 @@

 module.exports = (robot) ->

-  robot.respond /elasticsearch cluster health (.*)/i, (msg) ->
+  cluster_health = (msg, server) ->
+    msg.http("http://#{server}/_cluster/health")
+      .get() (err, res, body) ->
+        json = JSON.parse(body)
+        cluster_name=['cluster']
+        status = json['status']
+        number_of_nodes = json['number_of_nodes']
+        msg.send "Cluster: #{cluster_name} \nStatus: #{status} \n Nodes: #{number_of_nodes}"
+
+  node_health = (msg, server) ->
+    msg.http("http://#{server}/_status")
+      .get() (err, res, body) ->
+        json = JSON.parse(body)
+        name =['name']
+        status = json['status']
+        msg.send "Server: #{name} \nStatus: #{status}"
+
+  robot.respond /elasticsearch node (.*)/i, (msg) ->
     if msg.message.user.id is robot.name
       return

-    server = msg.match[1]
-    status = "OK"
-    msg.send("#{server} reports Status: #{status}")
+    node_health msg, msg.match[1], (text) ->
+      msg.send text
+
+  robot.respond /elasticsearch cluster (.*)/i, (msg) ->
+    if msg.message.user.id is robot.name
+      return
+
+    cluster_health msg, msg.match[1], (text) ->
+      msg.send text


If result from google images is empty, don't die.

--- src/scripts/google-images.coffee
+++ src/scripts/google-images.coffee
@@ -33,6 +33,7 @@
     .get() (err, res, body) ->
       images = JSON.parse(body)
       images = images.responseData.results
-      image  = msg.random images
-      cb "#{image.unescapedUrl}#.png"
+      if images.length > 0
+        image  = msg.random images
+        cb "#{image.unescapedUrl}#.png"


Add possible EOF at table body end

Add possible EOF at table body end

This will allow for capturing tables that are at end of file w/o trailing newline

--- lib/table-formatter.coffee
+++ lib/table-formatter.coffee
@@ -163,7 +163,7 @@
       (?:
         (?:[^\n]*?\|[^\n]*)       # line w/ at least one pipe
         \ *                       # maybe trailing whitespace
-        \r?\n                     # newline
+        (?:\r?\n|$)               # newline
       )+ # at least one
     )
     ///g


get rid of more ellipses

get rid of more ellipses

--- src/smc-webapp/projects_nav.cjsx
+++ src/smc-webapp/projects_nav.cjsx
@@ -98,7 +98,7 @@
         project_name_styles =
             whiteSpace: 'nowrap'
             overflow: 'hidden'
-            textOverflow: 'ellipsis'
+            #textOverflow: 'ellipsis'

         if @props.project_id == @props.active_top_tab
             text_color = COLORS.TOP_BAR.TEXT_ACTIVE
@@ -233,7 +233,7 @@
         menu_item_project_name_styles =
             whiteSpace   : 'nowrap'
             overflow     : 'hidden'
-            textOverflow : 'ellipsis'
+            #textOverflow : 'ellipsis'
             marginRight  : '3px'
             width        : '100%'

@@ -316,7 +316,7 @@
         project_name_styles =
             whiteSpace   : 'nowrap'
             overflow     : 'hidden'
-            textOverflow : 'ellipsis'
+            #textOverflow : 'ellipsis'
             marginRight  : '3px'

         title = @props.project_map?.getIn([project_id, 'title'])


implement field_selector_stack to replace current_field_selector

--- src/tex-writer-mkts-table.coffee
+++ src/tex-writer-mkts-table.coffee
@@ -60,17 +60,17 @@
 #-----------------------------------------------------------------------------------------------------------
 new_local_state = ->
   R =
-    layout_name_stack:              []
     selectors_and_content_events:   {}
     layout_events:                  {}
     content_buffer:                 null
     within_field:                   false
+    layout_name_stack:              []
     ### TAINT we use this attribute to communicate the current selector from `$handle_fields()` back to
     `$handle_content_events()`; this works b/c we're assuming that all event handling is happening in
     lockstep. It might stop working as soon as the lockstepping is broken by an intervening asynchronous
     or buffering stream transform. ###
-    ### TAINT use `field_selector_stack` ###
-    current_field_selector:         null
+    ### TAINT used to store `layout_name` as well, already in `layout_name_stack` ###
+    field_selector_stack:           []
   return R

@@ -80,8 +80,14 @@
 #-----------------------------------------------------------------------------------------------------------
 @get_current_layout_name = ( S, L ) ->
   unless ( R = L.layout_name_stack[ L.layout_name_stack.length - 1 ] )?
-    throw new Error "#{badge}#µ4451 layout stack empty"
-  return R
+    throw new Error "#{badge} µ79868 layout stack empty"
+  return R
+
+#-----------------------------------------------------------------------------------------------------------
+@get_current_field_selector = ( S, L ) ->
+  unless ( R = L.field_selector_stack[ L.field_selector_stack.length - 1 ] )?
+    throw new Error "#{badge} µ58212 field_selector_stack stack empty"
+  return R[ 1 ]

 #-----------------------------------------------------------------------------------------------------------
 @get_enclosing_layout_name = ( S, L ) ->
@@ -135,9 +141,20 @@
   return R

 #-----------------------------------------------------------------------------------------------------------
+@push_field_selector = ( S, L, layout_name, selector ) ->
+  L.field_selector_stack.push [ layout_name, selector, ]
+  return null
+
+#-----------------------------------------------------------------------------------------------------------
+@pop_field_selector = ( S, L ) ->
+  if L.field_selector_stack.length < 1
+    throw new Error "#{badge} µ49782 field selector stack empty"
+  return L.field_selector_stack.pop()
+
+#-----------------------------------------------------------------------------------------------------------
 @content_buffers_from_layout_name = ( S, L, layout_name ) ->
   unless ( R = L.selectors_and_content_events[ layout_name ] )?
-    throw new Error "#{badge}#µ4433 unknown layout #{rpr layout_name}"
+    throw new Error "#{badge} µ65671 unknown layout #{rpr layout_name}"
   return R

 #-----------------------------------------------------------------------------------------------------------
@@ -213,18 +230,12 @@
       selectors_and_content_events  = @get_selectors_and_content_events S, L, layout_name
       enclosing_layout_name         = @get_enclosing_layout_name        S, L
       content_events                = MKTS_TABLE._walk_events layout, selectors_and_content_events, L.layout_name_stack
-      urge '99983', enclosing_layout_name, L.current_field_selector
       #.....................................................................................................
       if enclosing_layout_name?
-        unless L.current_field_selector?
-          throw new Error "#{badge}#µ1181 (should never happen) missing `L.current_field_selector`"
+        current_field_selector    = @get_current_field_selector S, L
         enclosing_content_events  = @content_buffers_from_layout_name S, L, enclosing_layout_name
-        # enclosed_content_events   = [ L.current_field_selector, content_events..., ]
-        enclosed_content_events   = [ 'B1', content_events..., ]
-        debug '55563', CND.gold enclosing_content_events
-        debug '55563', CND.lime enclosed_content_events
+        enclosed_content_events   = [ current_field_selector, content_events..., ]
         enclosing_content_events.push enclosed_content_events
-        # send sub_event for sub_event from MKTS_TABLE._walk_events layout, selectors_and_content_events
       #.....................................................................................................
       else
         send sub_event for sub_event from content_events
@@ -259,14 +270,14 @@
         throw new Error "#{badge}#µ2132 missing <field> tag attribute 'key' in table #{rpr layout_name} (#{jr event})"
       ### TAINT this is exactly the kind of dangerous 'sound have happened anywhere, anytime' state mutation
       that advocates of immutable state are warning us about: ###
-      L.current_field_selector  = Q.key
-      content_buffer            = @new_content_buffer S, L, layout_name,  L.current_field_selector
+      @push_field_selector S, L, layout_name, Q.key
+      content_buffer            = @new_content_buffer S, L, layout_name,  Q.key
       return send stamp event
     #.......................................................................................................
     if select event, ')', 'field'
       within_field              = false
       content_buffer            = null
-      # L.current_field_selector  = null
+      @pop_field_selector S, L
       return send stamp event
     #.......................................................................................................
     if within_field


refactor MKTS Table to use a more distributed-responsibilities oriented approach, use named local state


Clear out width on TextEditorElement only if autoWidth was previously on

--- src/text-editor-component.coffee
+++ src/text-editor-component.coffee
@@ -152,7 +152,7 @@
       if (@newState.content.autoWidth isnt @oldState.content.autoWidth) or (@newState.content.width isnt @oldState.content.width)
         if @newState.content.autoWidth
           @hostElement.style.width = @newState.content.width + 'px'
-        else
+        else if @oldState.content.autoWidth
           @hostElement.style.width = ''

         @oldState.content.width = @newState.content.width


Update menu when right clicking

Update menu when right clicking

--- menus/test-navigator.cson
+++ menus/test-navigator.cson
@@ -3,7 +3,7 @@
   'atom-text-editor': [
     {
       'label': 'Test Navigator'
-      'command': 'test-navigator:toggle'
+      'command': 'test-navigator:navigate'
     }
   ]
 'menu': [
@@ -14,7 +14,7 @@
       'submenu': [
         {
           'label': 'Navigate'
-          'command': 'test-navigator:toggle'
+          'command': 'test-navigator:navigate'
         }
       ]
     ]


fixed build to not overwrite 'components' dir

--- component.coffee
+++ component.coffee
@@ -264,11 +264,11 @@
        `component template` command
     """
     action: () ->
-      commands.install.action()
-
       if resolve(argv.staticpath) isnt resolve(defaultStaticpath())
         log "installing default static path (#{defaultStaticpath()}) to #{argv.staticpath}"
         wrench.copyDirSyncRecursive defaultStaticpath(), argv.staticpath
+
+      commands.install.action()

       if argv.templatepath
         templateObj = templateCommands.all getConfig()


prevent default on button/link with disabled class or disabled attribute

--- lib/assets/javascripts/initializers.coffee
+++ lib/assets/javascripts/initializers.coffee
@@ -75,6 +75,7 @@
 documentListenerForButtons = (eventType, handler, useCapture = false) ->
   document.addEventListener eventType, (ev) ->
     target = ev.target
+    ev.preventDefault() if nodeIsDisabled(target)
     return if !(target.nodeName == "A" || target.nodeName == "BUTTON") || nodeIsDisabled(target)
     handler(ev)


Use two newlines between vendor js.

Use two newlines between vendor js.

--- src/fs_utils/source_file.coffee
+++ src/fs_utils/source_file.coffee
@@ -46,7 +46,7 @@
       """
     else
       if @type in ['javascript', 'template']
-        "#{data};\n"
+        "#{data};\n\n"
       else
         data


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -21,6 +21,8 @@

   equals text.I.alpha, 1

-  text.
+  text.update()
+
+  eq

 module()


GameObject.Effect

GameObject.Effect

--- test/text.coffee
+++ test/text.coffee
@@ -19,7 +19,7 @@

 test "add to engine", ->
   engine = Engine()
-
+
   engine.add "TextEffect.Floating"

-module()+module()


Fix snapshotting the cold-booted page

Fix snapshotting the cold-booted page

--- src/turbolinks/controller.coffee
+++ src/turbolinks/controller.coffee
@@ -58,8 +58,7 @@
   saveSnapshot: ->
     @notifyApplicationBeforeSnapshotSave()
     snapshot = @view.saveSnapshot()
-    location = @lastRenderedVisit?.location ? @location
-    @cache.put(location, snapshot)
+    @cache.put(@lastRenderedLocation, snapshot)

   restoreSnapshotForLocationWithAction: (location, action) ->
     if snapshot = @cache.get(location)
@@ -73,7 +72,7 @@
     @adapter.pageInvalidated()

   viewRendered: ->
-    @lastRenderedVisit = @currentVisit
+    @lastRenderedLocation = @currentVisit.location

   # History delegate

@@ -84,6 +83,7 @@
   # Event handlers

   pageLoaded: =>
+    @lastRenderedLocation = @location
     @notifyApplicationAfterPageLoad()

   clickCaptured: =>


Recycle text nodes in lines

Recycle text nodes in lines

--- src/lines-tile-component.coffee
+++ src/lines-tile-component.coffee
@@ -151,21 +151,21 @@
         indentGuide = @domElementPool.buildElement("span", "indent-guide")
         for j in [0...tabLength]
           if invisible = endOfLineInvisibles?[invisibleIndex++]
-            indentGuide.appendChild(
-              @domElementPool.buildElement("span", "invisible-character", invisible)
-            )
+            invisibleSpan = @domElementPool.buildElement("span", "invisible-character")
+            invisibleSpan.appendChild(@domElementPool.buildText(invisible))
+            indentGuide.appendChild(invisibleSpan)
           else
-            indentGuide.insertAdjacentText("beforeend", " ")
+            indentGuide.appendChild(@domElementPool.buildText(" "))
         lineNode.appendChild(indentGuide)

       while invisibleIndex < endOfLineInvisibles?.length
         invisible = endOfLineInvisibles[invisibleIndex++]
-        lineNode.appendChild(
-          @domElementPool.buildElement("span", "invisible-character", invisible)
-        )
+        invisibleSpan = @domElementPool.buildElement("span", "invisible-character")
+        invisibleSpan.appendChild(@domElementPool.buildText(invisible))
+        lineNode.appendChild(invisibleSpan)
     else
       unless @appendEndOfLineNodes(id, lineNode)
-        lineNode.textContent = "\u00a0"
+        lineNode.appendChild(@domElementPool.buildText("\u00a0"))

   setLineInnerNodes: (id, lineNode) ->
     lineState = @newTileState.lines[id]
@@ -213,11 +213,12 @@

   appendTokenNodes: (tokenText, isHardTab, firstNonWhitespaceIndex, firstTrailingWhitespaceIndex, hasIndentGuide, hasInvisibleCharacters, scopeNode) ->
     if isHardTab
-      hardTabNode = @domElementPool.buildElement("span", "hard-tab", tokenText)
+      hardTabNode = @domElementPool.buildElement("span", "hard-tab")
       hardTabNode.classList.add("leading-whitespace") if firstNonWhitespaceIndex?
       hardTabNode.classList.add("trailing-whitespace") if firstTrailingWhitespaceIndex?
       hardTabNode.classList.add("indent-guide") if hasIndentGuide
       hardTabNode.classList.add("invisible-character") if hasInvisibleCharacters
+      hardTabNode.appendChild(@domElementPool.buildText(tokenText))

       scopeNode.appendChild(hardTabNode)
     else
@@ -228,26 +229,24 @@
       trailingWhitespaceNode = null

       if firstNonWhitespaceIndex?
-        leadingWhitespaceNode = @domElementPool.buildElement(
-          "span",
-          "leading-whitespace",
-          tokenText.substring(0, firstNonWhitespaceIndex)
-        )
+        leadingWhitespaceNode = @domElementPool.buildElement("span", "leading-whitespace")
         leadingWhitespaceNode.classList.add("indent-guide") if hasIndentGuide
         leadingWhitespaceNode.classList.add("invisible-character") if hasInvisibleCharacters
+        leadingWhitespaceNode.appendChild(
+          @domElementPool.buildText(tokenText.substring(0, firstNonWhitespaceIndex))
+        )

         startIndex = firstNonWhitespaceIndex

       if firstTrailingWhitespaceIndex?
         tokenIsOnlyWhitespace = firstTrailingWhitespaceIndex is 0

-        trailingWhitespaceNode = @domElementPool.buildElement(
-          "span",
-          "trailing-whitespace",
-          tokenText.substring(firstTrailingWhitespaceIndex)
-        )
+        trailingWhitespaceNode = @domElementPool.buildElement("span", "trailing-whitespace")
         trailingWhitespaceNode.classList.add("indent-guide") if hasIndentGuide and not firstNonWhitespaceIndex? and tokenIsOnlyWhitespace
         trailingWhitespaceNode.classList.add("invisible-character") if hasInvisibleCharacters
+        trailingWhitespaceNode.appendChild(
+          @domElementPool.buildText(tokenText.substring(firstTrailingWhitespaceIndex))
+        )

         endIndex = firstTrailingWhitespaceIndex

@@ -255,11 +254,18 @@

       if tokenText.length > MaxTokenLength
         while startIndex < endIndex
-          text = @sliceText(tokenText, startIndex, startIndex + MaxTokenLength)
-          scopeNode.appendChild(@domElementPool.buildElement("span", null, text))
+          textNode = @domElementPool.buildText(
+            @sliceText(tokenText, startIndex, startIndex + MaxTokenLength)
+          )
+          textSpan = @domElementPool.buildElement("span")
+
+          textSpan.appendChild(textNode)
+          scopeNode.appendChild(textSpan)
           startIndex += MaxTokenLength
       else
-        scopeNode.insertAdjacentText("beforeend", @sliceText(tokenText, startIndex, endIndex))
+        scopeNode.appendChild(
+          @domElementPool.buildText(@sliceText(tokenText, startIndex, endIndex))
+        )

       scopeNode.appendChild(trailingWhitespaceNode) if trailingWhitespaceNode?

@@ -275,9 +281,9 @@
     if endOfLineInvisibles?
       for invisible in endOfLineInvisibles
         hasInvisibles = true
-        lineNode.appendChild(
-          @domElementPool.buildElement("span", "invisible-character", invisible)
-        )
+        invisibleSpan = @domElementPool.buildElement("span", "invisible-character")
+        invisibleSpan.appendChild(@domElementPool.buildText(invisible))
+        lineNode.appendChild(invisibleSpan)

     hasInvisibles


Add dryrun option to gulp deploy (#900)

Add dryrun option to gulp deploy (#900)

--- gulp/tasks/build.coffee
+++ gulp/tasks/build.coffee
@@ -16,21 +16,28 @@
   $.sequence 'reset', 'build'

-gulp.task 'deploy', 'Deploy project to Google App Engine.', ['build'], ->
-  options = yargs process.argv, configuration:
-    'boolean-negation': false
-    'camel-case-expansion': false
-  delete options['_']
-  options_str = ''
-  for k of options
-    if options[k] == true
-      options[k] = ''
-    options_str += " #{if k.length > 1 then '-' else ''}-#{k} #{options[k]}"
+gulp.task 'deploy',
+  'Deploy project to Google App Engine. Available options:', ['build'], ->
+    options = yargs process.argv, configuration:
+      'boolean-negation': false
+      'camel-case-expansion': false
+    delete options['_']
+    options_str = ''
+    dryrun = ''
+    for k of options
+      if k == 'dryrun'
+        dryrun = 'echo DRYRUN - would run: '
+      else
+        if options[k] == true
+          options[k] = ''
+        options_str += " #{if k.length > 1 then '-' else ''}-#{k} #{options[k]}"

-  gulp.src('run.py').pipe $.start [{
-    match: /run.py$/
-    cmd: "gcloud app deploy main/*.yaml#{options_str}"
-  }]
+    gulp.src('run.py').pipe $.start [{
+      match: /run.py$/
+      cmd: "#{dryrun}gcloud app deploy main/*.yaml#{options_str}"
+    }]
+  ,  options:
+      'dryrun': 'run all preparations but do not actually deploy'

 gulp.task 'run',


fix that waitingCustomer popover would animate

--- app/assets/javascripts/app/controllers/chat.coffee
+++ app/assets/javascripts/app/controllers/chat.coffee
@@ -122,6 +122,7 @@
       animation:  false
       delay:      0
       placement:  'bottom'
+      container:  'body' # place in body do prevent it from animating
       title: ->
         App.i18n.translateContent('Waiting Customers')
       content: =>
@@ -134,6 +135,7 @@
       animation:  false
       delay:      0
       placement:  'bottom'
+      container:  'body'
       title: ->
         App.i18n.translateContent('Chatting Customers')
       content: =>
@@ -146,6 +148,7 @@
       animation:  false
       delay:      0
       placement:  'bottom'
+      container:  'body'
       title: ->
         App.i18n.translateContent('Active Agents')
       content: =>


Fixes typo

Fixes typo

--- app/assets/javascripts/components/room.coffee.cjsx
+++ app/assets/javascripts/components/room.coffee.cjsx
@@ -83,7 +83,7 @@

-
Volume2

+
Volume


fix publishable bug

fix publishable bug

--- app/assets/javascripts/views/publishable.js.coffee
+++ app/assets/javascripts/views/publishable.js.coffee
@@ -9,9 +9,11 @@
   saveAndPublish: (e) ->
     e.preventDefault()
     $('#page_published').attr('checked', true)
+    $('#page_published').val(true)
     $(e.target).parents('form').submit()

   saveAsDraft: (e) ->
     e.preventDefault()
     $('#page_published').attr('checked', false)
+    $('#page_published').val(false)
     $(e.target).parents('form').submit()


Karma auto-requires chai and sinon

Karma auto-requires chai and sinon

And sinon causes error's if it's required outside of the Karma config

--- test/helpers/job.spec.coffee
+++ test/helpers/job.spec.coffee
@@ -1,5 +1,4 @@
-{expect} = require 'chai'
-sinon = require 'sinon'
+expect = chai.expect
 _ = require 'underscore'

 JobHelper = require '../../src/helpers/job'


Remove extra newline

Remove extra newline

--- app/assets/javascripts/dashboard.js.coffee
+++ app/assets/javascripts/dashboard.js.coffee
@@ -3,7 +3,6 @@
   $('a#fetch-pull-requests').click (event) ->
     event.preventDefault()
     $("#spinner, #search_button").toggle()
-

     $.ajax '/pull_request_download',
       dataType:'html',


BF-168 Bug fix 2

BF-168 Bug fix 2

--- app/assets/javascripts/libs/flycam2.coffee
+++ app/assets/javascripts/libs/flycam2.coffee
@@ -135,10 +135,8 @@

   getOffsets : (planeID) ->
     ind = @getIndices planeID
-    f   = @getPlaneScalingFactor planeID
-    #if @needsUpdate(planeID) then return [buffer[planeID], buffer[planeID]]
-    [ (@globalPosition[ind[0]] - @texturePosition[planeID][ind[0]]) + @buffer[planeID],
-      (@globalPosition[ind[1]] - @texturePosition[planeID][ind[1]]) + @buffer[planeID]]
+    [ (@globalPosition[ind[0]] - @texturePosition[planeID][ind[0]])/Math.pow(2, @getZoomStep planeID) + @buffer[planeID],
+      (@globalPosition[ind[1]] - @texturePosition[planeID][ind[1]])/Math.pow(2, @getZoomStep planeID) + @buffer[planeID]]

   #notifyNewTextureXY : ->
   #  @texturePositionXY = @globalPosition.slice()    #copy that position


Prevent MergeStatusPoller from throwing exception on the login page

--- app/assets/javascripts/merge_status.coffee
+++ app/assets/javascripts/merge_status.coffee
@@ -43,10 +43,10 @@
         @onPageChange()

   isMergeQueueEnabled: =>
-    document.querySelector('.merge-status-container .js-details-container').hasAttribute('data-queue-enabled')
+    document.querySelector('.merge-status-container .js-details-container')?.hasAttribute('data-queue-enabled')

   mergeStatus: =>
-    document.querySelector('.merge-status-container .js-details-container').getAttribute('data-merge-status') || 'unknown'
+    document.querySelector('.merge-status-container .js-details-container')?.getAttribute('data-merge-status') || 'unknown'

 class AjaxAction
   constructor: (@poller) ->


Rewritten "if" as guard

Rewritten "if" as guard

--- app/js/util/scrollto.coffee
+++ app/js/util/scrollto.coffee
@@ -7,4 +7,6 @@
         axis: 'y'

 Factlink.on 'factlink.factsLoaded', ->
-  scrollTo FactlinkConfig.scrollto if FactlinkConfig?.scrollto?
+  return unless FactlinkConfig.scrollto?
+
+  scrollTo FactlinkConfig.scrollto


improve mark constructor / initialization

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -23,9 +23,8 @@
       e.preventDefault()
       @mark = new Mark(e, @)
       @marks.create(@mark)
+      @mark.draw(e)
       @mark.onMouseDown(e)
-      @mark.dragging = true
-      @mark.draw(e)

   plotPoints: (xMin = @smallestX, xMax = @largestX) ->
     @xMin = xMin
@@ -112,7 +111,8 @@
     @element.style.textAlign = 'center'

     @startingPoint = @toCanvasXPoint(e) - @minWidth()
-    @dragging = false
+
+    @dragging = true

     @element.addEventListener 'mousemove', @updateCursor
     @element.addEventListener 'mousedown', @onMouseDown
@@ -125,12 +125,12 @@
     markLeftX = Math.max @startingPoint - @maxWidth(), Math.min @startingPoint, @toCanvasXPoint(e)
     markRightX = Math.max @startingPoint, @toCanvasXPoint(e)

-
+    # no overlapping of marks
     markLeftX = (Math.max markLeftX, (@closestXBelow || markLeftX))
     markRightX = (Math.min markRightX, (@closestXAbove || markRightX))

+    # max and min width on creating / resizing marks
     width = (Math.min (Math.max (Math.abs markRightX - markLeftX), @minWidth()), @maxWidth())
-

     @element.style.left = markLeftX + "px"
     @element.style.width = width + "px"
@@ -139,6 +139,8 @@

   move: (e) ->
     markWidth = parseInt(@element.style.width, 10)
+
+    # no overlapping of marks
     leftXPos = Math.max (@toCanvasXPoint(e) - @pointerOffset), (@closestXBelow || 0)
     leftXPos = Math.min leftXPos, ((@closestXAbove || @canvas.width) - markWidth)


update timestamp format with timezone

update timestamp format with timezone

--- js/libs/time.coffee
+++ js/libs/time.coffee
@@ -27,7 +27,7 @@
       date.format "MMM YYYY"

   time_since_timestamp: (time_string) =>
-    moment(time_string).format('llll');
+    moment(time_string).format('llll ZZ');

   as_duration: (duration) =>
     if not duration


Fixed not being able to drag thangs around every other attempt.

--- app/views/editor/level/thangs/ThangsTabView.coffee
+++ app/views/editor/level/thangs/ThangsTabView.coffee
@@ -408,6 +408,11 @@
     thang.isSelectable = not thang.isLand for thang in @world.thangs  # let us select walls and such
     @surface?.setWorld @world
     @selectAddThangType @addThangType, @cloneSourceThang if @addThangType  # make another addThang sprite, since the World just refreshed
+
+    # update selection, since the thangs have been remade
+    if @selectedExtantThang
+      @selectedExtantSprite = @surface.spriteBoss.sprites[@selectedExtantThang.id]
+      @selectedExtantThang = @selectedExtantSprite.thang
     Backbone.Mediator.publish 'editor:thangs-edited', thangs: @world.thangs

   onTreemaThangSelected: (e, selectedTreemas) =>


ThangsTabView letting you position spike walls on top of walls.

--- app/views/editor/level/thangs/ThangsTabView.coffee
+++ app/views/editor/level/thangs/ThangsTabView.coffee
@@ -16,7 +16,7 @@
 MOVE_SPEED = 13

 # Let us place these on top of other Thangs
-overlappableThangTypeNames = ['Torch', 'Chains', 'Bird', 'Cloud 1', 'Cloud 2', 'Cloud 3', 'Waterfall', 'Obstacle', 'Electrowall']
+overlappableThangTypeNames = ['Torch', 'Chains', 'Bird', 'Cloud 1', 'Cloud 2', 'Cloud 3', 'Waterfall', 'Obstacle', 'Electrowall', 'Spike Walls']

 class ThangTypeSearchCollection extends CocoCollection
   url: '/db/thang.type?project=original,name,version,slug,kind,components'


Add trailing whitespace to line

Add trailing whitespace to line

--- spec/text-editor-spec.coffee
+++ spec/text-editor-spec.coffee
@@ -3412,6 +3412,9 @@
     describe "when no text is selected", ->
       describe "when the line below isn't empty", ->
         it "joins the line below with the current line separated by a space and moves the cursor to the start of line that was moved up", ->
+          editor.setCursorBufferPosition([0, Infinity])
+          editor.insertText('  ')
+          editor.setCursorBufferPosition([0])
           editor.joinLines()
           expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () { var sort = function(items) {'
           expect(editor.getCursorBufferPosition()).toEqual [0, 29]


get feedback when an action is started with notifications

--- app/modules/vm/index.coffee
+++ app/modules/vm/index.coffee
@@ -39,11 +39,41 @@
           $scope.VDIs.push VDI if VDI?
     )

-    $scope.startVM = xo.vm.start
-    $scope.stopVM = xo.vm.stop
-    $scope.force_stopVM = (id) -> xo.vm.stop id, true
-    $scope.rebootVM = xo.vm.restart
-    $scope.force_rebootVM = (id) -> xo.vm.restart id, true
+    $scope.startVM = (id) ->
+      xo.vm.start id
+      notify.info {
+        title: 'VM starting...'
+        message: 'Start VM'
+      }
+
+    $scope.stopVM = (id) ->
+      xo.vm.stop id
+      notify.info {
+        title: 'VM shutdown...'
+        message: 'Gracefully shutdown the VM'
+      }
+
+    $scope.force_stopVM = (id) ->
+      xo.vm.stop id, true
+      notify.info {
+        title: 'VM force shutdown...'
+        message: 'Force shutdown the VM'
+      }
+
+    $scope.rebootVM = (id) ->
+      xo.vm.restart id
+      notify.info {
+        title: 'VM reboot...'
+        message: 'Gracefully reboot the VM'
+      }
+
+    $scope.force_rebootVM = (id) ->
+      xo.vm.restart id, true
+      notify.info {
+        title: 'VM reboot...'
+        message: 'Force reboot the VM'
+      }
+
     $scope.migrateVM = (id, hostId) ->
       (xo.vm.migrate id, hostId).catch (error) ->
         modal.confirm


Add some missing unit test mocks

Add some missing unit test mocks

--- services/web/test/unit/coffee/User/UserUpdaterTests.coffee
+++ services/web/test/unit/coffee/User/UserUpdaterTests.coffee
@@ -29,6 +29,8 @@
 				removeAffiliation: @removeAffiliation
 			"../../infrastructure/mongojs":@mongojs
 			"metrics-sharelatex": timeAsyncMethod: sinon.stub()
+			"settings-sharelatex": @settings = {}
+			"request": @request = {}

 		@stubbedUser =
 			_id: "3131231"


layer add fix

layer add fix

--- js/views/map.coffee
+++ js/views/map.coffee
@@ -30,8 +30,12 @@
         utils.detailMap = new Map(@mapElement)

     initLayers: ->
+      if(@routeLayer and @mapElement == "map")
+        utils.map.instance.removeLayer(@routeLayer)
       @routeLayer = new OpenLayers.Layer.Vector("Route")
       @addLayer(@routeLayer)
+      if(@poiLayer and @mapElement == "map")
+        utils.map.instance.removeLayer(@poiLayer)
       @poiLayer = new OpenLayers.Layer.Vector("POIs", {styleMap: utils.poiStyleMap})
       if(@mapElement == "map")
         selectControl = new OpenLayers.Control.SelectFeature(


Another fix for formatKpiTarget causing troubles..

This should do it..

--- src/services/kpis/kpis.svc.coffee
+++ src/services/kpis/kpis.svc.coffee
@@ -145,6 +145,7 @@
     # @param mappings [Array] array of objects to map mode names to given labels e.g [{label:
     #                         'over', mode: 'min'}]
     @formatKpiTarget = (target, unit, mappings=[])->
+      return '' unless target && unit
       targetMode = _.keys(target)[0]
       mapping = _.find(mappings, (map)-> map.mode == targetMode ) || {}
       "#{(mapping.label || targetMode)} #{$filter('mnoCurrency')(target[targetMode], unit, false)}"


Trying to get timestamps working

Trying to get timestamps working

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

-    bot.on "TimestampMessage", withAuthor (id, created, room, user, body, author) ->
-      unless bot.info.id == author.id
-        self.receive new Robot.TimestampMessage(author)
+    bot.on "TimestampMessage", () ->
+      console.log "Timestamp"
+      self.receive new Robot.TimestampMessage(null)

     bot.Me (err, data) ->
       bot.info = data.user


remove !! hack on isComment

remove !! hack on isComment

--- src/app/language-mode.coffee
+++ src/app/language-mode.coffee
@@ -184,7 +184,7 @@
     return unless /\w/.test(@editSession.lineForBufferRow(bufferRow))

     isRowComment = (row) =>
-      !!@editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(row).isComment()
+      @editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(row).isComment()

     if isRowComment(bufferRow)
       isOriginalRowComment = true


Added template ifwith and ifloop snippets

--- completions.cson
+++ completions.cson
@@ -89,6 +89,60 @@
   'module': 'silverstripe/framework'
   'scope': '.text.html.ss'
   'body': '<% else %>\n\t${0}'
+
+# Template scope & conditional logic
+'if Loop':
+  'prefix': 'ifloop'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.ss'
+  'variations': [
+    {
+      'minVersion': '3.0'
+      'body': """
+      <% if ${1} %>
+      \t<% loop ${1} %>
+      \t\t${2}
+      \t<% end_loop %>
+      <% end_if %>
+      """
+    }
+    {
+      'maxVersion': '3.0'
+      'body': """
+      <% if ${1} %>
+      \t<% control ${1} %>
+      \t\t${2}
+      \t<% end_control %>
+      <% end_if %>
+      """
+    }
+  ]
+'if With':
+  'prefix': 'ifwith'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.ss'
+  'variations': [
+    {
+      'minVersion': '3.0'
+      'body': """
+      <% if ${1} %>
+      \t<% with ${1} %>
+      \t\t${2}
+      \t<% end_with %>
+      <% end_if %>
+      """
+    }
+    {
+      'maxVersion': '3.0'
+      'body': """
+      <% if ${1} %>
+      \t<% control ${1} %>
+      \t\t${2}
+      \t<% end_control %>
+      <% end_if %>
+      """
+    }
+  ]

 'Include':
   'prefix': 'include'


more updates to support recursive optimization

pertains to #3

--- component.coffee
+++ component.coffee
@@ -82,13 +82,18 @@
       log "installing #{name} to #{tgtDir}"
       wrench.copyDirSyncRecursive src, join(tgtDir, name)

+appBuildProfile = (filename = "app.build.js", dir = ".") ->
+  eval read(join(dir, filename))
+
 allNodeModules = () ->
   modules = {}
   t.bfs directories = {path: "."}, (n) ->
     if exists join(n.path, "package.json")
       n.json = pkgJson n.path
     if exists join(n.path, "requirejs-config.json")
-      n.config = requirejsConfig(null, n.path)
+      n.config = requirejsConfig null, n.path
+    if exists join(n.path, "app.build.js")
+      n.build = appBuildProfile null, n.path
     modulesDir = join n.path, "node_modules"
     if exists modulesDir
       n.children = _(fs.readdirSync(modulesDir)).map((d) ->
@@ -308,8 +313,23 @@

         # returns the on-disk filename for a css dep like 'css!100:foo.css'
         cssFilename = (moduleName) ->
-          filename = moduleName.replace /^css!(\d+:)?/, ''
-          if filename[-4..] isnt ".css" then filename + ".css" else filename
+          _.compose.apply(_, [
+            # remove plugin prefix css!100:
+            (n) -> n.replace /^css!(\d+:)?/, ''
+
+            # check the paths config, replace the path if it's in there
+            (n) ->
+              for pfx,pth of config.paths
+                if n[...pfx.length] is pfx
+                  return pth + n[pfx.length..]
+              n
+
+            # add the .css extension
+            (n) -> if n[-4..] isnt ".css" then n + ".css" else n
+
+            # prefix the path with the path to the static directory on disk
+            (n) -> join argv.staticpath, n
+          ].reverse())(moduleName)

         # returns the order for a css dep like 'css!100:foo.css'
         cssOrder = (moduleName) ->
@@ -322,12 +342,20 @@

         # return the contents of `filename` w/ the css url(...) paths
         # re-written appropriately
-        cssWithPathsReWritten = (filename) ->
-          updateCssPaths read(join(argv.staticpath, filename)), (url) ->
-            if url[0] isnt "/"
-              join(argv.baseurl, dirname(filename), url)
-            else
-              url
+        cssWithPathsReWritten = (fname) ->
+          updateCssPaths read(fname), (u) ->
+            if u[0] isnt "/" then join(argv.baseurl, dirname(fname), u) else u
+
+        # go through all the components build configs and create a 'paths'
+        # config for the require.js optimizer that includes empty modules for
+        # all excluded files
+        pathsConfig = () ->
+          allComponents().reduce (paths, component) ->
+            if component.build?.exclude
+              paths[module] = "empty:" for module in component.build.exclude
+            paths
+          , extend(true, {strings: "empty:"}, config.paths)
+

         output = (text, opts) ->
           text = minify(text) if opts.minify
@@ -345,11 +373,15 @@
           write cssName, cssText
           log "writing optimized#{opts.minify and "/minified" or ""} file to #{cssName}"

-        for module in buildProfile().modules
+        profile = buildProfile()
+        for module in profile.modules
           css = {}
+          if isComponent() and /^\.[\/\\]/.test(module.name)
+            module.name = join(config.paths[componentName()], module.name[2..])
           requirejs.optimize
             baseUrl: argv.staticpath
-            paths: config.paths
+            paths: pathsConfig()
+            shim: config.shim
             name: module.name
             optimize: "none"
             keepBuildDir: true


MachineSettingsAdvancedView: Copy changes.

--- client/app/lib/providers/machinesettingsadvancedview.coffee
+++ client/app/lib/providers/machinesettingsadvancedview.coffee
@@ -12,18 +12,20 @@

     @machine = data

-    reinitButton   = @createButton 'Reinitialize your VM', 'reinit', 'Reset your VM back to its original state'
+    reinitButton   = @createButton 'Reinitialize your VM', 'reinit', 'This will take your VM back to its original state. (Note: you will loose all your data)'
     reassignButton = @createButton 'Reassign your VM', 'reassign', 'Reassign your VM to another node'
     terminateTitle = 'Terminate your VM'
+    terminateText  = 'This will delete your VM completely. (Note: you will loose all your data)'

     if @machine.isManaged()
       reinitButton.hide()
       reassignButton.show()
-      terminateTitle = 'Delete your VM'
+      terminateTitle = 'Disconnect your VM'
+      terminateText  = 'Remove the connection between your VM and Koding.'
     else
       reassignButton.hide()

-    terminateButton = @createButton terminateTitle, 'terminate', 'Completely remove your VM'
+    terminateButton = @createButton terminateTitle, 'terminate', terminateText

   createButton: (title, className, desc) ->


MachineSettingsModal: Show a warning on reinit if machine provider is aws.

--- client/app/lib/providers/machinesettingsadvancedview.coffee
+++ client/app/lib/providers/machinesettingsadvancedview.coffee
@@ -50,8 +50,12 @@
     if tagName is 'FIGURE' or parentTagName is 'FIGURE' or tagName is 'P'

       switch buttonType
-        when 'reinit'    then computeController.reinit  @machine
         when 'terminate' then computeController.destroy @machine
         when 'reassign'  then new FindManagedNodesModal reassign: yes, @machine
+        when 'reinit'
+          if @machine.provider is 'aws'
+            return new kd.NotificationView title: 'Coming soon', duration: 4500
+
+          computeController.reinit  @machine

       @emit 'ModalDestroyRequested'


Code styling

Code styling

--- client/stacks/lib/views/stacks/stacktemplatelistview.coffee
+++ client/stacks/lib/views/stacks/stacktemplatelistview.coffee
@@ -1,7 +1,5 @@
 kd                          = require 'kd'
-
 curryIn                     = require 'app/util/curryIn'
-
 StackTemplateList           = require 'app/stacks/stacktemplatelist'
 StackTemplateListController = require 'app/stacks/stacktemplatelistcontroller'

@@ -14,12 +12,9 @@

     super options, data

-    @list           = new StackTemplateList
     @listController = new StackTemplateListController
-      view       : @list
-      wrapper    : no
-      scrollView : no
-      viewType   : options.viewType
+      view          : new StackTemplateList
+      viewType      : options.viewType

     @listView = @listController.getView()


Fixing typo.

Fixing typo.

--- test/test_commandline.coffee
+++ test/test_commandline.coffee
@@ -96,7 +96,7 @@
         'works' : (error, stdout, stderr) ->
             assert.isNotNull(error)

-    'with example configuratin' :
+    'with example configuration' :

         topic : () ->
             args = [


NEW: logicalAreaInAncestor

NEW: logicalAreaInAncestor

--- source/Art/Engine/Core/Drawing/ElementDrawAreaMixin.coffee
+++ source/Art/Engine/Core/Drawing/ElementDrawAreaMixin.coffee
@@ -115,6 +115,10 @@

         drawArea ? @drawArea

+      logicalAreaInAncestor: (ancestor) ->
+        (@getElementToElementMatrix ancestor)
+        .transformBoundingRect @logicalArea
+
     # overridden by some children (Ex: Filter)
     _drawAreaChanged: ->
       if @_elementSpaceDrawArea


fix notifications date range search result announcement

fixes CNVS-35651

test plan:
- From the Account Settings, go to the Admin Tools
- Click on the View Notifications Tab
- With a Screen Reader active, Search for a user
- note that it announces the number of results when they're displayed
- Select a user, and select a date range from which that user has
  messages
- Click on the find button, and note that it announces results to the
  screen reader.

Change-Id: I4536b5f5282a891a5b8ea5bde5028f6781599f43
Reviewed-on: https://gerrit.instructure.com/106005
Reviewed-by: Steven Burnett <a0b450117c5d20a22d5c4d52bb8ae13adee003d8@instructure.com>
Tested-by: Jenkins
QA-Review: Deepeeca Soundarrajan <5bcd4b623f00b7fc2942cd1c65c245b30a48a61f@instructure.com>
Product-Review: Felix Milea-Ciobanu <9384aa226ac9d3237ed0bc52e18ffcb251ee0bf7@instructure.com>

--- app/coffeescripts/views/accounts/admin_tools/UserDateRangeSearchFormView.coffee
+++ app/coffeescripts/views/accounts/admin_tools/UserDateRangeSearchFormView.coffee
@@ -46,9 +46,13 @@
       @inputFilterView.collection.on 'setParam deleteParam', @fetchUsers
       @usersView.collection.on 'selectedModelChange', @selectUser
       @usersView.collection.on 'sync', @resultsFound
+      @collection.on 'sync', @notificationsFound

     resultsFound: =>
       $.screenReaderFlashMessage(I18n.t('results_found', "%{length} results found", { length: @usersView.collection.length }))
+
+    notificationsFound: =>
+      $.screenReaderFlashMessage(I18n.t('%{length} notifications found', { length: @collection.length }))

     fetchUsers: =>
       @selectUser null


add blank query params for settings

add blank query params for settings

--- tutor/src/components/course-calendar/add-menu-mixin.cjsx
+++ tutor/src/components/course-calendar/add-menu-mixin.cjsx
@@ -89,6 +89,7 @@
         text: linkText
         to: "/course/#{courseId}/settings"
         type: 'none'
+        query: {}
       }]

     renderLink = @renderMenuLink or @menuMixinRenderMenuLink


remove old createCard function

remove old createCard function

--- js/inner/billing.coffee
+++ js/inner/billing.coffee
@@ -143,7 +143,6 @@
         @chosenPlan(plan)
         $("#confirmForm").modal({keyboard: false}) # TODO: eww
       else
-        #@createCard(plan, event)
         @newPlan(plan, event)
     else
       VM.raiseIntercomDialog("I'd like ask about enterprise pricing...\n\n")
@@ -167,18 +166,6 @@
     name: "CircleCI"
     address: false
     email: VM.current_user().selected_email()
-
-  createCard: (plan, event) =>
-    vals =
-      panelLabel: 'Add card',
-      price: 100 * plan.price
-      description: "#{plan.name} plan"
-      token: (token) =>
-        @chosenPlan(plan)
-        @recordStripeTransaction event, token
-
-    StripeCheckout.open($.extend @stripeDefaults(), vals)
-

   updateCard: (data, event) =>
     vals =


Add Roxy Front Choice

Add Roxy Front Choice

--- src/coffee/game/front.coffee
+++ src/coffee/game/front.coffee
@@ -64,6 +64,15 @@
         switch student
           when 1 then 'student1'

+    front choice 'roxy',
+      'Pursue Roxy the gang leader'
+      'She wants Mrs. Brown out of town. But why?'
+      visible:
+        lead: reqs.eq leads.roxy
+      next: (roxy) ->
+        switch roxy
+          when 2 then 'roxy1'
+
     front choice 'dealerFront',
       'Visit the Rarities Dealer'
       'Perhaps you have something he\'d like to trade.'


More refactoring

More refactoring

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -198,20 +198,6 @@
   for el, i in list by 2
     [el, list[i + 1]]

-labelTop = (ast) ->
-  {wheres} = fnImplementation inside ast
-  labelWhere wheres
-  ast
-
-labelBottom = (ast) ->
-  {wheres} = bottomImplementation inside ast
-  labelWhere wheres
-  ast
-
-labelDefinitions = (ast) ->
-  labelWhere whereList inside ast
-  ast
-
 labelFns = (ast) ->
   macro 'fn', ast, (node, args) ->
     node.type = 'function'
@@ -287,32 +273,60 @@
   walk ast, (node) ->
     [openDelim, operator] = node
     if openDelim.token is '('
-      if not operator.type and operator.label is 'bare'
-        operator.label = 'operator'
-    else if node.type isnt 'pattern'
-      [openDelim, ..., closeDelim] = node
-      openDelim.label = 'operator'
-      closeDelim.label = 'operator'
+      labelFnCall operator
+    else
+      labelDelimeterCall node
     node
+
+labelFnCall = (operator) ->
+  if not operator.type and operator.label is 'bare'
+    operator.label = 'operator'
+
+labelDelimeterCall = (node) ->
+  if node.type isnt 'pattern'
+    [openDelim, ..., closeDelim] = node
+    openDelim.label = 'operator'
+    closeDelim.label = 'operator'
+
+
+# Standard labelling and typing

 typifyMost = (ast) ->
   apply ast, typeComments, typeDatas, typeTypes, labelSimple, labelMatches,
     labelFns, labelRequires, labelComments

+# Special labeling for files
+
+labelTop = (ast) ->
+  {wheres} = fnImplementation inside ast
+  labelWhere wheres
+  ast
+
+labelBottom = (ast) ->
+  {wheres} = bottomImplementation inside ast
+  labelWhere wheres
+  ast
+
+labelDefinitions = (ast) ->
+  labelWhere whereList inside ast
+  ast
+
+# Labelling and typing different kinds of ASTs
+
+typifyExp = (ast) ->
+  typifyWith ast, (x) -> x
+
+typifyTop = (ast) ->
+  typifyWith ast, labelTop
+
+typifyBottom = (ast) ->
+  typifyWith ast, labelBottom
+
+typifyDefinitions = (ast) ->
+  typifyWith ast, labelDefinitions
+
 typifyWith = (ast, mainLabeling) ->
   apply ast, typifyMost, mainLabeling, labelOperators
-
-typifyExp = (ast) ->
-  typifyWith ast, (x) -> x
-
-typifyTop = (ast) ->
-  typifyWith ast, labelTop
-
-typifyBottom = (ast) ->
-  typifyWith ast, labelBottom
-
-typifyDefinitions = (ast) ->
-  typifyWith ast, labelDefinitions

 toHtml = (highlighted) ->
   crawl highlighted, (word) ->
@@ -324,6 +338,8 @@
     result = fn result
   result

+# Syntax printing to HTML
+
 syntaxedTop = (source) ->
   collapse inside toHtml typifyTop astize tokenize "(#{source})"

@@ -332,9 +348,6 @@

 tokenizedDefinitions = (source) ->
   (parentize shiftPos [inside typifyDefinitions astize tokenize "(#{source})"])[0]
-
-tokenizedExp = (source) ->
-  parentize typifyExp astize tokenize source

 # Correct offset by 1 in positions, when we wrap the source in an S-exp
 shiftPos = (ast) ->
@@ -355,9 +368,16 @@
       collapsed += node
   collapsed

+# end of Syntax printing
+
+# Main compilation from plain text source to tokens
+
 variableCounter = 1
 warnings = []
 nonstrict = no
+
+tokenizedExp = (source) ->
+  parentize typifyExp astize tokenize source

 # exp followed by list of definitions
 compiledTop = (source) ->
@@ -389,6 +409,7 @@
   variableCounter = 1
   compileWheresInModule preCompileDefs source

+# for including in other files
 exportList = (source) ->
   wheres = whereList inside preCompileDefs source
   names = []
@@ -460,6 +481,8 @@
       "var #{identifier} = exports['#{identifier}'] = #{compileImpl def};"
   else
     compileDef [name, def]
+
+# Actual compilation of s-expressions starts here

 compileImpl = (node, hoistableWheres = []) ->
   # For now special case match to allow definition hoisting up one level
@@ -516,6 +539,8 @@
                 else
                   throw new Error "#{names.join ', '} used but not defined"
             escapedName
+
+# Recognizing arrays, tuples and map literals

 isList = (node) ->
   Array.isArray(node) and node[0].token is '['


Fixed kind printing

Fixed kind printing

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -5204,7 +5204,7 @@

 arityOfKind = (kind) ->
   if kind instanceof KindFn
-    1 + arityOfKind kind.arg
+    1 + arityOfKind kind.to
   else
     0


Marked correctly origin of types for uniform collections

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1109,12 +1109,13 @@
     keys = (map (__ string_, _labelName), labels)
     assignCompile ctx, form, (irMap keys, compiledItems)

-
+# collectionType will be discarded, don't use it afterwards
 uniformCollectionCompile = (ctx, form, items, collectionType, moreConstraints = []) ->
+  markedType = mapWithOrigin collectionType, if isCall form then _operator form else form
   {constraints, itemType, compiled} = termsCompileExpectingSameType ctx, form, items
   (labelOperator form) if not isCall form
   form.tea = new Constrained (join moreConstraints, constraints),
-      (withOrigin (new TypeApp collectionType, itemType), form)
+      (withOrigin (new TypeApp markedType, itemType), form)
   compiled

 termsCompileExpectingSameType = (ctx, origin, items) ->
@@ -5004,6 +5005,12 @@
       mutateMappingOrigin type.op, expression
       mutateMappingOrigin type.arg, expression
     mutateMarkingOrigin type, expression
+
+# Used only on types which are being constructed from builtins and can be cloned
+mapWithOrigin = (type, expression) ->
+  clone = cloneType type
+  mapOrigin clone, expression
+  clone

 # Used on builtin types
 markOrigin = (typeOrConstraint, expression) ->