comment parts in the OPF

comment parts in the OPF

--- scripts/gh-book/opf-file.coffee
+++ scripts/gh-book/opf-file.coffee
@@ -21,6 +21,7 @@
     branch: true # This element will show up in the sidebar listing

     initialize: () ->
+      # For TocNode, let it know this is the root
       super {root:@}

       # Contains all entries in the OPF file (including images)
@@ -43,15 +44,25 @@
           # Keep track of local changes if there is a remote conflict
           @_localNavAdded[model.id] = model

+
+      # If a node was added-to/removed-from a TocNode ensure it is/is-not in the set of `tocNodes`
+      # TODO: This may be redundant and may be able to be removed
       @tocNodes.on 'tree:add',    (model, collection, options) => @tocNodes.add model, options
       @tocNodes.on 'tree:remove', (model, collection, options) => @tocNodes.remove model, options

+      # When the book title changes update the OPF XML
+      # TODO: use the value of `@get('title')` when serializing instead.
       @on 'change:title', (model, value, options) =>
         $title = @$xml.find('title')
         if value != $title.text()
           $title.text(value)
           @_save()

+      # When a title changes on one of the nodes in the ToC:
+      #
+      # 1. remember the change
+      # 2. try to autosave
+      # 3. if a remote conflict occurse the remembered change will be replayed (see `onReloaded`)
       @tocNodes.on 'change:title', (model, value, options) =>
         return if not model.previousAttributes()['title'] # skip if we are parsing the file
         return if @ == model # Ignore if changing the OPF title
@@ -107,6 +118,12 @@
       # This is useful when the OPF file was remotely updated
       @_localAddedItems[model.id] = model

+    # Called on "autosave".
+    # Only save the navModel and new files (and all the OPF files).
+    # Delay the save and if more than one thing changed during SAVE_DELAY
+    # only save once.
+    #
+    # Reason for SAVE_DELAY: a "move" is 2 operations, `remove` followed by `add`
     _save: ->
       clearTimeout(@_savingTimeout)
       @_savingTimeout = setTimeout (() =>
@@ -114,7 +131,8 @@
         delete @_savingTimeout
       ), SAVE_DELAY

-
+    # A book is not loaded until the navModel is loaded.
+    # Once the navModel is loaded, "autosave" whenever it changes.
     _loadComplex: (fetchPromise) ->
       fetchPromise
       .then () =>
@@ -358,6 +376,9 @@
         #@tocNodes.add node
       return node

+    # Do not change the contentView when the book opens
+    contentView: null
+
     # Change the sidebar view when editing this
     sidebarView: (callback) ->
       require ['cs!views/workspace/sidebar/toc'], (View) =>


confilt resolution

confilt resolution

--- app/assets/javascripts/cms/dust/sortables.js.coffee
+++ app/assets/javascripts/cms/dust/sortables.js.coffee
@@ -18,8 +18,11 @@
     $("ul#tree").nestedSortable @options

     $('.order').bind 'click', ()->
-      listClass = "." + $(@).data("list-class") + " .index-item"
-      Dust.sortables.saveItems(listClass)
+      if $(@).data("list-class")
+        Dust.sortables.listClass = "." + $(@).data("list-class") + " .index-item"
+      else
+        Dust.sortables.listClass = null
+      Dust.sortables.saveItems()
       false

   saveItems : ()->
@@ -37,10 +40,11 @@
   className: ()->
     $('.order').data('class')

-  list: (listClass=nil)->
-    listClass ||= '.index-item, .sortable-item'
+  list: ()->
+    if @listClass == null
+      @listClass = '.index-item, .sortable-item'
     children = new Array()
-    @itemList = _.map($(listClass), (item, index)->
+    @itemList = _.map($(@listClass), (item, index)->
       data =
         id: $(item).data('id')
         position: index
@@ -53,8 +57,6 @@
         return data
     )
     @buildChildren(children)
-    #console.log @itemList
-    #console.log children

     _.compact(@itemList)


Inteligently rank exact match highest

Inteligently rank exact match highest

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SuggestionManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SuggestionManager.coffee
@@ -10,7 +10,10 @@
 			if window?._ide?.browserIsSafari
 				limit = 100

+			# fully formed commands
 			realCommands = []
+			# commands which match the prefix exactly,
+			# and could be partially typed or malformed
 			incidentalCommands = []
 			seen = {}
 			iterations = 0
@@ -30,24 +33,22 @@
 					args++

 				commandHash = "#{command}\\#{optionalArgs}\\#{args}"
-				if !seen[commandHash]?
-					seen[commandHash] = true

-					# skip the first occurence of the current command at the cursor
-					if @prefix? && "\\#{command}" == @prefix
-						incidentalCommands.push [command, optionalArgs, args]
-					else
+				if @prefix? && "\\#{command}" == @prefix
+					incidentalCommands.push [command, optionalArgs, args]
+				else
+					if !seen[commandHash]?
+						seen[commandHash] = true
 						realCommands.push [command, optionalArgs, args]

 				# Reset to before argument to handle nested commands
 				@doc = docState

-			# check incidentals
+			# check incidentals, see if we should pluck out a match
 			if incidentalCommands.length > 1
 				bestMatch = incidentalCommands.sort((a, b) =>  a[1]+a[2] < b[1]+b[2])[0]
 				realCommands.push bestMatch

-			window.I = incidentalCommands
 			return realCommands

 		# Ignore single letter commands since auto complete is moot then.
@@ -58,8 +59,7 @@
 			if i == -1
 				return false
 			else
-				M = @doc.match(@commandRegex)
-				match = M[1]
+				match = @doc.match(@commandRegex)[1]
 				@doc = @doc.substr(i + match.length + 1)
 				return match


BF-49 Integration of Backbone

BF-49 Integration of Backbone

--- app/assets/javascripts/libs/gl_engine/flycam.coffee
+++ app/assets/javascripts/libs/gl_engine/flycam.coffee
@@ -74,7 +74,7 @@
     @move(@stepFront)

   pitch : (angle) ->
-    currentMatrix = M4x4.rotate(angle, [ 1, 0, 0 ], @currentMatrix)
+    @currentMatrix = M4x4.rotate(angle, [ 1, 0, 0 ], @currentMatrix)

   pitchDistance : (angle) ->
     @move(@stepBack)


fix script loading

fix script loading

--- app/assets/javascripts/pirates/socketHandler.coffee
+++ app/assets/javascripts/pirates/socketHandler.coffee
@@ -35,8 +35,8 @@
     @packetQueue.push packet

   onOpen = (event) ->
-  console.log 'Socket connection ready!'
-  console.log event
+    console.log 'Socket connection ready!'
+    console.log event

   onClose = (event) ->
     Console.logError "Verbindung zum Server verloren, bitte lade die Seite neu!"


Increased the offset from bottom you need to be to fetch more drops.

--- app/assets/javascripts/views/clouds/drops.js.coffee
+++ app/assets/javascripts/views/clouds/drops.js.coffee
@@ -39,7 +39,7 @@
       @removeDrop(model)

     $(@el).on 'scroll', =>
-      if $(@el).scrollTop() > (@el.scrollHeight - $(@el).innerHeight() - 100)
+      if $(@el).scrollTop() > (@el.scrollHeight - $(@el).innerHeight() - 250)
         @fetchMore() unless @collection.lastPage() or @currentlyLoading

   unbindEvents: ->


clean up todo flag

clean up todo flag

--- scripts/pagerv2_commands.coffee
+++ scripts/pagerv2_commands.coffee
@@ -122,7 +122,6 @@
         res.send e
       res.finish()

-  # TODO
   #   hubot pd [who is] next [oncall] - tells who is next on call
     robot.respond (
       /(?:pd )?(?:who(?: is|'s) )?(next on ?call|on ?call next)\s*$/


Add BAU to pivotal projects list

Add BAU to pivotal projects list

--- scripts/pivotal-listener.coffee
+++ scripts/pivotal-listener.coffee
@@ -42,7 +42,8 @@
         "Data Architecture Revamp": "DAR-",
         "DAR - ENG": "DARENG-",
         "Salesforce": "SFDC-",
-        "JAM": "JAM-"
+        "JAM": "JAM-",
+        "Business As Usual": "BAU-"

       project =
         projectId: reqBody["project"]["id"]


followfeed: disable temporarily

followfeed: disable temporarily

--- workers/social/lib/social/main.coffee
+++ workers/social/lib/social/main.coffee
@@ -126,5 +126,5 @@

 console.log "Koding Social Worker #{process.pid} has started."

-require './followfeed' # side effects
+# require './followfeed' # side effects


always return deferred from startUpload

always return deferred from startUpload

--- app/assets/javascripts/uploadcare/files/object.js.coffee
+++ app/assets/javascripts/uploadcare/files/object.js.coffee
@@ -69,7 +69,7 @@
         return df
       if @fileSize > 100 * 1024 * 1024
         @__rejectApi 'size'
-        return
+        return df

       formData = new FormData()
       formData.append('UPLOADCARE_PUB_KEY', @settings.publicKey)
@@ -112,7 +112,7 @@
         return df
       if @settings.imagesOnly
         @__rejectApi 'image'
-        return
+        return df

       @multipartStart().done (data) =>
         @uploadParts(data.parts).done =>


Update zh-TW.js.coffee

Update zh-TW.js.coffee

Fixed free to crop string.

--- app/assets/javascripts/uploadcare/locale/zh-TW.js.coffee
+++ app/assets/javascripts/uploadcare/locale/zh-TW.js.coffee
@@ -85,7 +85,7 @@
           crop:
             title: '裁切並加入這個圖片'
             done: '完成'
-            free: '剩餘'
+            free: '自由裁切'
           error:
             default:
               title: '錯誤！'


cleaning up

cleaning up

--- public/coffeescripts/renderer.coffee
+++ public/coffeescripts/renderer.coffee
@@ -37,7 +37,7 @@
       nbrPropsToShow = 3
       addEllipsis = nbrProps - nbrPropsToShow > 0
       text = text[0..nbrPropsToShow]
-      text.push "(and #{nbrProps-nbrPropsToShow} more)" if addEllipsis
+      text.push "(#{nbrProps-nbrPropsToShow} more)" if addEllipsis

     line[0..40] for line in text

@@ -72,7 +72,7 @@
       # Store dims so we can use them in hit testing later.
       # TODO sort out the MARGIN business (separate textSize from nodeSize)
       node.data.width = width + MARGIN*2;
-      node.data.height = height + MARGIN*2;
+      node.data.height = height;

       color = if node.data.first then "blue" else "green"
       util.roundRect(ctx, point, width+(MARGIN*2), height, color, 10)


new region: Cheile Nerei

new region: Cheile Nerei

--- src/data.coffee
+++ src/data.coffee
@@ -22,6 +22,7 @@
   fagaras:    {bbox: [24.30, 45.47, 24.89, 45.73], title: "Făgăraș"}
   iezer:      {bbox: [24.85, 45.38, 25.10, 45.55], title: "Iezer"}
   macin:      {bbox: [28.13, 45.07, 28.42, 45.29], title: "Măcin"}
+  nerei:      {bbox: [21.70, 44.80, 22.00, 45.03], title: "Cheile Nerei"}
   parang:     {bbox: [23.43, 45.31, 23.82, 45.41], title: "Parâng"}
   piatramare: {bbox: [25.59, 45.48, 25.75, 45.60], title: "Piatra Mare"}
   retezat:    {bbox: [22.72, 45.29, 23.00, 45.42], title: "Retezat"}
@@ -400,6 +401,8 @@

 altitudeList = (coordinateList) ->
+  return Q([]) unless coordinateList.length
+
   input = coordinateList.map((p) -> "#{p[0]} #{p[1]}\n").join('')

   exec("gdallocationinfo -wgs84 -valonly data/srtm-1arcsec-ro.tiff", input)


Don't call connection#close directly

Don't call connection#close directly

--- lib/assets/javascripts/cable.js.coffee
+++ lib/assets/javascripts/cable.js.coffee
@@ -80,7 +80,7 @@

     @waitForPingTimeout = setTimeout =>
       console.log "Ping took too long to arrive. Reconnecting.."
-      @connection?.close()
+      @reconnect()
     , @PING_STALE_INTERVAL * 1000

   clearPingWaitTimeout: =>


add comment to document optimization

add comment to document optimization

--- lib/assets/javascripts/twine.js.coffee
+++ lib/assets/javascripts/twine.js.coffee
@@ -213,7 +213,7 @@

     refresh = ->
       newValue = fn.call(node, context, rootContext)
-      return if newValue == lastValue
+      return if newValue == lastValue # return if we can and avoid a DOM operation

       lastValue = newValue
       return if newValue == node[valueAttribute]


Removed old visualization view

Removed old visualization view

--- enterprise/server/src/main/coffeescript/neo4j/webadmin/views/databrowser/VisualizedView.old.coffee
+++ enterprise/server/src/main/coffeescript/neo4j/webadmin/views/databrowser/VisualizedView.old.coffee
@@ -1,285 +0,0 @@
-###
-Copyright (c) 2002-2011 "Neo Technology,"
-Network Engine for Objects in Lund AB [http://neotechnology.com]
-
-This file is part of Neo4j.
-
-Neo4j is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-###
-
-define(
-  ['neo4j/webadmin/data/ItemUrlResolver'
-   'neo4j/webadmin/ui/LoadingSpinner'
-   'neo4j/webadmin/views/View'
-   'neo4j/webadmin/ui/Tooltip'
-   'neo4j/webadmin/security/HtmlEscaper'
-   'neo4j/webadmin/templates/databrowser/visualization'
-   'lib/raphael'
-   'lib/dracula.graffle'
-   'lib/dracula.graph'
-   'lib/backbone'],
-  (ItemUrlResolver, LoadingSpinner, View, Tooltip, HtmlEscaper, template) ->
-
-    GROUP_IDS = 0
-
-    class VisualizedView extends View
-
-      initialize : (options)->
-
-        @server = options.server
-
-        @urlResolver = new ItemUrlResolver(@server)
-        @htmlEscaper = new HtmlEscaper
-
-        @dataModel = options.dataModel
-
-        @tooltip = new Tooltip
-
-        @nodeMap = {}
-        @groupMap = {}
-
-      render : =>
-        $(@el).html(template())
-
-        @nodeMap = {}
-        @groupMap = {}
-
-        width = $(document).width() - 40;
-        height = $(document).height() - 120;
-
-        @g = new Graph()
-
-        switch @dataModel.get("type")
-          when "node"
-            node = @dataModel.getData().getItem()
-            @addNode(node)
-            @loadRelationships(node)
-          when "relationship"
-            @addRelationship(@dataModel.getData().getItem())
-          when "relationshipList"
-            @addRelationships(@dataModel.getData().getRawRelationships())
-          else
-            return this
-
-        @layout = new Graph.Layout.Spring(@g)
-        @renderer = new Graph.Renderer.Raphael('visualization', @g, width, height)
-
-        return this
-
-      redrawVisualization : =>
-        @layout.layout()
-        @renderer.draw()
-
-      hasNode : (nodeUrl) =>
-         if @nodeMap[nodeUrl]
-           return true
-         else
-           return false
-
-      addNode : (node) =>
-        @nodeMap[node.getSelf()] =
-          neoNode    : node
-          visualNode : @g.addNode(node.getSelf(), { node : node, url:node.getSelf(), explored:true, render:@nodeRenderer })
-
-      addUnexploredNode : (nodeUrl) =>
-        @nodeMap[nodeUrl] =
-          neoNode    : null
-          visualNode : @g.addNode(nodeUrl, { node : null, url:nodeUrl, explored:false, render:@unexploredNodeRenderer})
-
-      addRelationship : (rel) =>
-        @addRelationships([rel])
-
-      addAndGroupRelationships : (rels, node) =>
-        groups = {}
-        for rel in rels
-          if not groups[rel.getType()]
-            groups[rel.getType()] = { type:rel.getType(), size:0, relationships:[] }
-
-          groups[rel.getType()].size++
-          groups[rel.getType()].relationships.push(rel)
-
-        for type, group of groups
-
-          if group.size > 5
-            @addGroup(group, node)
-          else
-            @addRelationships(group.relationships)
-
-        @redrawVisualization()
-
-      addGroup : (group, node) =>
-
-        id = "group-" + GROUP_IDS++
-        @groupMap[id] =
-          group : group
-          visualNode : @g.addNode(id, { id:id, size:group.size, render:@groupRenderer })
-
-        @g.addEdge(node.getSelf(), id, { label : group.type })
-
-      addRelationships : (rels) =>
-
-        for rel in rels
-          if @hasNode(rel.getEndNodeUrl()) == false
-            @addUnexploredNode(rel.getEndNodeUrl())
-
-          if @hasNode(rel.getStartNodeUrl()) == false
-            @addUnexploredNode(rel.getStartNodeUrl())
-
-          @g.addEdge(rel.getStartNodeUrl(), rel.getEndNodeUrl(), { label : rel.getType(), directed : true })
-
-
-      nodeRenderer : (r, node) =>
-        circle = r.circle(0, 0, 10).attr({fill: "#ffffff", stroke: "#333333", "stroke-width": 2})
-
-        if node.node.hasProperty("name")
-          label = r.text(0, 0, node.node.getProperty("name"))
-        else
-          label = r.text(0, 0, @urlResolver.extractNodeId(node.url))
-
-        clickHandler = (ev) =>
-          @nodeClicked(node, circle)
-
-        mouseOverHandler = (ev) =>
-          @mouseOverNode(ev, node, circle)
-
-        mouseOutHandler = (ev) =>
-          @mouseLeavingNode(ev, node, circle)
-
-        circle.click(clickHandler)
-        label.click(clickHandler)
-        circle.hover(mouseOverHandler, mouseOutHandler)
-        label.hover(mouseOverHandler, mouseOutHandler)
-
-        shape = r.set().
-          push(circle).
-          push(label)
-        return shape
-
-      unexploredNodeRenderer : (r, node) =>
-        circle = r.circle(0, 0, 10).attr({fill: "#ffffff", stroke: "#dddddd", "stroke-width": 2})
-
-        label = r.text(0, 0, @urlResolver.extractNodeId(node.url))
-
-        clickHandler = (ev) =>
-          @nodeClicked(node, circle)
-
-        mouseOverHandler = (ev) =>
-          @mouseOverNode(ev, node, circle)
-
-        mouseOutHandler = (ev) =>
-          @mouseLeavingNode(ev, node, circle)
-
-        circle.click(clickHandler)
-        label.click(clickHandler)
-        circle.hover(mouseOverHandler, mouseOutHandler)
-        label.hover(mouseOverHandler, mouseOutHandler)
-
-        shape = r.set().
-          push(circle).
-          push(label)
-        return shape
-
-      groupRenderer : (r, group) =>
-        circle = r.circle(0, 0, 6).attr({fill: "#eeeeee", stroke: "#dddddd", "stroke-width": 2})
-
-        label = r.text(0, 0, group.size)
-
-        clickHandler = (ev) =>
-          @groupClicked(group, circle)
-        circle.click(clickHandler)
-        label.click(clickHandler)
-
-        shape = r.set().
-          push(circle).
-          push(label)
-        return shape
-
-      nodeClicked : (nodeMeta, circle) =>
-        nodeMeta = @nodeMap[nodeMeta.url].visualNode
-
-        if nodeMeta.explored == false
-          circle.attr({fill: "#ffffff", stroke: "#333333", "stroke-width": 2})
-          @server.node(nodeMeta.url).then (node) =>
-            nodeMeta.explored = true
-            nodeMeta.node = node
-            @addNode(node)
-            @loadRelationships(node)
-
-      mouseOverNode : (ev, nodeMeta, circle) =>
-        # XXX: As with most things in this class, this is a temp hack
-        if nodeMeta.explored
-          node = nodeMeta.node
-          propHtml = for key, val of node.getProperties()
-            key = @htmlEscaper.escape(key)
-            val = @htmlEscaper.escape JSON.stringify(val)
-            "
#{key}: #{val}
"
-
-          propHtml = propHtml.join("\n")
-          html = "
#{propHtml}
"
-        else
-          html = "
Unexplored node
"
-        @tooltip.show(html, [ev.clientX, ev.clientY])
-
-      mouseLeavingNode : (ev, nodeMeta, circle) =>
-        @tooltip.hide()
-
-      groupClicked : (groupMeta, circle) =>
-        group = @groupMap[groupMeta.id].group
-        visualNode = @groupMap[groupMeta.id].visualNode
-        @showLoader()
-
-        ungroup = ()=>
-          @removeVisualNode(visualNode)
-          @addExplodedGroup(group)
-          @redrawVisualization()
-          @hideLoader()
-
-        setTimeout( ungroup, 1 )
-
-      loadRelationships : (node) =>
-        @showLoader()
-        node.getRelationships().then (rels) =>
-          @addAndGroupRelationships(rels, node)
-          @hideLoader()
-
-      removeVisualNode : (visualNode, id) =>
-        visualNode.hide()
-        for edge in visualNode.edges
-          edge.connection.label.hide()
-          edge.hide()
-        @g.removeNode(visualNode.id)
-
-      showLoader : =>
-        @hideLoader()
-        @loader = new LoadingSpinner($(".workarea"))
-        @loader.show()
-
-      hideLoader : =>
-        if @loader?
-          @loader.destroy()
-
-      remove : =>
-        @dataModel.unbind("change", @render)
-        super()
-
-      detach : =>
-        @dataModel.unbind("change", @render)
-        super()
-
-      attach : (parent) =>
-        super(parent)
-        @dataModel.bind("change", @render)
-
-
-)


Create class MockedMethod and move related functions into it.

--- src/TinyMockJS.coffee
+++ src/TinyMockJS.coffee
@@ -31,6 +31,8 @@
 #
 # Expects
 #
+# Note: this is better thought of as a mixin; comment further
+#
 class Expects

   @expects: (method_name) ->
@@ -85,7 +87,7 @@
     for outer in [0.._expectations.length-2]
       for inner in [outer+1.._expectations.length-1]
         if _expectations[outer].equals( _expectations[inner] )
-          throw_duplicate_expectation("#{_expectations[outer]._method_name}(#{_expectations[outer]._args})")
+          _throw_duplicate_expectation("#{_expectations[outer]._method_name}(#{_expectations[outer]._args})")

   @verify_all_expectations: ->
     errors = _find_all_errors()
@@ -107,25 +109,29 @@

   _find_all_errors = ->
       ( expectation.find_errors() for expectation in _expectations ).join("")
-
-
-#
-# mocked_method
-#
-build_mocked_method = (method_name) ->
-  (args...) ->
-    expectation = AllExpectations.find_expectation(@, method_name, args...)
-    throw_unknown_expectation("#{method_name}(#{args})") unless expectation?
-    AllExpectations.check_for_duplicate_expectations()
-    expectation._called = yes
-    throw expectation._throws if expectation._throws?
-    expectation._returns
-
-throw_unknown_expectation = (expectation) ->
-  throw new Error( "#{expectation} does not match any expectations" )
-
-throw_duplicate_expectation = (expectation) ->
-  throw new Error( "#{expectation} is a duplicate expectation" )
+
+  _throw_duplicate_expectation = (expectation) ->
+    throw new Error( "#{expectation} is a duplicate expectation" )
+
+
+#
+# MockedMethod
+#
+class MockedMethod
+
+  @build_mocked_method: (method_name) ->
+    (args...) ->
+      expectation = AllExpectations.find_expectation(@, method_name, args...)
+      _throw_unknown_expectation("#{method_name}(#{args})") unless expectation?
+      AllExpectations.check_for_duplicate_expectations()
+      expectation._called = yes
+      throw expectation._throws if expectation._throws?
+      expectation._returns
+
+  # private
+
+  _throw_unknown_expectation = (expectation) ->
+    throw new Error( "#{expectation} does not match any expectations" )

 #
@@ -221,7 +227,7 @@
   _install_mock_method: ->
     object = if is_class(@_object) then @_object.prototype else @_object
     original_method = object[ @_method_name ]
-    object[ @_method_name ] = build_mocked_method(@_method_name)
+    object[ @_method_name ] = MockedMethod.build_mocked_method(@_method_name)
     if original_method?
       @uninstall_mocked_method = -> object[ @_method_name ] = original_method
     else


Unbanning now adds louds back in.

Unbanning now adds louds back in.

--- scripts/loud.coffee
+++ scripts/loud.coffee
@@ -42,16 +42,20 @@
       else
         res.send "Couldn't find that loud."

+    addLoud = (data, brain) ->
+      robot.brain.get(brain).push(data) if data not in robot.brain.get(brain)
+
     switch action
       when 'delete'
         deleteLoud(data, 'louds')

       when 'ban'
-        robot.brain.get('louds_banned').push(data) if data not in robot.brain.get('louds_banned')
+        addLoud(data, 'louds_banned')
         deleteLoud(data, 'louds') if data in robot.brain.get('louds')

       when 'unban'
         deleteLoud(data, 'louds_banned') if data in robot.brain.get('louds_banned')
+        addLoud(data, 'louds')

       when 'nuke'
         if process.env.DEBUG != 'true'


patch to deal with empty string resolving to false

--- src/giphy.coffee
+++ src/giphy.coffee
@@ -221,7 +221,9 @@
       msg.send message

   respond: (msg) ->
-    if msg and msg.match and msg.match[1]
+    # we must check the match.length >= 2 here because just checking the value
+    # match[2] could give us a false negative since empty string resolves to false
+    if msg and msg.match and msg.match.length >= 2
       state = @createState msg

       @getEndpoint state


querying for elements just inside of the surveyApp's root element (slightly faster)

--- jsapp/xlform/src/view.surveyApp.coffee
+++ jsapp/xlform/src/view.surveyApp.coffee
@@ -378,14 +378,14 @@
       @

     shrinkAllGroups: ->
-      $('.survey__row--group:not(.group--shrunk)').each (i, el) ->
+      @$('.survey__row--group:not(.group--shrunk)').each (i, el) ->
         if !$(el).hasClass('group--shrunk')
           $(el).find('.group__caret').click()

     expandAllGroups: ->
       depth = 0
-      while $('.survey__row--group.group--shrunk').length > 0
-        $('.survey__row--group.group--shrunk').each (i, el) ->
+      while @$('.survey__row--group.group--shrunk').length > 0
+        @$('.survey__row--group.group--shrunk').each (i, el) ->
           $(el).find('.group__caret').click()
         if depth++ > 10
           break


sitemap: remove sitemap endpoint handler

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -499,22 +499,6 @@

     res.send "verified"

-app.get "/sitemap.xml", (req, res)->
-  getSiteMap "/sitemap.xml", req, res
-
-app.get "/sitemap/:sitemapName", (req, res)->
-  getSiteMap "/sitemap/#{req.params.sitemapName}", req, res
-
-getSiteMap = (name, req, res)->
-  {
-    bareRequest
-  } = require "../../../workers/social/lib/social/models/socialapi/helper"
-
-  bareRequest "getSiteMap", {name:name}, (err, result)->
-    res.setHeader "Content-Type", "text/xml"
-    res.send result
-    res.end
-
 app.get "/-/presence/:service", (req, res) ->
   # if services[service] and services[service].count > 0
   res.status(200).end()


Fix campaign index update from campaign editor. (#180)

--- app/views/editor/campaign/CampaignEditorView.ozar.coffee
+++ app/views/editor/campaign/CampaignEditorView.ozar.coffee
@@ -147,8 +147,6 @@
       # Save campaign to level if its of type 'course' so 'Back to unit map' knows where to return.
       # (Not if it's a defaulted, typeless campaign like game-dev-hoc or auditions.)
       campaignLevel.campaign = @campaign.get 'slug' if @campaign.get('type') is 'course'
-      # Save campaign index to level if it's a course campaign, since we show linear level order numbers for course levels.
-      campaignLevel.campaignIndex = (@levels.models.length - levelIndex - 1) if @campaign.get('type', true) is 'course'
       campaignLevels[levelOriginal] = campaignLevel

     @campaign.set('levels', campaignLevels)


:new: Implement hide in panel

:new: Implement hide in panel

--- lib/views/panel.coffee
+++ lib/views/panel.coffee
@@ -7,6 +7,9 @@
     @decorations.forEach (decoration) ->
       try decoration.destroy()
     @decorations = []
+  hide: ->
+    @innerHTML = ''
+    @linter.views.panelWorkspace.hide() if @linter.views.panelWorkspace.isVisible()
   render: (messages)->
     @removeDecorations()
     @innerHTML = ''


Don't copy themes to .atom dir

Don't copy themes to .atom dir

--- src/app/config.coffee
+++ src/app/config.coffee
@@ -60,13 +60,6 @@
       queue.push({sourcePath, destinationPath})
     fsUtils.traverseTree(templateConfigDirPath, onConfigDirFile, (path) -> true)

-    configThemeDirPath = path.join(@configDirPath, 'themes')
-    onThemeDirFile = (sourcePath) ->
-      relativePath = sourcePath.substring(bundledThemesDirPath.length + 1)
-      destinationPath = path.join(configThemeDirPath, relativePath)
-      queue.push({sourcePath, destinationPath})
-    fsUtils.traverseTree(bundledThemesDirPath, onThemeDirFile, (path) -> true)
-
   load: ->
     @initializeConfigDirectory()
     @loadUserConfig()


Revert "Replace @getLastVisibleScreenRow with @getFirstVisibleScreenRow"

This reverts commit 89ac1f50af60850d54e6f322b989e6d49efff40f.

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -450,7 +450,7 @@

       return if oldScreenRange.start.row > @lastRenderedScreenRow

-      maxEndRow = Math.max(@getFirstVisibleScreenRow() + @lineOverdraw, @lastRenderedScreenRow)
+      maxEndRow = Math.max(@getLastVisibleScreenRow() + @lineOverdraw, @lastRenderedScreenRow)
       @gutter.renderLineNumbers(@firstRenderedScreenRow, maxEndRow) if e.lineNumbersChanged

       newScreenRange = newScreenRange.copy()


FIX: Avoid use of campus function in filter if not needed

--- src/app/common/filters/filters.coffee
+++ src/app/common/filters/filters.coffee
@@ -186,7 +186,7 @@
   (tutorials, project) ->
     return tutorials unless project?
     _.filter tutorials, (tute) ->
-      tute.campus == project.campus()
+      tute.campus_id == project.campus_id
 )

 .filter('groupsInTutorials', ->


Only use event.which when key identifier is non-ASCII

Previously event.which was used when it was less than the key identifier
which broke fn-delete since that generates a valid key identifier but a
lower event.which value that does not translate to 'delete' using the char
code converter.

Close #611

--- src/app/keymap.coffee
+++ src/app/keymap.coffee
@@ -150,7 +150,7 @@
     if event.originalEvent.keyIdentifier.indexOf('U+') == 0
       hexCharCode = event.originalEvent.keyIdentifier[2..]
       charCode = parseInt(hexCharCode, 16)
-      charCode = Math.min(event.which, charCode) if event.which?
+      charCode = event.which if !@isAscii(charCode) and @isAscii(event.which)
       key = @keyFromCharCode(charCode)
     else
       key = event.originalEvent.keyIdentifier.toLowerCase()
@@ -171,6 +171,9 @@

     [modifiers..., key].join('-')

+  isAscii: (charCode) ->
+    0 <= charCode <= 127
+
   keyFromCharCode: (charCode) ->
     switch charCode
       when 8 then 'backspace'


Skipping docs for language-inappropriate entries in spell palette.

--- app/views/play/level/tome/spell_palette_view.coffee
+++ app/views/play/level/tome/spell_palette_view.coffee
@@ -57,6 +57,7 @@
     allDocs = {}
     for lc in lcs
       for doc in (lc.get('propertyDocumentation') ? [])
+        continue if doc.codeLanguages and not (@options.language in doc.codeLanguages)
         allDocs['__' + doc.name] ?= []
         allDocs['__' + doc.name].push doc
         if doc.type is 'snippet' then doc.owner = 'snippets'
@@ -83,7 +84,6 @@
         'this': 'apiProperties'
     count = 0
     propGroups = {}
-    console.log 'thang', @thang
     for owner, storage of propStorage
       props = _.reject @thang[storage] ? [], (prop) -> prop[0] is '_'  # no private properties
       added = propGroups[owner] = _.sortBy(props).slice()


Set `options.port` if it has a falsy value

This ensures the `createServer` function gets the correct port.

--- src/index.coffee
+++ src/index.coffee
@@ -150,10 +150,11 @@
 #

 getPort = (options) ->
-  port = options.port or parseInt process.env.PORT
-  return port if port
-  return 443 if options.secure
-  return 8000
+  unless port = options.port
+    unless port = parseInt process.env.PORT
+      port = if options.secure then 443 else 8000
+    options.port = port
+  return port

 parseQuery = (query) ->
   parsed = {}


fix: use new polyfill Promise::asCallback

--- src/index.coffee
+++ src/index.coffee
@@ -6,11 +6,8 @@
 defineProperty  = require('util-ex/lib/defineProperty')
 # Promise         = require('any-promise')
 any             = require('promise-sequence/lib/any')
-{callbackify}   = require('./callbackify')
-
-if !Promise::asCallback
-  Promise::asCallback = (done) ->
-    callbackify(this, done)
+{ promisify }   = require('./promisify')
+require('./polyfill-asCallback')

 getKeys     = Object.keys

@@ -61,7 +58,6 @@
     aPath ?= @path
     aOptions ?= @options
     Config.load aPath, aOptions, done
-    @

   loadSync: (aPath, aOptions)->
     if isObject(aPath)
@@ -165,17 +161,15 @@
       if fs.readFileSync && !fs.readFile
         fs.readFile = fs.readFileSync
       else if fs.readFile
-        if !fs.readFile.then
-          fs.readFile = ((aReadFile) ->
-            (aPath, aOptions) ->
-              new Promise (resolve, reject)->
-                aReadFile.call fs, aPath, aOptions, (err, content) ->
-                  if err then return reject(err)
-                  resolve(content)
-                  return
-          )(fs.readFile)
+        fs.readFilep = promisify fs.readFile, fs
+        # fs.readFilep = (aPath, aOptions) ->
+        #   new Promise (resolve, reject)->
+        #     fs.readFile aPath, aOptions, (err, content) ->
+        #       if err then return reject(err)
+        #       resolve(content)
+        #       return
       Config::path = path = aFileSystem.path if aFileSystem.path
-      Config::readFile = readFile = fs.readFile.bind(fs)
+      Config::readFile = readFile = fs.readFilep
       true

 Config.setFileSystem require('fs')


* [Value] rename toObjectInfo to toObjectWithType + [Value] add the withType option to toObject(options)

--- src/index.coffee
+++ src/index.coffee
@@ -22,6 +22,7 @@
       result = result.substring(i+1, result.length-1) if i >= 0
     else
       result = typeof aValue
+      result =result.charAt(0).toUpperCase() + result.substring(1)
     result
   constructor: (aValue, aType, aOptions)->
     if not (aType instanceof Type)
@@ -58,15 +59,18 @@
     @$type.createValue aValue, aOptions
   clone: (aOptions) ->
     @create @valueOf(), aOptions
-  toString: (aOptions)->
-    String(@valueOf())
+  toString: -> String(@valueOf())
   valueOf: ->@value
   _toObject: (aOptions)->@valueOf()
   toObject: (aOptions)->
+    if aOptions and aOptions.withType
+      delete aOptions.withType
+      result = @toObjectWithType(aOptions)
+    else
+      result = @_toObject(aOptions)
+    result
+  toObjectWithType: (aOptions)->
     result = @_toObject(aOptions)
-    result
-  toObjectInfo: (aOptions)->
-    result = @toObject(aOptions)
     aOptions = {} unless aOptions
     aOptions.value = result
     @$type.toObject(aOptions)
@@ -83,7 +87,11 @@
   toJSON: (aOptions)->
     result = @toObject(aOptions)
     vType  = @$type
-    result = vType.valueToString result if vType.valueToString
+    if vType.valueToString
+      if result.hasOwnProperty 'value'
+        result.value = vType.valueToString result.value
+      else
+        result = vType.valueToString result
     result
   toJson: (aOptions)->
     result = @toJSON(aOptions)


Fix replacement; Re: GH-3

Fix replacement; Re: GH-3

--- src/index.coffee
+++ src/index.coffee
@@ -162,8 +162,8 @@
     uniquifyID = (id) -> "#{ id }___#{ svgID }"
     svgText.replace idPattern, (m, p1, p2, p3, p4, p5) ->
       if p2 then "#{ p1 }=\"#{ uniquifyID p2 }\""
-      else if p3 then "=\"url(##{ uniquifyID p3 })\""
-      else if p5 then "#{ p4 }=\"##{ uniquifyID p4 }\""
+      else if p4 then "#{ p3 }=\"##{ uniquifyID p4 }\""
+      else if p5 then "=\"url(##{ uniquifyID p5 })\""

 # Gets a unique ID for the given component, for use in the id attribute.


Now those checks can be simpler, too.

Now those checks can be simpler, too.

--- src/index.coffee
+++ src/index.coffee
@@ -47,8 +47,11 @@
         return

     for challenger in challengers
-      unless challenger.hp() > 0
+      unless challenger.isAlive()
         msg.reply "#{challenger.displayName()} is dead!"
+        return
+      if challenger.isInCombat()
+        msg.reply "#{challenger.displayName()} is already fighting!"
         return

     m = theCircle.startMatch(challengers)


Add missing change events

Add missing change events

--- src/index.coffee
+++ src/index.coffee
@@ -199,9 +199,11 @@
   if /\d\s$/.test(value)
     e.preventDefault()
     QJ.val(target, value.replace(/\d\s$/, ''))
+    QJ.trigger(target, 'change')
   else if /\s\d?$/.test(value)
     e.preventDefault()
     QJ.val(target, value.replace(/\s\d?$/, ''))
+    QJ.trigger(target, 'change')

 # Format Expiry

@@ -216,10 +218,12 @@
   if /^\d$/.test(val) and val not in ['0', '1']
     e.preventDefault()
     QJ.val(target, "0#{val} / ")
+    QJ.trigger(target, 'change')

   else if /^\d\d$/.test(val)
     e.preventDefault()
     QJ.val(target, "#{val} / ")
+    QJ.trigger(target, 'change')

 formatMonthExpiry = (e) ->
   digit = String.fromCharCode(e.which)
@@ -231,10 +235,12 @@
   if /^\d$/.test(val) and val not in ['0', '1']
     e.preventDefault()
     QJ.val(target, "0#{val}")
+    QJ.trigger(target, 'change')

   else if /^\d\d$/.test(val)
     e.preventDefault()
     QJ.val(target, "#{val}")
+    QJ.trigger(target, 'change')

 formatForwardExpiry = (e) ->
   digit = String.fromCharCode(e.which)
@@ -245,6 +251,7 @@

   if /^\d\d$/.test(val)
     QJ.val(target, "#{val} / ")
+    QJ.trigger(target, 'change')

 formatForwardSlash = (e) ->
   slash = String.fromCharCode(e.which)
@@ -255,6 +262,7 @@

   if /^\d$/.test(val) and val isnt '0'
     QJ.val(target, "0#{val} / ")
+    QJ.trigger(target, 'change')

 formatBackExpiry = (e) ->
   # If shift+backspace is pressed
@@ -273,9 +281,11 @@
   if /\d(\s|\/)+$/.test(value)
     e.preventDefault()
     QJ.val(target, value.replace(/\d(\s|\/)*$/, ''))
+    QJ.trigger(target, 'change')
   else if /\s\/\s?\d?$/.test(value)
     e.preventDefault()
     QJ.val(target, value.replace(/\s\/\s?\d?$/, ''))
+    QJ.trigger(target, 'change')

 #  Restrictions


* [bug] the bluebird@3.x changes the promisify() api.

--- src/index.coffee
+++ src/index.coffee
@@ -133,7 +133,7 @@
     if aFileSystem and aFileSystem.readFile
       Config::fs = fs = aFileSystem
       Config::path = path = aFileSystem.path if aFileSystem.path
-      Config::readFile = readFile = Promise.promisify(fs.readFile, fs)
+      Config::readFile = readFile = Promise.promisify(fs.readFile, context:fs)
       true

 Config.setFileSystem require('fs')


Auto fill path and name attributes for components

--- src/index.coffee
+++ src/index.coffee
@@ -93,7 +93,10 @@

     for name in options.components or []
-        app.component require name
+        comp = require name
+        comp::name ?= name
+        comp::view ?= require.resolve name, paths: globalPaths
+        app.component comp

     for moduleInfo in options.modules
         if _.isString moduleInfo


Include palm position in hand data

Include palm position in hand data

--- src/index.coffee
+++ src/index.coffee
@@ -1,29 +1,36 @@
 {EventEmitter} = require 'events'
 # Local gesture and actions configs
-config = require('.././config.json') # TODO: Use yaml
+config = require '.././config.json'
+Leap = require 'leapjs'

-# Input controller's job is to recieve "leapgim frames" from the frame controller.
+# Input controller's job is to recieve "leapgim frames" from the frame
+# controller.
 class InputController
     constructor: ->
         # Keyboard and mouse control
         @robot = require("robotjs")

-    parseGestures: (frameModel) =>
-        extendedFingers = frameModel.extendedFingers
-        console.log "Extended fingers: ", extendedFingers
+    parseGestures: (handModel) =>
+        console.log "handModel: ", handModel

-# Frame controller recieves leap frame data from leapd and parses it into a structured format we'll use later to configure gestures with
+
+# Frame controller recieves leap frame data from leapd and parses it into a
+# structured format we'll use later to configure gestures with
 class FrameController extends EventEmitter
     constructor: ->
-        # Frame model - just the extended fingers of an arbitary hand for now
+        # Hand model - just the extended fingers of an arbitary hand for now
         @handModel =
-            extendedFingers =
+            extendedFingers :
                 thumb : 0
                 indexFinger : 0
                 middleFinger : 0
                 ringerFinger : 0
                 pinky : 0
-            # Palm direction ?
+            position : {
+                x : 0
+                y : 0
+                z : 0
+            }

     processFrame: (frame) =>
         #console.info "frame: ", frame
@@ -36,24 +43,57 @@
             return

         firstHand = frame.hands[0]
-        #console.log "First hand: ", firstHand
+
+        palmPosition = @relative3DPosition(frame, firstHand.palmPosition)

         # Update frame model
-        @handModel.extendedFingers =
-            @extendedFingers =
+        @handModel =
+            extendedFingers:
                 thumb : firstHand.thumb.extended
                 indexFinger : firstHand.indexFinger.extended
                 middleFinger : firstHand.middleFinger.extended
                 ringerFinger : firstHand.ringFinger.extended
                 pinky : firstHand.pinky.extended
+            position: palmPosition

         console.log "Frame succesfully processed"
         @emit 'update', @handModel

+    ###
+    # Produce x and y coordinates for a leap pointable.
+    ###
+
+    relative3DPosition: (frame, leapPoint) ->
+        iBox = frame.interactionBox
+        normalizedPoint = iBox.normalizePoint(leapPoint, false)
+
+        # Translate coordinates so that origin is in the top left corner
+        x = normalizedPoint[0]
+        y = 1 - (normalizedPoint[1])
+        z = normalizedPoint[2]
+
+        # Clamp
+        if x < 0
+            x = 0
+        if x > 1
+            x = 1
+        if y < 0
+            y = 0
+        if y > 1
+            y = 1
+        if z < -1
+            z = -1
+        if z > 1
+            z = 1
+        {
+            x: x
+            y: y
+            z: z
+        }
+
+
 # Init Leap Motion
-@Leap = require("leapjs")
-
-loopController = new @Leap.Controller (
+loopController = new Leap.Controller (
     inBrowser:              false,
     enableGestures:         true,
     frameEventName:         'deviceFrame',
@@ -65,8 +105,6 @@
 inputController = new InputController

 frameController.on('update', inputController.parseGestures)
-frameController.on 'update', ->
-    console.log "Foo"
 loopController.on('frame', frameController.processFrame)

 # Start the show


Removed a debug line from email.coffee

Removed a debug line from email.coffee

--- examples/email/html/email.coffee
+++ examples/email/html/email.coffee
@@ -1,4 +1,3 @@
-console.log "EMAIL.JS IS GETTING LOADED"
 ImapConnection = require("imap").ImapConnection

 imap = new ImapConnection


Use correct attribute

Use correct attribute

--- core/app/backbone/models/session.coffee
+++ core/app/backbone/models/session.coffee
@@ -2,7 +2,7 @@
   url: -> '/api/beta/session'

   initialize: (attributes, options) ->
-    @_user = new User attributes
+    @_user = new User attributes?.user

     @on 'change', @_onChange, @


fixing the problem

fixing the problem

--- lib/operators/general-operators.coffee
+++ lib/operators/general-operators.coffee
@@ -94,8 +94,9 @@
   execute: (count) ->
     if _.contains(@motion.select(count, @selectOptions), true)
       @setTextRegister(@register, @editor.getSelectedText())
-      for selection in @editor.getSelections()
-        selection.deleteSelectedText()
+      @editor.transact =>
+        for selection in @editor.getSelections()
+          selection.deleteSelectedText()
       for cursor in @editor.getCursors()
         if @motion.isLinewise?()
           cursor.skipLeadingWhitespace()


Move and rename Expectation@verify to verify_all_expectations.

--- src/TinyMockJS.coffee
+++ src/TinyMockJS.coffee
@@ -4,13 +4,12 @@
 clear_all_expectations = ->
   all_expectations.length = 0

+verify_all_expectations = ->
+  errors = _build_errors(@)
+  throw new Error(errors) unless errors == ""
+

 class Expectation
-
-  #
-  @verify: ->
-    errors = _build_errors(@)
-    throw new Error(errors) unless errors == ""

   constructor: (object, method_name) ->
     @_object = object
@@ -199,7 +198,7 @@
     Object.prototype.expects = expects
     mocks = ( new Object() for i in [1..5] )
     fn.apply(undefined, mocks)
-    Expectation.verify()
+    verify_all_expectations()
   finally
     delete Object.prototype.expects
     for expectation in all_expectations     # TODO: do this in reverse


Ensure message is a string and not a number Numbers cause problems with some adapters (like slack) https://github.com/hubot-scripts/hubot-calculator/issues/1

--- src/calculator.coffee
+++ src/calculator.coffee
@@ -19,7 +19,8 @@
 module.exports = (robot) ->
   robot.respond /(calc|calculate|calculator|convert|math|maths)( me)? (.*)/i, (msg) ->
     try
-      msg.send mathjs.eval msg.match[3]
+      result = mathjs.eval msg.match[3]
+      msg.send "#{result}"
     catch error
       msg.send error.message || 'Could not compute.'


IT'S A GAME!

IT'S A GAME!

--- src/game.coffee
+++ src/game.coffee
@@ -75,17 +75,17 @@
 class Enemy extends Entity

   constructor: (@game)->
-    super 0, 0, 100, 100, 5, @game
+    super 0, 0, 100, 100, 10, @game
     @y =  @height
-    @x = -@width
+    @x =  @game.width + @width
     @move @x, @y

   render: ->
     @game.ctx.drawImage @game.assets.enemyShip, @x, @y, @width, @height

   update: ->
-    @move(@x + @speed)
-    @move(-@width) if @x > @game.width+@width
+    @move(@x - @speed)
+    @move(@game.width + @width) if @x < -@width

   move: (x)->
     super x, @y
@@ -129,25 +129,26 @@
     @ctx              = @canvas.getContext '2d'

     @projectiles      = []
+    @enemies          = [new Enemy @]

     $('body').append @canvas

     @controller = new Controller @
     @player     = new Player @
-    @enemy      = new Enemy @

   timestamp: ->
     window.performance.now()

   update: ->
     @player.update()
-    @enemy.update()
+    @updateEnemies()
     @updateProjectiles()
+    @handleCollisions()

   render: ->
     @renderBackground()
     @player.render()
-    @enemy.render()
+    @renderEnemies()
     @renderProjectiles()

   frame: ->
@@ -174,6 +175,44 @@
     for projectile of @projectiles
       @projectiles[projectile].render()

+  updateEnemies: ->
+    for enemy of @enemies
+      @enemies[enemy].update()
+
+  renderEnemies: ->
+    for enemy of @enemies
+      @enemies[enemy].render()
+
+  handleCollisions: ->
+    for projectile of @projectiles
+      for enemy of @enemies
+        if @isCollision @projectiles[projectile], @enemies[enemy]
+          @killEnemy @enemies[enemy]
+          @addEnemy()
+
+  killEnemy: (enemy)->
+    @enemies.splice @enemies.indexOf(enemy), 1
+
+  addEnemy: ->
+    @enemies = [new Enemy @]
+
+  isCollision: (entity1, entity2)->
+    points = []
+    points.push x: entity1.x,                y: entity1.y
+    points.push x: entity1.x+entity1.width,  y: entity1.y
+    points.push x: entity1.x,                y: entity1.y+entity1.height
+    points.push x: entity1.x+entity1.width,  y: entity1.y+entity1.height
+
+    for point of points
+      return true if @isPointInEntity points[point], entity2
+    false
+
+  isPointInEntity: (point, entity)->
+    (
+      point.x > entity.x && point.x < (entity.x+entity.width) &&
+      point.y > entity.y && point.y < (entity.y+entity.height)
+    )
+
   addProjectile: (projectile)->
     @projectiles.push projectile


Cleaning + verbatim

Cleaning + verbatim

--- grammars/latex.cson
+++ grammars/latex.cson
@@ -88,6 +88,9 @@
         include: '#makeatsomething'
       }
       {
+        include: '#verbatim'
+      }
+      {
         include: '#genericMacro'
       }
     ]
@@ -222,22 +225,20 @@
   escapedCharacter: {
     comment: 'Finds escaped characters like \$ and \%. Uses this regex so unique capture groups can be used.'
     name: 'constant.character.latex'
-    match: '(\\\\)(\\&|\\%|\\$|\\#|\\_|\\{|\\}|(textascii(tilde|circum)|textbackslash)(?=[^a-zA-Z@]))'
+    match: '\\\\(\\&|\\%|\\$|\\#|\\_|\\{|\\}|(textascii(tilde|circum)|textbackslash)(?=[^a-zA-Z@]))'
   }
   accents: {
     comment: 'For standard latex accent macros such as \^ or \`'
     name: 'constant.character.accent.latex'
-    match: '''(\\\\)([\\`\\'\\^\\"\\~\\=\\.](\\w|\\{\\w?\\})|[Hcklbdruv]\\{\\w?\\})'''
+    match: '''\\\\([\\`\\'\\^\\"\\~\\=\\.](\\w|\\{\\w?\\})|[Hcklbdruv]\\{\\w?\\})'''
   }
   hypersetup: {
     comment: 'Recongnises hypersetup environment. Can be extended to be a general key-option scoper, but this (potentially) allows custom colours for known keys.'
     name: 'meta.preamble.latex'
-    begin: '(\\\\)hypersetup(?=[^a-zA-Z@])'
+    begin: '\\\\hypersetup(?=[^a-zA-Z@])'
     beginCaptures:
       0:
         name: 'keyword.control.preamble.hypersetup.latex'
-      1:
-        name: 'punctuation.definition.function.latex'
     end: '(?<=\\})|(^\\s*$)'
     endCaptures:
       1:
@@ -267,12 +268,10 @@
   usepackage: {
     comment: 'Package finder. Similar to documentclass, but allows custom colours. A macro name is only valid with letters (exceptions apply--eg. makeatletter). This checks to see if the name is purely "usepackage" and the following character is not a legal part of the macro name.'
     name: 'meta.preamble.latex'
-    begin: '(\\\\)usepackage(?=[^a-zA-Z@])'
+    begin: '\\\\usepackage(?=[^a-zA-Z@])'
     beginCaptures:
       0:
         name: 'keyword.control.preamble.latex'
-      1:
-        name: 'punctuation.definition.function.latex'
     end: '(?<=\\})|(^\\s*$)'
     endCaptures:
       1:
@@ -297,12 +296,10 @@
   sectioning: {
     comment: 'Covers all sectioning commands such as section, subsection, subparagraph, etc.'
     name: 'support.function.section.latex'
-    begin: '(\\\\)((sub){0,2}section|(sub){0,1}paragraph|part|chapter)(\\*)?(?=[^a-zA-Z@])'
+    begin: '\\\\((sub){0,2}section|(sub){0,1}paragraph|part|chapter)(\\*)?(?=[^a-zA-Z@])'
     beginCaptures:
       0:
         name: 'keyword.control.section.latex'
-      1:
-        name: 'punctuation.definition.function.latex'
     end: '(?<=\\})|(^\\s*$)'
     endCaptures:
       1:
@@ -351,28 +348,26 @@
     match: '\\\\[a-zA-Z]+'
   }
   magicComment: {
-    comment: 'Magic commands work in first 20 lines. Can be improved to change color depending if the command matches a known string'
+    comment: 'Can be improved to change color depending if the command matches a known string'
     name: 'keyword.control.magic.latex'
-    match: '''(?x)  # Allow comments like this
-           ((        # Capture groups 1 & 2
-             (%)     # Capture percent sign
-             (\\ !(?>TEX|BIB)\\ )  # Exact match required
-             (\\S+)    # A word (this is required as far as I know)
-             )       # End capture group 2
-             (\\ = \\ )  # Match = with space (required)
-           )         # End capture group 1
-           .*$       # To end; some options have spaces
-         '''
+    match: '''(?x)   # Allow comments like this
+     ((        # Capture groups 1 & 2
+       (%)     # Capture percent sign
+       (\\ !(?>TEX|BIB)\\ )  # Exact match required
+       (\\S+)    # A word (this is required as far as I know)
+       )       # End capture group 2
+       (\\ = \\ )  # Match = with space (required)
+     )         # End capture group 1
+     .*$       # To end; some options have spaces
+   '''
   }
   documentclass: {
     comment: 'Documentclass finder.'
     name: 'meta.preamble.latex'
-    begin: '(\\\\)documentclass(?=[^a-zA-Z@])'
+    begin: '\\\\documentclass(?=[^a-zA-Z@])'
     beginCaptures:
       0:
         name: 'keyword.control.preamble.latex'
-      1:
-        name: 'punctuation.definition.function.latex'
     end: '(?<=\\})|(^\\s*$)'
     endCaptures:
       1:
@@ -558,30 +553,26 @@
   }
   genericEnvironment: {
     comment: 'Matches environments not recognised by other rules. Less lenient with random formatting here, it is expected to all be on one line.'
-    contentName: 'support.environment.latex'
-    begin: '(\\\\)(begin)(\\{)([\\w \\*]*)(\\})'
-    beginCaptures:
-      1:
-        name: 'punctuation.definition.function.latex'
-      2:
+    contentName: 'meta.environment.latex'
+    begin: '\\\\begin(\\{)([\\w \\*]*)(\\})'
+    beginCaptures:
+      0:
         name: 'support.function.environment.begin.latex'
-      3:
-        name: 'punctuation.definition.argument.begin.latex'
-      4:
+      1:
+        name: 'punctuation.definition.argument.begin.latex'
+      2:
         name: 'variable.parameter.argument.latex'
-      5:
-        name: 'punctuation.definition.argument.end.latex'
-    end: '(\\\\)(end)(\\{)(\\4)(\\})'
-    endCaptures:
-      1:
-        name: 'punctuation.definition.function.latex'
-      2:
+      3:
+        name: 'punctuation.definition.argument.end.latex'
+    end: '\\\\end(\\{)(\\2)(\\})'
+    endCaptures:
+      0:
         name: 'support.function.environment.end.latex'
-      3:
-        name: 'punctuation.definition.argument.begin.latex'
-      4:
+      1:
+        name: 'punctuation.definition.argument.begin.latex'
+      2:
         name: 'variable.parameter.argument.latex'
-      5:
+      3:
         name: 'punctuation.definition.argument.end.latex'
     patterns: [
       {
@@ -591,29 +582,25 @@
   }
   documentEnvironment: {
     comment: 'To stop the entire contents of the document environment being labelled. Its useless anyway, as I split up my work and just include it into the main file.'
-    begin: '(\\\\)(begin)(\\{)(document)(\\})'
-    beginCaptures:
-      1:
-        name: 'punctuation.definition.function.latex'
-      2:
+    begin: '\\\\begin(\\{)(document)(\\})'
+    beginCaptures:
+      0:
         name: 'support.function.environment.begin.latex'
-      3:
-        name: 'punctuation.definition.argument.begin.latex'
-      4:
+      1:
+        name: 'punctuation.definition.argument.begin.latex'
+      2:
         name: 'variable.parameter.argument.latex'
-      5:
-        name: 'punctuation.definition.argument.end.latex'
-    end: '(\\\\)(end)(\\{)(document)(\\})'
-    endCaptures:
-      1:
-        name: 'punctuation.definition.function.latex'
-      2:
+      3:
+        name: 'punctuation.definition.argument.end.latex'
+    end: '\\\\end(\\{)(document)(\\})'
+    endCaptures:
+      0:
         name: 'support.function.environment.end.latex'
-      3:
-        name: 'punctuation.definition.argument.begin.latex'
-      4:
+      1:
+        name: 'punctuation.definition.argument.begin.latex'
+      2:
         name: 'variable.parameter.argument.latex'
-      5:
+      3:
         name: 'punctuation.definition.argument.end.latex'
     patterns: [
       {
@@ -623,36 +610,32 @@
   }
   figureEnvironment: {
     comment: 'Matches the common figure environment'
-    contentName: 'support.environment.figure.latex'
-    begin: '(\\\\)(begin)(\\{)(figure)(\\})((\\[)(\\w*)(\\]))?'
-    beginCaptures:
-      1:
-        name: 'punctuation.definition.function.latex'
-      2:
+    contentName: 'meta.environment.figure.latex'
+    begin: '\\\\begin(\\{)(figure)(\\})((\\[)(\\w*)(\\]))?'
+    beginCaptures:
+      0:
         name: 'support.function.environment.begin.latex'
-      3:
-        name: 'punctuation.definition.argument.begin.latex'
-      4:
+      1:
+        name: 'punctuation.definition.argument.begin.latex'
+      2:
         name: 'variable.parameter.argument.latex'
+      3:
+        name: 'punctuation.definition.argument.end.latex'
       5:
-        name: 'punctuation.definition.argument.end.latex'
+        name: 'punctuation.definition.optional.begin.latex'
+      6:
+        name: 'variable.parameter.optional.latex'
       7:
-        name: 'punctuation.definition.optional.begin.latex'
-      8:
-        name: 'variable.parameter.optional.latex'
-      9:
         name: 'punctuation.definition.optional.end.latex'
-    end: '(\\\\)(end)(\\{)(\\4)(\\})'
-    endCaptures:
-      1:
-        name: 'punctuation.definition.function.latex'
-      2:
+    end: '\\\\end(\\{)(\\2)(\\})'
+    endCaptures:
+      0:
         name: 'support.function.environment.end.latex'
-      3:
-        name: 'punctuation.definition.argument.begin.latex'
-      4:
+      1:
+        name: 'punctuation.definition.argument.begin.latex'
+      2:
         name: 'variable.parameter.argument.latex'
-      5:
+      3:
         name: 'punctuation.definition.argument.end.latex'
     patterns: [
       {
@@ -670,30 +653,26 @@
   }
   alignMathEnvironment: {
     comment: 'Matches align environment'
-    contentName: 'support.environment.math.align.latex'
-    begin: '(\\\\)(begin)(\\{)(align(\\*)?)(\\})'
-    beginCaptures:
-      1:
-        name: 'punctuation.definition.function.latex'
-      2:
+    contentName: 'meta.environment.math.align.latex'
+    begin: '\\\\begin(\\{)(align(\\*)?)(\\})'
+    beginCaptures:
+      0:
         name: 'support.function.environment.begin.latex'
-      3:
-        name: 'punctuation.definition.argument.begin.latex'
+      1:
+        name: 'punctuation.definition.argument.begin.latex'
+      2:
+        name: 'variable.parameter.argument.latex'
       4:
+        name: 'punctuation.definition.argument.end.latex'
+    end: '\\\\end(\\{)(\\2)(\\})'
+    endCaptures:
+      0:
+        name: 'support.function.environment.end.latex'
+      1:
+        name: 'punctuation.definition.argument.begin.latex'
+      2:
         name: 'variable.parameter.argument.latex'
-      6:
-        name: 'punctuation.definition.argument.end.latex'
-    end: '(\\\\)(end)(\\{)(\\4)(\\})'
-    endCaptures:
-      1:
-        name: 'punctuation.definition.function.latex'
-      2:
-        name: 'support.function.environment.end.latex'
-      3:
-        name: 'punctuation.definition.argument.begin.latex'
-      4:
-        name: 'variable.parameter.argument.latex'
-      5:
+      3:
         name: 'punctuation.definition.argument.end.latex'
     patterns: [
       {
@@ -714,12 +693,10 @@
   }
   inputFile: {
     comment: 'Matches the input/include command and allows custom path colours.'
-    begin: '(\\\\)input(?=[^a-zA-Z@])'
+    begin: '\\\\input(?=[^a-zA-Z@])'
     beginCaptures:
       0:
         name: 'keyword.control.input.latex'
-      1:
-        name: 'punctuation.definition.function.latex'
     end: '(?<=\\})|(^\\s*$)'
     endCaptures:
       1:
@@ -774,4 +751,14 @@
   verbatim: {
     comment: 'Finds an inline verbatim string of the format \verb+...+'
     name: 'string.function.verbatim'
-  }
+    match: '\\\\verb([^a-zA-Z])(.*?)(\\1)'
+    captures:
+      0:
+        name: 'support.function.vebatim.latex'
+      1:
+        name: 'punctuation.latex'
+      2:
+        name: 'markup.raw.verbatim.latex'
+      3:
+        name: 'punctuation.latex'
+  }


Support python-like docstrings in block comments

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -878,6 +878,9 @@
           {
             'include': '#comments-block'
           }
+          {
+            'include': '#docstring'
+          }
         ]
       }
     ]
@@ -952,6 +955,31 @@
         'captures':
           '1':
             'name': 'keyword.docblockr.stata'
+      }
+    ]
+  'docstring':
+    'patterns': [
+      {
+        'begin': "'''"
+        'beginCaptures':
+          '0':
+            'name': 'punctuation.definition.string.begin.stata'
+        'end': "'''"
+        'endCaptures':
+          '0':
+            'name': 'punctuation.definition.string.begin.stata'
+        'name': 'string.quoted.docstring.stata'
+      }
+      {
+        'begin': '"""'
+        'beginCaptures':
+          '0':
+            'name': 'punctuation.definition.string.begin.stata'
+        'end': '"""'
+        'endCaptures':
+          '0':
+            'name': 'punctuation.definition.string.begin.stata'
+        'name': 'string.quoted.docstring.stata'
       }
     ]
   'macro-commands':


Improve syntax command to work across multiple lines

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -365,27 +365,29 @@
   }
   # Start manual functions:
   {
-    'comment': 'Color the syntax command'
-    'begin': '^\\s*(?=(syntax))'
+    'begin': '(?=syntax)'
     'end': '\\n'
     'patterns': [
       {
+        'begin': '///'
+        'end': '\\n'
+        'name': 'comment.block.stata'
+      }
+      {
         'comment': 'color before the comma'
-        'begin': '(syntax)\\s*'
+        'begin': 'syntax'
         'beginCaptures':
-          '1':
+          '0':
             'name': 'keyword.functions.program.stata'
-        'end': '(?=,|\\n)'
-        'patterns': [
-          {
-            'match': '((\\[?)(varlist|varname|newvarlist|newvarname|namelist|name|anything|if|in|using|exp|fweight|aweight|pweight|iweight)(\\])?)+\\s*'
-            'captures':
-              '2':
-                'name': 'punctuation.definition.parameters.begin.stata'
-              '3':
-                'name': 'entity.name.type.class.stata'
-              '4':
-                'name': 'punctuation.definition.parameters.end.stata'
+        'end': '(?=,)'
+        'patterns': [
+          {
+            'match': '\\[|\\]'
+            'name': 'punctuation.definition.parameters.stata'
+          }
+          {
+            'match': 'varlist|varname|newvarlist|newvarname|namelist|name|anything|if|in|using|exp|fweight|aweight|pweight|iweight'
+            'name': 'entity.name.type.class.stata'
           }
           {
             'include': '$self'
@@ -395,12 +397,17 @@
       {
         'comment': 'things to color after the comma'
         'comment': 'TODO: This can be improved to alert when new macros are created with illegal meaning'
-        'begin': '(,)'
+        'begin': ','
         'beginCaptures':
-          '1':
+          '0':
             'name': 'punctuation.definition.variable.begin.stata'
         'end': '(?=\\n)'
         'patterns': [
+          {
+            'begin': '///'
+            'end': '\\n'
+            'name': 'comment.block.stata'
+          }
           {
             'comment': 'color options with parentheses'
             'begin': '([a-zA-Z_0-9]+)(\\()'


add unicode regex functions with ICU support!!

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -10,6 +10,9 @@
 'patterns': [
   {
     'include': '#ascii-regex-functions'
+  }
+  {
+    'include': '#unicode-regex-functions'
   }
   {
     'include': '#builtin_functions'
@@ -514,7 +517,7 @@
   'builtin_functions':
     'patterns': [
       {
-        'begin': '\\b(bofd|Cdhms|Chms|Clock|clock|Cmdyhms|Cofc|cofC|Cofd|cofd|daily|date|day|dhms|dofb|dofC|dofc|dofh|dofm|dofq|dofw|dofy|dow|doy|halfyear|halfyearly|hh|hhC|hms|hofd|hours|mdy|mdyhms|minutes|mm|mmC|mofd|month|monthly|msofhours|msofminutes|msofseconds|qofd|quarter|quarterly|seconds|ss|ssC|tC|tc|td|th|tm|tq|tw|week|weekly|wofd|year|yearly|yh|ym|yofd|yq|yw|betaden|ibeta|ibetatail|invibeta|invibetatail|nbetaden|nibeta|invnibeta|binomialp|binomial|binomialtail|invbinomial|invbinomialtail|cauchyden|cauchy|cauchytail|invcauchy|invcauchytail|lncauchyden|chi2den|chi2|chi2tail|invchi2|invchi2tail|nchi2den|nchi2|nchi2tail|invnchi2|invnchi2tail|npnchi2|dunnettprob|invdunnettprob|exponentialden|exponential|exponentialtail|invexponential|invexponentialtail|Fden|F|Ftail|invF|invFtail|nFden|nF|nFtail|invnF|invnFtail|npnF|gammaden|gammap|gammaptail|invgammap|invgammaptail|dgammapda|dgammapdada|dgammapdadx|dgammapdx|dgammapdxdx|lnigammaden|hypergeometricp|hypergeometric|igaussianden|igaussian|igaussiantail|invigaussian|invigaussiantail|lnigaussianden|laplaceden|laplace|laplacetail|invlaplace|invlaplacetail|lnlaplaceden|logisticden|logisticden|logisticden|logistic|logistic|logistic|logistictail|logistictail|logistictail|invlogistic|invlogistic|invlogistic|invlogistictail|invlogistictail|invlogistictail|nbinomialp|nbinomial|nbinomialtail|invnbinomial|invnbinomialtail|normalden|normalden|normalden|normal|invnormal|lnnormalden|lnnormalden|lnnormalden|lnnormal|binormal|lnmvnormalden|poissonp|poisson|poissontail|invpoisson|invpoissontail|tden|t|ttail|invt|invttail|invnt|invnttail|ntden|nt|nttail|npnt|tukeyprob|invtukeyprob|weibullden|weibullden|weibull|weibull|weibulltail|weibulltail|invweibull|invweibull|invweibulltail|invweibulltail|weibullphden|weibullphden|weibullph|weibullph|weibullphtail|weibullphtail|invweibullph|invweibullph|invweibullphtail|invweibullphtail|lnwishartden|lniwishartden|abs|ceil|cloglog|comb|digamma|exp|floor|int|invcloglog|invlogit|ln|lnfactorial|lngamma|log|log10|logit|max|min|mod|reldif|round|sign|sqrt|sum|trigamma|trunc|cholesky|corr|diag|get|hadamard|I|inv|invsym|J|matuniform|nullmat|sweep|vec|vecdiag|coleqnumb|colnfreeparms|colnumb|colsof|det|diag0cnt|el|issymmetric|matmissing|mreldif|roweqnumb|rownfreeparms|rownumb|rowsof|trace|autocode|byteorder|c|chop|clip|cond|e|e|epsdouble|epsfloat|fileexists|fileread|filereaderror|filewrite|float|fmtwidth|inlist|inrange|irecode|matrix|maxbyte|maxdouble|maxfloat|maxint|maxlong|mi|minbyte|mindouble|minfloat|minint|minlong|missing|r|recode|replay|return|s|scalar|smallestdouble|runiform|runiform|runiformint|rbeta|rbinomial|rcauchy|rchi2|rexponential|rgamma|rhypergeometric|rigaussian|rlaplace|rlogistic|rlogistic|rlogistic|rnbinomial|rnormal|rnormal|rnormal|rpoisson|rt|rweibull|rweibull|rweibullph|rweibullph|abbrev|char|uchar|collatorlocale|collatorversion|indexnot|plural|plural|real|regexs|ustrregexm|ustrregexrf|ustrregexra|ustrregexs|soundex|strcat|strdup|string|string|stritrim|strlen|ustrlen|udstrlen|strlower|ustrlower|strltrim|ustrltrim|strmatch|strofreal|strofreal|strpos|ustrpos|strproper|ustrtitle|strreverse|ustrreverse|strrpos|ustrrpos|strrtrim|ustrrtrim|strtoname|ustrtoname|strtrim|ustrtrim|strupper|ustrupper|subinstr|usubinstr|subinword|substr|usubstr|udsubstr|tobytes|uisdigit|uisletter|ustrcompare|ustrcompareex|ustrfix|ustrfrom|ustrinvalidcnt|ustrleft|ustrnormalize|ustrright|ustrsortkey|ustrsortkeyex|ustrto|ustrtohex|ustrunescape|word|ustrword|wordbreaklocale|wordcount|ustrwordcount|abbrev|char|indexnot|itrim|length|lower|ltrim|plural|proper|real|regexs|reverse|rtrim|soundex|strcat|strdup|string|string|strmatch|strpos|strtoname|strtoname|subinstr|subinword|substr|trim|upper|word|wordcount|tin|twithin|acos|acosh|asin|asinh|atan|atan2|atanh|cos|cosh|sin|sinh|tan|tanh|mi)(\\()'
+        'begin': '\\b(bofd|Cdhms|Chms|Clock|clock|Cmdyhms|Cofc|cofC|Cofd|cofd|daily|date|day|dhms|dofb|dofC|dofc|dofh|dofm|dofq|dofw|dofy|dow|doy|halfyear|halfyearly|hh|hhC|hms|hofd|hours|mdy|mdyhms|minutes|mm|mmC|mofd|month|monthly|msofhours|msofminutes|msofseconds|qofd|quarter|quarterly|seconds|ss|ssC|tC|tc|td|th|tm|tq|tw|week|weekly|wofd|year|yearly|yh|ym|yofd|yq|yw|betaden|ibeta|ibetatail|invibeta|invibetatail|nbetaden|nibeta|invnibeta|binomialp|binomial|binomialtail|invbinomial|invbinomialtail|cauchyden|cauchy|cauchytail|invcauchy|invcauchytail|lncauchyden|chi2den|chi2|chi2tail|invchi2|invchi2tail|nchi2den|nchi2|nchi2tail|invnchi2|invnchi2tail|npnchi2|dunnettprob|invdunnettprob|exponentialden|exponential|exponentialtail|invexponential|invexponentialtail|Fden|F|Ftail|invF|invFtail|nFden|nF|nFtail|invnF|invnFtail|npnF|gammaden|gammap|gammaptail|invgammap|invgammaptail|dgammapda|dgammapdada|dgammapdadx|dgammapdx|dgammapdxdx|lnigammaden|hypergeometricp|hypergeometric|igaussianden|igaussian|igaussiantail|invigaussian|invigaussiantail|lnigaussianden|laplaceden|laplace|laplacetail|invlaplace|invlaplacetail|lnlaplaceden|logisticden|logisticden|logisticden|logistic|logistic|logistic|logistictail|logistictail|logistictail|invlogistic|invlogistic|invlogistic|invlogistictail|invlogistictail|invlogistictail|nbinomialp|nbinomial|nbinomialtail|invnbinomial|invnbinomialtail|normalden|normalden|normalden|normal|invnormal|lnnormalden|lnnormalden|lnnormalden|lnnormal|binormal|lnmvnormalden|poissonp|poisson|poissontail|invpoisson|invpoissontail|tden|t|ttail|invt|invttail|invnt|invnttail|ntden|nt|nttail|npnt|tukeyprob|invtukeyprob|weibullden|weibullden|weibull|weibull|weibulltail|weibulltail|invweibull|invweibull|invweibulltail|invweibulltail|weibullphden|weibullphden|weibullph|weibullph|weibullphtail|weibullphtail|invweibullph|invweibullph|invweibullphtail|invweibullphtail|lnwishartden|lniwishartden|abs|ceil|cloglog|comb|digamma|exp|floor|int|invcloglog|invlogit|ln|lnfactorial|lngamma|log|log10|logit|max|min|mod|reldif|round|sign|sqrt|sum|trigamma|trunc|cholesky|corr|diag|get|hadamard|I|inv|invsym|J|matuniform|nullmat|sweep|vec|vecdiag|coleqnumb|colnfreeparms|colnumb|colsof|det|diag0cnt|el|issymmetric|matmissing|mreldif|roweqnumb|rownfreeparms|rownumb|rowsof|trace|autocode|byteorder|c|chop|clip|cond|e|e|epsdouble|epsfloat|fileexists|fileread|filereaderror|filewrite|float|fmtwidth|inlist|inrange|irecode|matrix|maxbyte|maxdouble|maxfloat|maxint|maxlong|mi|minbyte|mindouble|minfloat|minint|minlong|missing|r|recode|replay|return|s|scalar|smallestdouble|runiform|runiform|runiformint|rbeta|rbinomial|rcauchy|rchi2|rexponential|rgamma|rhypergeometric|rigaussian|rlaplace|rlogistic|rlogistic|rlogistic|rnbinomial|rnormal|rnormal|rnormal|rpoisson|rt|rweibull|rweibull|rweibullph|rweibullph|abbrev|char|uchar|collatorlocale|collatorversion|indexnot|plural|plural|real|regexs|ustrregexs|soundex|strcat|strdup|string|string|stritrim|strlen|ustrlen|udstrlen|strlower|ustrlower|strltrim|ustrltrim|strmatch|strofreal|strofreal|strpos|ustrpos|strproper|ustrtitle|strreverse|ustrreverse|strrpos|ustrrpos|strrtrim|ustrrtrim|strtoname|ustrtoname|strtrim|ustrtrim|strupper|ustrupper|subinstr|usubinstr|subinword|substr|usubstr|udsubstr|tobytes|uisdigit|uisletter|ustrcompare|ustrcompareex|ustrfix|ustrfrom|ustrinvalidcnt|ustrleft|ustrnormalize|ustrright|ustrsortkey|ustrsortkeyex|ustrto|ustrtohex|ustrunescape|word|ustrword|wordbreaklocale|wordcount|ustrwordcount|abbrev|char|indexnot|itrim|length|lower|ltrim|plural|proper|real|regexs|reverse|rtrim|soundex|strcat|strdup|string|string|strmatch|strpos|strtoname|strtoname|subinstr|subinword|substr|trim|upper|word|wordcount|tin|twithin|acos|acosh|asin|asinh|atan|atan2|atanh|cos|cosh|sin|sinh|tan|tanh|mi)(\\()'
         'beginCaptures':
           '1':
             'name': 'support.function.builtin.stata'
@@ -529,6 +532,9 @@
             'include': '#ascii-regex-functions'
           }
           {
+            'include': '#unicode-regex-functions'
+          }
+          {
             'include': '#builtin_functions'
           }
           {
@@ -585,6 +591,9 @@
         'patterns': [
           {
             'include': '#ascii-regex-functions'
+          }
+          {
+            'include': '#unicode-regex-functions'
           }
           {
             'include': '#builtin_functions'
@@ -1375,3 +1384,429 @@
         ]
       }
     ]
+  'unicode-regex-functions':
+    'patterns': [
+      {
+        'comment': 'color regexm with regular quotes i.e. " '
+        'match': '\\b(ustrregexm)(\\()([^,]+)(,)\\s*(\")([^"]+)(\"(\')?)([,0-9\\s]*)?\\s*(\\))'
+        'captures':
+          '1':
+            'name': 'support.function.builtin.stata'
+          '2':
+            'name': 'punctuation.definition.parameters.begin.stata'
+          '3':
+            'patterns': [
+              {
+                'include': '#string-compound'
+              }
+              {
+                'include': '#string-regular'
+              }
+              {
+                'include': '#macro-local'
+              }
+              {
+                'include': '#macro-global'
+              }
+              {
+                'include': '#builtin_functions'
+              }
+              {
+                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'name': 'variable.parameter.function.stata'
+              }
+              {
+                'include': '#comments-triple-slash'
+              }
+            ]
+          '4':
+            'name': 'punctuation.definition.variable.begin.stata' # the comma
+          '5':
+            'name': 'punctuation.definition.string.begin.stata'
+          '6':
+            'patterns': [
+              {
+                'include': '#unicode-regex-internals'
+              }
+              # {
+              #   'include': '#comments-triple-slash'
+              # }
+              # Unknown if this helps. Regex not colored when regexm split over multiple lines as of 9/11/2017, 1:32:26 AM
+            ]
+          '7':
+            'name': 'punctuation.definition.string.end.stata'
+          '8':
+            'name': 'invalid.illegal.punctuation.stata'
+          '9':
+            'patterns': [
+              {
+                'include': '#numbers'
+              }
+              {
+                'match': ','
+                'name': 'punctuation.definition.variable.begin.stata' # the comma
+              }
+            ]
+          '10':
+            'name': 'punctuation.definition.parameters.end.stata'
+      }
+      {
+        'comment': 'color regexm with compound quotes'
+        'match': '\\b(ustrregexm)(\\()([^,]+)(,)\\s*(`\")([^"]+)(\"\')([,0-9\\s]*)?\\s*(\\))'
+        'captures':
+          '1':
+            'name': 'support.function.builtin.stata'
+          '2':
+            'name': 'punctuation.definition.parameters.begin.stata'
+          '3':
+            'patterns': [
+              {
+                'include': '#string-compound'
+              }
+              {
+                'include': '#string-regular'
+              }
+              {
+                'include': '#macro-local'
+              }
+              {
+                'include': '#macro-global'
+              }
+              {
+                'include': '#builtin_functions'
+              }
+              {
+                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'name': 'variable.parameter.function.stata'
+              }
+              {
+                'include': '#comments-triple-slash'
+              }
+            ]
+          '4':
+            'name': 'punctuation.definition.variable.begin.stata' # the comma
+          '5':
+            'name': 'punctuation.definition.string.begin.stata'
+          '6':
+            'patterns': [
+              {
+                'include': '#unicode-regex-internals'
+              }
+              # {
+              #   'include': '#comments-triple-slash'
+              # }
+              # Unknown if this helps. Regex not colored when regexm split over multiple lines as of 9/11/2017, 1:32:26 AM
+            ]
+          '7':
+            'name': 'punctuation.definition.string.end.stata'
+          '8':
+            'patterns': [
+              {
+                'include': '#numbers'
+              }
+              {
+                'match': ','
+                'name': 'punctuation.definition.variable.begin.stata' # the comma
+              }
+            ]
+          '9':
+            'name': 'punctuation.definition.parameters.end.stata'
+      }
+      {
+        'comment': 'color regexr with regular quotes i.e. " '
+        'match': '\\b(ustrregexrf|ustrregexra)(\\()([^,]+)(,)\\s*(\")([^"]+)(\"(\')?)\\s*(,)([^\\),]*)([,0-9\\s]*)?\\s*(\\))'
+        'captures':
+          '1':
+            'name': 'support.function.builtin.stata'
+          '2':
+            'name': 'punctuation.definition.parameters.begin.stata'
+          '3':
+            'patterns': [
+              {
+                'include': '#string-compound'
+              }
+              {
+                'include': '#string-regular'
+              }
+              {
+                'include': '#macro-local'
+              }
+              {
+                'include': '#macro-global'
+              }
+              {
+                'include': '#builtin_functions'
+              }
+              {
+                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'name': 'variable.parameter.function.stata'
+              }
+              {
+                'include': '#comments'
+              }
+            ]
+          '4':
+            'name': 'punctuation.definition.variable.begin.stata' # the comma
+          '5':
+            'name': 'punctuation.definition.string.begin.stata'
+          '6':
+            'patterns': [
+              {
+                'include': '#unicode-regex-internals'
+              }
+            ]
+          '7':
+            'name': 'punctuation.definition.string.end.stata'
+          '8':
+            'name': 'invalid.illegal.punctuation.stata'
+          '9':
+            'name': 'punctuation.definition.variable.begin.stata' # the comma
+          '10':
+            'patterns': [
+              {
+                'include': '#string-compound'
+              }
+              {
+                'include': '#string-regular'
+              }
+              {
+                'include': '#macro-local'
+              }
+              {
+                'include': '#macro-global'
+              }
+              {
+                'include': '#builtin_functions'
+              }
+              {
+                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'name': 'variable.parameter.function.stata'
+              }
+              {
+                'include': '#comments-triple-slash'
+              }
+            ]
+          '11':
+            'patterns': [
+              {
+                'include': '#numbers'
+              }
+              {
+                'match': ','
+                'name': 'punctuation.definition.variable.begin.stata' # the comma
+              }
+            ]
+          '12':
+            'name': 'punctuation.definition.parameters.end.stata'
+      }
+      {
+        'comment': 'color regexr with compound quotes i.e. `"text"\' '
+        'match': '\\b(ustrregexrf|ustrregexra)(\\()([^,]+)(,)\\s*(`\")([^"]+)(\"\')\\s*(,)([^\\)]*)(\\))'
+        'captures':
+          '1':
+            'name': 'support.function.builtin.stata'
+          '2':
+            'name': 'punctuation.definition.parameters.begin.stata'
+          '3':
+            'patterns': [
+              {
+                'include': '#string-compound'
+              }
+              {
+                'include': '#string-regular'
+              }
+              {
+                'include': '#macro-local'
+              }
+              {
+                'include': '#macro-global'
+              }
+              {
+                'include': '#builtin_functions'
+              }
+              {
+                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'name': 'variable.parameter.function.stata'
+              }
+              {
+                'include': '#comments'
+              }
+            ]
+          '4':
+            'name': 'punctuation.definition.variable.begin.stata' # the comma
+          '5':
+            'name': 'punctuation.definition.string.begin.stata'
+          '6':
+            'patterns': [
+              {
+                'include': '#unicode-regex-internals'
+              }
+            ]
+          '7':
+            'name': 'punctuation.definition.string.end.stata'
+          '8':
+            'name': 'punctuation.definition.variable.begin.stata' # the comma
+          '9':
+            'patterns': [
+              {
+                'include': '#string-compound'
+              }
+              {
+                'include': '#string-regular'
+              }
+              {
+                'include': '#macro-local'
+              }
+              {
+                'include': '#macro-global'
+              }
+              {
+                'include': '#builtin_functions'
+              }
+              {
+                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'name': 'variable.parameter.function.stata'
+              }
+              {
+                'include': '#comments-triple-slash'
+              }
+            ]
+          '10':
+            'patterns': [
+              {
+                'include': '#numbers'
+              }
+              {
+                'match': ','
+                'name': 'punctuation.definition.variable.begin.stata' # the comma
+              }
+            ]
+          '11':
+            'name': 'punctuation.definition.parameters.end.stata'
+      }
+    ]
+  'unicode-regex-internals':
+    'patterns': [
+      {
+        'match': '\\\\[bBAZzG]|\\^|\\$'
+        'name': 'keyword.control.anchor.stata'
+      }
+      {
+        'match': '\\\\[1-9][0-9]?'
+        'name': 'keyword.other.back-reference.stata'
+      }
+      {
+        'match': '[?+*][?+]?|\\{(\\d+,\\d+|\\d+,|,\\d+|\\d+)\\}\\??'
+        'name': 'keyword.operator.quantifier.stata'
+      }
+      {
+        'match': '\\|'
+        'name': 'keyword.operator.or.stata'
+      }
+      {
+        'begin': '\\(\\?\\#'
+        'end': '\\)'
+        'name': 'comment.block.stata'
+      }
+      {
+        'comment': 'We are restrictive in what we allow to go after the comment character to avoid false positives, since the availability of comments depend on regexp flags.'
+        'match': '(?<=^|\\s)#\\s[[a-zA-Z0-9,. \\t?!-:][^\\x{00}-\\x{7F}]]*$'
+        'name': 'comment.line.number-sign.stata'
+      }
+      {
+        'match': '\\(\\?[iLmsux]+\\)'
+        'name': 'keyword.other.option-toggle.stata'
+        # @NOTE not sure if this is included in the ICU regex
+      }
+      {
+        'begin': '(\\()((\\?=)|(\\?!)|(\\?<=)|(\\?<!))'
+        'beginCaptures':
+          '1':
+            'name': 'punctuation.definition.group.stata'
+          '2':
+            'name': 'punctuation.definition.group.assertion.stata'
+          '3':
+            'name': 'meta.assertion.look-ahead.stata'
+          '4':
+            'name': 'meta.assertion.negative-look-ahead.stata'
+          '5':
+            'name': 'meta.assertion.look-behind.stata'
+          '6':
+            'name': 'meta.assertion.negative-look-behind.stata'
+        'end': '(\\))'
+        'endCaptures':
+          '1':
+            'name': 'punctuation.definition.group.stata'
+        'name': 'meta.group.assertion.stata'
+        'patterns': [
+          {
+            'include': '#unicode-regex-internals'
+          }
+        ]
+      }
+      {
+        'begin': '(\\()(\\?\\(([1-9][0-9]?|[a-zA-Z_][a-zA-Z_0-9]*)\\))'
+        'beginCaptures':
+          '1':
+            'name': 'punctuation.definition.group.stata'
+          '2':
+            'name': 'punctuation.definition.group.assertion.conditional.stata'
+          '3':
+            'name': 'entity.name.section.back-reference.stata'
+        'comment': 'we can make this more sophisticated to match the | character that separates yes-pattern from no-pattern, but it\'s not really necessary.'
+        'end': '(\\))'
+        'name': 'meta.group.assertion.conditional.stata'
+        'patterns': [
+          {
+            'include': '#unicode-regex-internals'
+          }
+        ]
+      }
+      {
+        'include': '#unicode-regex-character-class'
+      }
+      {
+        # 'comment' 'NOTE: Error if I have \.+ No idea why but it works fine it seems with just \.'
+        # Also extremely weird but if I have the previous 'comment' line uncommented, everything inside the regexm(, "...") goes white.
+        'match': '.'
+        'name': 'string.quoted.stata'
+      }
+    ]
+  'unicode-regex-character-class':
+    'patterns': [
+      {
+        'match': '\\\\[wWsSdD]|\\.'
+        'name': 'constant.character.character-class.stata'
+      }
+      {
+        'match': '\\\\.'
+        'name': 'constant.character.escape.backslash.stata'
+      }
+      {
+        'begin': '(\\[)(\\^)?'
+        'beginCaptures':
+          '1':
+            'name': 'punctuation.definition.character-class.stata'
+          '2':
+            'name': 'keyword.operator.negation.stata'
+        'end': '(\\])'
+        'endCaptures':
+          '1':
+            'name': 'punctuation.definition.character-class.stata'
+        'name': 'constant.other.character-class.set.stata'
+        'patterns': [
+          {
+            'include': '#unicode-regex-character-class'
+          }
+          {
+            'captures':
+              '2':
+                'name': 'constant.character.escape.backslash.stata'
+              '4':
+                'name': 'constant.character.escape.backslash.stata'
+            'match': '((\\\\.)|.)\\-((\\\\.)|[^\\]])'
+            'name': 'constant.other.character-class.range.stata'
+          }
+        ]
+      }
+    ]


comment: fix mention link

comment: fix mention link

--- client/Social/Activity/views/comments/inputwidget.coffee
+++ client/Social/Activity/views/comments/inputwidget.coffee
@@ -99,10 +99,10 @@

     @input.unsetClass 'placeholder'

-    @input.setContent \
+    @input.setValue \
       if value.indexOf("@#{username}") >= 0 then value
-      else if value.length is 0 then "@#{username} "
-      else "#{value} @#{username} "
+      else if value.length is 0 then "@#{username} "
+      else "#{value} @#{username} "

     @setFocus()


client: remove close button from delete team overlay view

--- client/app/lib/components/deleteteamoverlay/index.coffee
+++ client/app/lib/components/deleteteamoverlay/index.coffee
@@ -29,7 +29,6 @@
             Your team is gone but we hope the memories were stellar.
             Next, you can login to another team or create a new one.

-          Close
         '''

     setTimeout ->


do not reraise errors from loading extensions

--- src/app/window.coffee
+++ src/app/window.coffee
@@ -79,7 +79,6 @@
       extension
     catch e
       console.error "Failed to load extension named '#{name}'"
-      throw e

   reload: ->
     if rootView.getModifiedBuffers().length > 0


Remove rootViewStates tweak no longer needed

--- src/app/window.coffee
+++ src/app/window.coffee
@@ -41,7 +41,7 @@
   # Note: RootView assigns itself on window on initialization so that
   # window.rootView is available when loading user configuration
   attachRootView: (pathToOpen) ->
-    if rootViewState = atom.rootViewStates?[$windowNumber]
+    if rootViewState = atom.rootViewStates[$windowNumber]
       RootView.deserialize(JSON.parse(rootViewState))
     else
       new RootView(pathToOpen)


Replace Notifier with App.popup*

Replace Notifier with App.popup*

--- src/controller/boardcontroller.coffee
+++ src/controller/boardcontroller.coffee
@@ -57,8 +57,9 @@
   ###*
   @inheritdoc Controller#activate
   ###
-  activate: (initialPage = "catalog") ->
+  activate: (initialPage) ->
     $ = @$
+    initialPage or= (@_lastTabPage or "catalog")
     return super(
     ).then(=>
       # Setup jquery-scrollTabs (only once)
@@ -95,43 +96,33 @@
   @inheritdoc Controller#deactivate
   ###
   deactivate: ->
-    notify = new Notifier({
-      icon: "glyphicon glyphicon-warning-sign"
-    }, {
-      type: "warning"
-      allow_dismiss: true
-      placement: {from: "top", align: "center"}
-      delay: 2
-      offset: {x: 20, y: 50}
-      showProgressbar: true
-    })
     return Promise.resolve(
     ).then(=>
       return false if @_board?
-      notify.show(
-        message: I18n.getMessage("Board_is_not_selected")
-      ) # Do not wait until notification closing
-      return true
+      App.popupWarning(
+        I18n.getMessage("Board_is_not_selected")
+      )
+      return true # Do not wait until notification closing
     ).then((skip) =>
       return true if skip
       return @_board.loadFirmware().catch(=> return)
     ).then((firmware) =>
       return true if firmware == true
       return false if firmware?
-      notify.show(
-        message: I18n.getMessage("Firmware_is_not_selected")
-      ) # Do not wait until notification closing
-      return true
+      App.popupWarning(
+        I18n.getMessage("Firmware_is_not_selected")
+      )
+      return true # Do not wait until notification closing
     ).then((skip) =>
       return true if skip
       return @_board.loadFirmRevision().catch(=> return)
     ).then((firmRevision) =>
       return true if firmRevision == true
       return firmRevision if firmRevision?
-      notify.show(
-        message: I18n.getMessage("Firmware_revision_is_not_selected")
-      ) # Do not wait until notification closing
-      return true
+      App.popupWarning(
+        I18n.getMessage("Firmware_revision_is_not_selected")
+      )
+      return true # Do not wait until notification closing
     ).then((firmRevision) =>
       return true if firmRevision == true
       return firmRevision.checkCacheAvailability(
@@ -145,11 +136,10 @@
             return spin.html("#{msg}\n#{url}") if url?
             return spin.html(I18n.getMessage("Saving_firmware"))
         ).catch((error) =>
-          App.error(error)
-          notify.show(
-            message: I18n.getMessage("Firmware_download_failed")
-          ) # Do not wait until notification closing
-          return
+          App.popupError(
+            I18n.getMessage("Firmware_download_failed")
+          )
+          return  # Do not wait until notification closing
         ).finally(=>
           spin.hide()
         ) # return firmRevision.download().then()...
@@ -451,4 +441,3 @@
 App = require("app/app")
 Board = require("board/board")
 BoardCatalog = require("firmware/boardcatalog")
-Notifier = require("ui/notifier")


export a api

export a api

--- src/coffee/api.coffee
+++ src/coffee/api.coffee
@@ -26,3 +26,4 @@
     if data._id then delete data._id
     ajax.postJson "#{API_DOMAIN}user/#{user}/type/#{type}/target/#{target}/Comment", data:data

+EH.api = api


Rename method.

Rename method.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -8,7 +8,7 @@

 fs = Promise.promisifyAll require('fs')

-processImages = (files, description) ->
+resizeAndUpload = (files, description) ->

   bar = new ProgressBar "Processing prefix '#{description.headers.prefix}':\t[:bar] :percent, :current of :total images done (time: elapsed :elapseds, eta :etas)", {
     complete: '=',
@@ -47,7 +47,7 @@
         content.Key.indexOf(suffix) > 0

     # process files
-    processImages files, description
+    resizeAndUpload files, description
 , {concurrency: 1}
 .catch (error) ->
   console.log error