[fix] change grammars name.

[fix] change grammars name.

--- grammars/bve_map.cson
+++ grammars/bve_map.cson
@@ -4,552 +4,552 @@
   'firstLineMatch': '^BveTs\\s*Map\\s*2.02'
   'patterns': [
     {
-      'name': 'comment.bve-map'
+      'name': 'comment.bve-map-2.02'
       'match': '((//|#).*)'
       'captures':
           '1':
-              'name': 'comment.line.bve-map'
-    }
-    {
-      'name': 'distance.bve-map'
+              'name': 'comment.line.bve-map-2.02'
+    }
+    {
+      'name': 'distance.bve-map-2.02'
       'match': '(\\d*)\\s*;'
       'captures':
         '1':
-          'name': 'entity.name.section.bve-map'
-    }
-    {
-      'name': 'include.bve-map'
+          'name': 'entity.name.section.bve-map-2.02'
+    }
+    {
+      'name': 'include.bve-map-2.02'
       'match': '(include)\\s*\'(.+)\'\\s*;'
       'captures':
           '1':
-              'name': 'keyword.control.bve-map'
+              'name': 'keyword.control.bve-map-2.02'
           '2':
-              'name': 'entity.other.inherited-class.bve-map'
-    }
-    {
-      'name': 'repeater.bve-map'
+              'name': 'entity.other.inherited-class.bve-map-2.02'
+    }
+    {
+      'name': 'repeater.bve-map-2.02'
       'match': '(Repeater)\\s*\\[\\s*\'(.+)\'\\s*\\]'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.tag.bve-map'
-    }
-    {
-      'name': 'repeater.begin0.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.tag.bve-map-2.02'
+    }
+    {
+      'name': 'repeater.begin0.bve-map-2.02'
       'match': '\.(Begin0?)\\s*\\((.*)\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'curve.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'curve.bve-map-2.02'
       'match': '\\b(Curve)'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-    }
-    {
-      'name': 'curve.setGauge.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+    }
+    {
+      'name': 'curve.setGauge.bve-map-2.02'
       'match': '\.(SetGauge)\\(\\s*(.*)\\s*\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'gauge.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'gauge.bve-map-2.02'
       'match': '\.(Gauge)\\(\\s*(.*)\\s*\\);'
       'captures':
         '1':
-          'name': 'invalid.deprecated.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'curve.setCenter.bve-map'
+          'name': 'invalid.deprecated.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'curve.setCenter.bve-map-2.02'
       'match': '\.(SetCenter)\\(\\s*(.*)\\s*\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'curve.setFunction.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'curve.setFunction.bve-map-2.02'
       'match': '\.(SetFunction)\\(\\s*(\\d)\\s*\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'curve.beginTransition.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'curve.beginTransition.bve-map-2.02'
       'match': '\.(BeginTransition)\\(\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-    }
-    {
-      'name': 'curve.begin.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+    }
+    {
+      'name': 'curve.begin.bve-map-2.02'
       'match': '(Curve)\\s*\.(Begin)\\(((\\d|.|\\s|,)+)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'curve.end.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'curve.end.bve-map-2.02'
       'match': '\.(End)\\(\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-    }
-    {
-      'name': 'curve.interpolate.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+    }
+    {
+      'name': 'curve.interpolate.bve-map-2.02'
       'match': '\.(Interpolate)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'curve.change.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'curve.change.bve-map-2.02'
       'match': '\.(Change)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'gradient.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'gradient.bve-map-2.02'
       'match': '\\b(Gradient)'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-    }
-    {
-      'name': 'track.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+    }
+    {
+      'name': 'track.bve-map-2.02'
       'match': '\\b(Track)\\[\'(\.+)\'\\]'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'entity.name.tag.bve-map'
-    }
-    {
-      'name': 'track.xInterpolate.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'entity.name.tag.bve-map-2.02'
+    }
+    {
+      'name': 'track.xInterpolate.bve-map-2.02'
       'match': '\.(X)\.(Interpolate)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'entity.type.bve-map'
-        '2':
-          'name': 'entity.type.bve-map'
-        '3':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'track.yInterpolate.bve-map'
+          'name': 'entity.type.bve-map-2.02'
+        '2':
+          'name': 'entity.type.bve-map-2.02'
+        '3':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'track.yInterpolate.bve-map-2.02'
       'match': '\.(Y)\.(Interpolate)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'entity.type.bve-map'
-        '2':
-          'name': 'entity.type.bve-map'
-        '3':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'track.position.bve-map'
+          'name': 'entity.type.bve-map-2.02'
+        '2':
+          'name': 'entity.type.bve-map-2.02'
+        '3':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'track.position.bve-map-2.02'
       'match': '\.(Position)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'track.cant.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'track.cant.bve-map-2.02'
       'match': '\.(Cant)\.'
       'captures':
         '1':
-          'name': 'entity.type.bve-map'
-    }
-    {
-      'name': 'structure.load.bve-map'
+          'name': 'entity.type.bve-map-2.02'
+    }
+    {
+      'name': 'structure.load.bve-map-2.02'
       'match': '\\b(Structure)\.(Load)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'entity.other.inherited-class.bve-map'
-    }
-    {
-      'name': 'structure.put.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'entity.other.inherited-class.bve-map-2.02'
+    }
+    {
+      'name': 'structure.put.bve-map-2.02'
       'match': '\\b(Structure)\\[\'(\.+)\'\\]\.(Put0?)\\(\\s*\'(\.*)\'(.*)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.tag.bve-map'
-        '3':
-          'name': 'entity.name.type.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.tag.bve-map-2.02'
+        '3':
+          'name': 'entity.name.type.bve-map-2.02'
         '4':
-          'name': 'entity.name.tag.bve-map'
+          'name': 'entity.name.tag.bve-map-2.02'
         '5':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'structure.putBetWeen.bve-map'
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'structure.putBetWeen.bve-map-2.02'
       'match': '\\b(Structure)\\[\'(.*)\'\\]\.(PutBetween)\\(\'(.*)\',\'(.*)\'(,(.*))?\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'entity.name.tag.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'entity.name.tag.bve-map-2.02'
         '4':
-          'name': 'entity.name.tag.bve-map'
+          'name': 'entity.name.tag.bve-map-2.02'
         '5':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'background.change.bve-map'
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'background.change.bve-map-2.02'
       'match': '\\b(Background)\.(Change)\\((.+)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'entity.name.tag.bve-map'
-    }
-    {
-      'name': 'station.load.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'entity.name.tag.bve-map-2.02'
+    }
+    {
+      'name': 'station.load.bve-map-2.02'
       'match': '\\b(Station)\.(Load)\\((.+)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'entity.other.inherited-class.bve-map'
-    }
-    {
-      'name': 'station.put.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'entity.other.inherited-class.bve-map-2.02'
+    }
+    {
+      'name': 'station.put.bve-map-2.02'
       'match': '\\b(Station)\\[(.+)\\]\.(Put)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.tag.bve-map'
-        '3':
-          'name': 'entity.name.type.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.tag.bve-map-2.02'
+        '3':
+          'name': 'entity.name.type.bve-map-2.02'
         '4':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'section.bve-map'
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'section.bve-map-2.02'
       'match': '\\b(Section)'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-    }
-    {
-      'name': 'section.setSpeedLimit.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+    }
+    {
+      'name': 'section.setSpeedLimit.bve-map-2.02'
       'match': '\.(SetSpeedLimit)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'section.BeginNew.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'section.BeginNew.bve-map-2.02'
       'match': '\.(BeginNew)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'signal.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'signal.bve-map-2.02'
       'match': '\\b(Signal)\.(SpeedLimit)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'signal.load.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'signal.load.bve-map-2.02'
       'match': '\\b(Signal)\.(Load)\\((.+)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'entity.other.inherited-class.bve-map'
-    }
-    {
-      'name': 'signal.put.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'entity.other.inherited-class.bve-map-2.02'
+    }
+    {
+      'name': 'signal.put.bve-map-2.02'
       'match': '\\b(Signal)\\[\'(.*)\'\\]\.(Put)\\((.*),(.*),(.*)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.tag.bve-map'
-        '3':
-          'name': 'entity.name.type.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.tag.bve-map-2.02'
+        '3':
+          'name': 'entity.name.type.bve-map-2.02'
         '4':
-          'name': 'constant.numeric.bve-map'
+          'name': 'constant.numeric.bve-map-2.02'
         '5':
-          'name': 'entity.name.tag.bve-map'
+          'name': 'entity.name.tag.bve-map-2.02'
         '6':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'beacon.put.bve-map'
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'beacon.put.bve-map-2.02'
       'match': '\\b(Beacon)\.(Put)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'speedLimit.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'speedLimit.bve-map-2.02'
       'match': '\\b(SpeedLimit)'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-    }
-    {
-      'name': 'pretrain.pass.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+    }
+    {
+      'name': 'pretrain.pass.bve-map-2.02'
       'match': '\\b(PreTrain)\.(Pass)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'light.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'light.bve-map-2.02'
       'match': '\\b(Light)'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-    }
-    {
-      'name': 'light.ambient.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+    }
+    {
+      'name': 'light.ambient.bve-map-2.02'
       'match': '\.(Ambient)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'light.diffuse.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'light.diffuse.bve-map-2.02'
       'match': '\.(Diffuse)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'light.direction.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'light.direction.bve-map-2.02'
       'match': '\.(Direction)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'entity.name.type.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'fog.bve-map'
+          'name': 'entity.name.type.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'fog.bve-map-2.02'
       'match': '(Fog)'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-    }
-    {
-      'name': 'set.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+    }
+    {
+      'name': 'set.bve-map-2.02'
       'match': '\.(Set)\\s*\\((.*)\\)'
       'captures':
         '1':
-          'name': 'invalid.deprecated.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'drawdistance.bve-map'
+          'name': 'invalid.deprecated.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'drawdistance.bve-map-2.02'
       'match': '\\b(DrawDistance)'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-    }
-    {
-      'name': 'cabIlluminance.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+    }
+    {
+      'name': 'cabIlluminance.bve-map-2.02'
       'match': '\\b(CabIlluminance)'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-    }
-    {
-      'name': 'irregularity.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+    }
+    {
+      'name': 'irregularity.bve-map-2.02'
       'match': '\\b(Irregularity)'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-    }
-    {
-      'name': 'adhesion.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+    }
+    {
+      'name': 'adhesion.bve-map-2.02'
       'match': '\\b(Adhesion)'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-    }
-    {
-      'name': 'sound.load.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+    }
+    {
+      'name': 'sound.load.bve-map-2.02'
       'match': '\\b(Sound)\.(Load)\\((.+)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'entity.other.inherited-class.bve-map'
-    }
-    {
-      'name': 'sound.play.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'entity.other.inherited-class.bve-map-2.02'
+    }
+    {
+      'name': 'sound.play.bve-map-2.02'
       'match': '\\b(Sound)\\[\'(.+)\'\\]\.(Play)\\(\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.tag.bve-map'
-        '3':
-          'name': 'entity.name.type.bve-map'
-    }
-    {
-      'name': 'sound3d.load.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.tag.bve-map-2.02'
+        '3':
+          'name': 'entity.name.type.bve-map-2.02'
+    }
+    {
+      'name': 'sound3d.load.bve-map-2.02'
       'match': '\\b(Sound3D)\.(Load)\\((.+)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'entity.other.inherited-class.bve-map'
-    }
-    {
-      'name': 'sound3d.put.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'entity.other.inherited-class.bve-map-2.02'
+    }
+    {
+      'name': 'sound3d.put.bve-map-2.02'
       'match': '\\b(Sound3D)\\[\'(.+)\'\\]\.(Put)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.tag.bve-map'
-        '3':
-          'name': 'entity.name.type.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.tag.bve-map-2.02'
+        '3':
+          'name': 'entity.name.type.bve-map-2.02'
         '4':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'rollingNoise.bve-map'
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'rollingNoise.bve-map-2.02'
       'match': '\\b(RollingNoise)'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-    }
-    {
-      'name': 'flangeNoise.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+    }
+    {
+      'name': 'flangeNoise.bve-map-2.02'
       'match': '\\b(FlangeNoise)'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-    }
-    {
-      'name': 'jointNoise.play.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+    }
+    {
+      'name': 'jointNoise.play.bve-map-2.02'
       'match': '\\b(JointNoise)\.(Play)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'train.add.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'train.add.bve-map-2.02'
       'match': '\\b(Train)\.(Add)\\((.*)\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.type.bve-map'
-        '3':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'train.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.type.bve-map-2.02'
+        '3':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'train.bve-map-2.02'
       'match': '\\b(Train)\\[\'(.*)\'\\]'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.tag.bve-map'
-    }
-    {
-      'name': 'train.load.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.tag.bve-map-2.02'
+    }
+    {
+      'name': 'train.load.bve-map-2.02'
       'match': '\\b(Train)\\[\'(.*)\'\\]\.(Load)\\(\'(.*)\'\\);'
       'captures':
         '1':
-          'name': 'keyword.control.bve-map'
-        '2':
-          'name': 'entity.name.tag.bve-map'
-        '3':
-          'name': 'entity.other.inherited-class.bve-map'
-    }
-    {
-      'name': 'train.enable.bve-map'
+          'name': 'keyword.control.bve-map-2.02'
+        '2':
+          'name': 'entity.name.tag.bve-map-2.02'
+        '3':
+          'name': 'entity.other.inherited-class.bve-map-2.02'
+    }
+    {
+      'name': 'train.enable.bve-map-2.02'
       'match': '\.(Enable)\\(\'(.*)\'\\);'
       'captures':
         '1':
-          'name': 'entity.name.tag.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
-    }
-    {
-      'name': 'train.stop.bve-map'
+          'name': 'entity.name.tag.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
+    }
+    {
+      'name': 'train.stop.bve-map-2.02'
       'match': '\.(Stop)\\(\'(.*)\'\\);'
       'captures':
         '1':
-          'name': 'entity.name.tag.bve-map'
-        '2':
-          'name': 'constant.numeric.bve-map'
+          'name': 'entity.name.tag.bve-map-2.02'
+        '2':
+          'name': 'constant.numeric.bve-map-2.02'
     }
   ]
 }


Fixed failing popover update for several orphans

--- src/coffee/bootstrap-tour.coffee
+++ src/coffee/bootstrap-tour.coffee
@@ -230,6 +230,8 @@
         $element
         .popover('destroy')
         .removeClass "tour-#{@_options.name}-element tour-#{@_options.name}-#{i}-element"
+        $element
+        .data('bs.popover', null)
         if step.reflex
           $ step.reflexElement
           .removeClass('tour-step-element-reflex')


head: remove google fonts embed, fixes #63

--- src/components/head/index.coffee
+++ src/components/head/index.coffee
@@ -81,51 +81,10 @@
         innerHTML: modelSerialization or ''

       # fonts
-      z 'style',
-        innerHTML: '
-          @font-face {
-            font-family: "Roboto";
-            font-style: normal;
-            font-weight: 300;
-            src:
-              local("Roboto Light"),
-              local("Roboto-Light"),
-              url(https://fonts.gstatic.com/s/roboto/v15/Hgo13k-tfSpn0qi1S' +
-              'FdUfZBw1xU1rKptJj_0jans920.woff2) format("woff2"),
-              url(https://fonts.gstatic.com/s/roboto/v15/Hgo13k-tfSpn0qi1S' +
-              'FdUfbO3LdcAZYWl9Si6vvxL-qU.woff) format("woff"),
-              url(https://fonts.gstatic.com/s/roboto/v15/Hgo13k-tfSpn0qi1S' +
-              'FdUfSZ2oysoEQEeKwjgmXLRnTc.ttf) format("truetype");
-          }
-          @font-face {
-            font-family: "Roboto";
-            font-style: normal;
-            font-weight: 400;
-            src:
-              local("Roboto"),
-              local("Roboto-Regular"),
-              url(https://fonts.gstatic.com/s/roboto/v15/oMMgfZMQthOryQo9n' +
-              '22dcuvvDin1pK8aKteLpeZ5c0A.woff2) format("woff2"),
-              url(https://fonts.gstatic.com/s/roboto/v15/CrYjSnGjrRCn0pd9V' +
-              'QsnFOvvDin1pK8aKteLpeZ5c0A.woff) format("woff"),
-              url(https://fonts.gstatic.com/s/roboto/v15/QHD8zigcbDB8aPfIo' +
-              'aupKOvvDin1pK8aKteLpeZ5c0A.ttf) format("truetype");
-          }
-          @font-face {
-            font-family: "Roboto";
-            font-style: normal;
-            font-weight: 500;
-            src:
-              local("Roboto Medium"),
-              local("Roboto-Medium"),
-              url(https://fonts.gstatic.com/s/roboto/v15/RxZJdnzeo3R5zSexg' +
-              'e8UUZBw1xU1rKptJj_0jans920.woff2) format("woff2"),
-              url(https://fonts.gstatic.com/s/roboto/v15/RxZJdnzeo3R5zSexg' +
-              'e8UUbO3LdcAZYWl9Si6vvxL-qU.woff) format("woff"),
-              url(https://fonts.gstatic.com/s/roboto/v15/RxZJdnzeo3R5zSexg' +
-              'e8UUSZ2oysoEQEeKwjgmXLRnTc.ttf) format("truetype");
-          }
-        '
+      z 'link',
+        rel: 'stylesheet'
+        type: 'text/css'
+        href: 'https://fonts.googleapis.com/css?family=Roboto:400,300,500'

       # styles
       if isInliningSource


convert PlayDetail to class component

convert PlayDetail to class component

--- resources/assets/coffee/react/_components/play-detail.coffee
+++ resources/assets/coffee/react/_components/play-detail.coffee
@@ -19,75 +19,81 @@
 {a, div, i, img, small, span} = ReactDOMFactories
 el = React.createElement

-@PlayDetail = ({score}) ->
-  pp = score.best?.pp ? score.pp
+class @PlayDetail extends React.PureComponent
+  constructor: (props) ->
+    super props

-  modsText =
-    if score.mods.length
-      " +#{(mod.shortName for mod in score.mods).join(',')} "
-    else
-      ' '

-  div
-    className: 'detail-row'
+  render: =>
+    score = @props.score
+    pp = score.best?.pp ? score.pp
+
+    modsText =
+      if score.mods.length
+        " +#{(mod.shortName for mod in score.mods).join(',')} "
+      else
+        ' '
+
     div
-      className: 'detail-row__content'
+      className: 'detail-row'
       div
-        className: 'detail-row__icon'
-        div className: "badge-rank badge-rank--full badge-rank--#{score.rank}"
+        className: 'detail-row__content'
+        div
+          className: 'detail-row__icon'
+          div className: "badge-rank badge-rank--full badge-rank--#{score.rank}"

-      div className: 'detail-row__detail',
-        div
-          className: 'detail-row__detail-column detail-row__detail-column--full'
+        div className: 'detail-row__detail',
           div
-            className: 'detail-row__detail-row detail-row__detail-row--main'
-            a
-              href: score.beatmap.url
-              className: 'detail-row__text-score detail-row__text-score--title'
-              title: "#{score.beatmapset.artist} - #{score.beatmapset.title} "
-              "#{score.beatmapset.title} [#{score.beatmap.version}]"
-              ' '
-              small
-                className: 'detail-row__text-score detail-row__text-score--artist'
-                score.beatmapset.artist
-          div
-            className: 'detail-row__detail-row detail-row__detail-row--bottom'
-            span
-              className: 'detail-row__text-score detail-row__text-score--time'
-              dangerouslySetInnerHTML:
-                __html: osu.timeago score.created_at
-        div
-          className: 'detail-row__detail-column detail-row__detail-column--score-data'
-          div
-            className: 'detail-row__score-data detail-row__score-data--mods'
-            el Mods, mods: score.mods
-          div
-            className: 'detail-row__score-data detail-row__score-data--main'
+            className: 'detail-row__detail-column detail-row__detail-column--full'
             div
               className: 'detail-row__detail-row detail-row__detail-row--main'
+              a
+                href: score.beatmap.url
+                className: 'detail-row__text-score detail-row__text-score--title'
+                title: "#{score.beatmapset.artist} - #{score.beatmapset.title} "
+                "#{score.beatmapset.title} [#{score.beatmap.version}]"
+                ' '
+                small
+                  className: 'detail-row__text-score detail-row__text-score--artist'
+                  score.beatmapset.artist
+            div
+              className: 'detail-row__detail-row detail-row__detail-row--bottom'
               span
-                className: 'detail-row__text-score detail-row__text-score--pp'
-                title:
-                  if score.weight
-                    osu.trans 'users.show.extra.top_ranks.weighted_pp',
-                      percentage: "#{Math.round(score.weight.percentage)}%"
-                      pp: osu.trans('users.show.extra.top_ranks.pp', amount: Math.round(score.weight.pp)).toLocaleString()
-                if pp > 0
-                  osu.trans('users.show.extra.top_ranks.pp', amount: Math.round(pp).toLocaleString())
-                else
-                  span
-                    title:
-                      if score.beatmapset.status not in ['ranked', 'approved']
-                        osu.trans('users.show.extra.top_ranks.not_ranked')
-                    '-'
+                className: 'detail-row__text-score detail-row__text-score--time'
+                dangerouslySetInnerHTML:
+                  __html: osu.timeago score.created_at
+          div
+            className: 'detail-row__detail-column detail-row__detail-column--score-data'
             div
-              className: 'detail-row__score-details'
+              className: 'detail-row__score-data detail-row__score-data--mods'
+              el Mods, mods: score.mods
+            div
+              className: 'detail-row__score-data detail-row__score-data--main'
               div
-                className: 'detail-row__text-score'
-                osu.trans 'users.show.extra.historical.recent_plays.accuracy',
-                  percentage: "#{(score.accuracy * 100).toFixed(2)}%"
+                className: 'detail-row__detail-row detail-row__detail-row--main'
+                span
+                  className: 'detail-row__text-score detail-row__text-score--pp'
+                  title:
+                    if score.weight
+                      osu.trans 'users.show.extra.top_ranks.weighted_pp',
+                        percentage: "#{Math.round(score.weight.percentage)}%"
+                        pp: osu.trans('users.show.extra.top_ranks.pp', amount: Math.round(score.weight.pp)).toLocaleString()
+                  if pp > 0
+                    osu.trans('users.show.extra.top_ranks.pp', amount: Math.round(pp).toLocaleString())
+                  else
+                    span
+                      title:
+                        if score.beatmapset.status not in ['ranked', 'approved']
+                          osu.trans('users.show.extra.top_ranks.not_ranked')
+                      '-'
+              div
+                className: 'detail-row__score-details'
+                div
+                  className: 'detail-row__text-score'
+                  osu.trans 'users.show.extra.historical.recent_plays.accuracy',
+                    percentage: "#{(score.accuracy * 100).toFixed(2)}%"

-    div
-      className: 'detail-row__more'
-      if score.replay
-        el window._exported.PlayDetailMenu, score: score
+      div
+        className: 'detail-row__more'
+        if score.replay
+          el window._exported.PlayDetailMenu, { score }


Fixes typo in annotable.js.coffee

Fixes typo in annotable.js.coffee

--- app/assets/javascripts/annotable.js.coffee
+++ app/assets/javascripts/annotable.js.coffee
@@ -10,7 +10,7 @@
         .include(annotator.ui.main, { element: this })
         .include(annotator.storage.http, { prefix: "", urls: { search: "/annotations/search" } })
         .include ->
-          beforeannotationCreated: (ann) ->
+          beforeAnnotationCreated: (ann) ->
             ann[ann_type + "_id"] = ann_id
             ann.user_id           = ann_user_id


Tweak welcome message

Tweak welcome message

--- scripts/welcome-to-zihub.coffee
+++ scripts/welcome-to-zihub.coffee
@@ -19,9 +19,9 @@
     msg = """
 Hola {nick}, soy Robotico...un bot, un amigo, un sirviente

-Te recominedo que antes de nada revises tu perfil y te pongas un avatar (nos gusta ver la cara a la gente para conocernos cuando coincidamos en algún sarao), luego puedes presentarte en el canal #presentaciones para que sepan que has llegado.
+Te recomiendo que antes de nada revises tu perfil y te pongas un avatar (nos gusta ver la cara a la gente para conocernos cuando coincidamos en algún sarao), luego puedes presentarte en el canal <https://zithub.slack.com/messages/presentaciones/|#presentaciones> para que sepan que has llegado.

-Por aquí usamos la regla de los dos pies, así que te recomiendo que eches un vistazo a los canales y te unas a los de tu interés. En el momento que veas que no te aportan nada o que tu no puedes aportar nada puedes (debes) abandonar el canal y aquí no ha pasado nada.
+Por aquí usamos la regla de los dos pies, así que echa un vistazo a los canales y únete a los de tu interés. En el momento que veas que no te aportan nada o que tu no puedes aportar nada puedes (debes) abandonar el canal y aquí no ha pasado nada.

 Bienvenid@
 """
@@ -54,3 +54,6 @@
       robot.messageRoom user.name, welcomeMsg(user.name)
       robot.messageRoom '#presentaciones', "Hola @#{user.name}"
       welcomeGif(user, 'presentaciones')
+
+  robot.respond /bienvenida/i, (msg) ->
+    msg.reply welcomeMsg(msg.user.name)


Remove one last javascript file

Remove one last javascript file

--- app/assets/javascripts/bootstrap.js.coffee
+++ app/assets/javascripts/bootstrap.js.coffee
@@ -1,4 +0,0 @@
-jQuery ->
-  $("a[rel=popover]").popover()
-  $(".tooltip").tooltip()
-  $("a[rel=tooltip]").tooltip()


removed logging code

removed logging code

--- app/assets/javascripts/chatrooms.js.coffee
+++ app/assets/javascripts/chatrooms.js.coffee
@@ -38,10 +38,8 @@
     last = msg_id
     $.each(data, (key, val) ->
       email = val.user.email.split("@")[0]
-      console.log email
       html = '

' + val.message + '

' + email + '' + val.created_at + '

'
       items.push(html)
-      console.log items
       if val.id > last
         last = val.id
     )
@@ -54,7 +52,6 @@
   chatroom_id = $("#chatroom-id").html()
   url = "/chatrooms/" + chatroom_id + "/msgs.js"
   params = $("#new_msg").serialize()
-  console.log params
   $.post(url,params,() ->
     $("#msg_message").val("")
     updateChatroom()


#123 hotfix

#123 hotfix

--- app/assets/javascripts/main/routing.coffee
+++ app/assets/javascripts/main/routing.coffee
@@ -134,7 +134,7 @@

       return

-    "admin.user.userAdministration" : ->
+    "admin.user.userList" : ->
       require ["multiselect"], ->

         $popovers = $("a[rel=popover]")


OX-366 fixed team selection on user administration page

--- app/assets/javascripts/main/routing.coffee
+++ app/assets/javascripts/main/routing.coffee
@@ -120,7 +120,6 @@

     "admin.user.userAdministration" : ->
       require ["multiselect"], ->
-        $(".multiselect").multiselect()

         $popovers = $("a[rel=popover]")
         template = """
@@ -166,6 +165,7 @@
           $modal.find(".modal-body").html(template.html())
           $modal.find(".modal-header h3").text(title)
           $modal.find(".modal-hide").on "click", -> $modal.modal("hide")
+          $modal.find(".multiselect").multiselect()

           $modal.modal("show")


properly disable onclick

properly disable onclick

--- app/assets/javascripts/mtl/dropdown.coffee
+++ app/assets/javascripts/mtl/dropdown.coffee
@@ -1,5 +1,7 @@
 updateAttrs = ($el) ->
-  $el.attr('data-constrainwidth', 'false')
+  $el
+    .on('click', -> false)
+    .attr('data-constrainwidth', 'false')
     .attr('data-beloworigin', 'true')
     .attr('data-alignment', alignment($el))
     .attr('data-activates', activator($el))


bugfix: remove joinRoot that was concatenating paths

--- app/assets/javascripts/workflows.js.coffee
+++ app/assets/javascripts/workflows.js.coffee
@@ -12,7 +12,7 @@
     show_job_panel()
     $.ajax
       type: 'GET'
-      url: joinRoot(Routes.workflow_path(id))
+      url: Routes.workflow_path(id)
       contentType: "application/json; charset=utf-8"
       dataType: "json"
       error: (jqXHR, textStatus, errorThrown) ->
@@ -88,7 +88,7 @@

 @update_copy_button = (id) ->
   if id?
-    $("#copy_button").attr("href", joinRoot(Routes.copy_workflow_path(id)))
+    $("#copy_button").attr("href", Routes.copy_workflow_path(id))
     $("#copy_button").data("method", "PUT")
     $("#copy_button").removeAttr("disabled")
     $("#copy_button").unbind('click', false)
@@ -99,7 +99,7 @@

 @update_edit_button = (id) ->
   if id?
-    $("#edit_button").attr("href", joinRoot(Routes.edit_workflow_path(id)))
+    $("#edit_button").attr("href", Routes.edit_workflow_path(id))
     $("#edit_button").removeAttr("disabled")
     $("#edit_button").unbind('click', false)
   else
@@ -109,7 +109,7 @@

 @update_submit_button = (id, status_char) ->
   if id? && !status_char?
-      $("#submit_button").attr("href", joinRoot(Routes.submit_workflow_path(id)))
+      $("#submit_button").attr("href", Routes.submit_workflow_path(id))
       $("#submit_button").data("method", "PUT")
       $("#submit_button").removeAttr("disabled")
       $("#submit_button").unbind('click', false)
@@ -120,7 +120,7 @@

 @update_stop_button = (id, status_char) ->
   if id? && status_char? && (status_char == "R" || status_char == "Q")
-    $("#stop_button").attr("href", joinRoot(Routes.stop_workflow_path(id)))
+    $("#stop_button").attr("href", Routes.stop_workflow_path(id))
     $("#stop_button").data("method", "PUT")
     $("#stop_button").removeAttr("disabled")
     $("#stop_button").unbind('click', false)
@@ -132,7 +132,7 @@
 @update_template_button = (id) ->
   if id?
     params = { jobid: "#{id}" }
-    $("#template_button").attr("href", joinRoot(Routes.new_template_path( params )))
+    $("#template_button").attr("href", Routes.new_template_path( params ))
     $("#template_button").removeAttr("disabled")
     $("#template_button").unbind('click', false)
   else
@@ -142,7 +142,7 @@

 @update_destroy_button = (id) ->
   if id?
-    $("#destroy_button").attr("href", joinRoot(Routes.workflow_path(id)))
+    $("#destroy_button").attr("href", Routes.workflow_path(id))
     $("#destroy_button").data("method", "DELETE")
     $("#destroy_button").removeAttr("disabled")
     $("#destroy_button").unbind('click', false)


Added focuspoint snippets

Added focuspoint snippets

--- snippets/addons.cson
+++ snippets/addons.cson
@@ -28,3 +28,21 @@
     \t${3}
     )$0
     """
+'.text.html.ss .variable':
+  # Focuspoint 3.2
+  'Focus Fill Image 3.2+':
+    'prefix': 'focusfill'
+    'body': 'FocusFill(${1:width},${2:${1:height}})${0}'
+  'Focus Fill Max Image 3.2+':
+    'prefix': 'focusfillmax'
+    'body': 'FocusFillMax(${1:width},${2:${1:height}})${0}'
+  'Focus Crop Width Image 3.2+':
+    'prefix': 'focuscropwidth'
+    'body': 'FocusCropWidth(${1:width})${0}'
+  'Focus Crop Height Image 3.2+':
+    'prefix': 'focuscropheight'
+    'body': 'FocusCropHeight(${1:width})${0}'
+  # Focuspoint 3.0 - 3.1
+  'Cropped Focused Image 3.0 - 3.1':
+    'prefix': 'croppedfocusedimage'
+    'body': 'CroppedFocusedImage(${1:width},${2:${1:height}})${0}'


Sprite is implicit

Sprite is implicit

--- app/scripts/components/entities/Sun.coffee
+++ app/scripts/components/entities/Sun.coffee
@@ -8,7 +8,7 @@
       bigGlare: [0, 256, 200, 200]
     })

-    @requires '2D, Canvas, Sprite, Choreography, ViewportFixed, Collision, sun'
+    @requires '2D, Canvas, Choreography, ViewportFixed, Collision, sun'

     @attr(w: 20, h: 20, z: -1000)


ignore eslint when no eslintrc

ignore eslint when no eslintrc

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -1,28 +1,30 @@
-'global':
-  'welcome':
-    'showOnStartup': false
-  'editor':
-    'fontFamily': 'Consolas, Inconsolata, Monaco, Courier New, Courier'
-    'fontSize': 14
-    'invisibles':
-      'tab': '→'
-    'showInvisibles': true
-  'core':
-    'themes': [
-      'neutron-ui'
-      'leaf-syntax'
+"*":
+  welcome:
+    showOnStartup: false
+  editor:
+    fontFamily: "Consolas, Inconsolata, Monaco, Courier New, Courier"
+    fontSize: 14
+    invisibles:
+      tab: "→"
+    showInvisibles: true
+  core:
+    themes: [
+      "neutron-ui"
+      "leaf-syntax"
     ]
-    'disabledPackages': [
-      'metrics'
-      'tabs'
-      'tree-view'
+    disabledPackages: [
+      "metrics"
+      "tabs"
+      "tree-view"
     ]
-  'tabs':
-    'showIcons': true
-  'tree-view':
-    'hideVcsIgnoredFiles': false
-    'hideIgnoredNames': false
-    'showOnRightSide': false
-    'sortFoldersBeforeFiles': true
-  'file-icons':
-    'coloured': false
+  tabs:
+    showIcons: true
+  "tree-view":
+    hideVcsIgnoredFiles: false
+    hideIgnoredNames: false
+    showOnRightSide: false
+    sortFoldersBeforeFiles: true
+  "file-icons":
+    coloured: false
+  "linter-eslint":
+    disableWhenNoEslintrcFileInPath: true


Use 'source.shell' Atom file type for all files ending in `.muttrc`.

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -69,7 +69,7 @@
     "^.mailcap": "source.shell"
     "mutt_account": "source.shell"
     "mutt_provider": "source.shell"
-    "^muttrc": "source.shell"
+    ".*muttrc": "source.shell"
     "^.profile$": "source.shell"
     "^.psqlrc": "source.sql"
     "^.rspec$": "text.generic-config"


Update Atom

Update Atom

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -12,7 +12,6 @@
     'showIndentGuide': true
     'fontFamily': 'Ubuntu Mono'
     'fontSize': 14
-    'invisibles': {}
   'core':
     'themes': [
       'atom-dark-ui'
@@ -68,7 +67,6 @@
     'ensureSingleTrailingNewline': false
   'jsformat':
     'format_on_save': false
-  'atom-lint': {}
 '.ftl.html.text':
   'editor': {}
 '.basic.html.text':


Remove mongoose-troop from deps and code

--- servers/api/lib/database.coffee
+++ servers/api/lib/database.coffee
@@ -1,5 +1,4 @@
 mongoose = require("mongoose")
-pagination = require("mongoose-troop-pagination")
 nconf = require("nconf")
 mime = require("mime")
 url = require("url")


Still getting grid error

Still getting grid error

--- src/draw.coffee
+++ src/draw.coffee
@@ -28,4 +28,4 @@
     class: "svg_grid #{world}"
     width: grid_size
     height: grid_size
-  }#, world.map_child draw_world
+  }, world.map_child draw_world


Subscribe to grammars-loaded event in editor

Previously an error would be logged if this event fired
after an editor had been removed.

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -417,7 +417,7 @@
       @gutter.widthChanged = (newWidth) =>
         @scrollView.css('left', newWidth + 'px')

-      syntax.on 'grammars-loaded', =>
+      @subscribe syntax, 'grammars-loaded', =>
         @reloadGrammar()
         for session in @editSessions
           session.reloadGrammar() unless session is @activeEditSession


More updates

More updates

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -225,7 +225,10 @@
   # position - An {Array} of two numbers: the screen row, and the screen column.
   # options - An object with the following properties:
   setCursorScreenPosition: (position, options) -> @activeEditSession.setCursorScreenPosition(position, options)
+  # Public: Duplicates the current line.
   duplicateLine: -> @activeEditSession.duplicateLine()
+  # Public: Duplicates the current line.
+
   getCursorScreenPosition: -> @activeEditSession.getCursorScreenPosition()
   getCursorScreenRow: -> @activeEditSession.getCursorScreenRow()
   setCursorBufferPosition: (position, options) -> @activeEditSession.setCursorBufferPosition(position, options)


comment review

comment review

--- src/arguegraph.coffee
+++ src/arguegraph.coffee
@@ -23,8 +23,7 @@

 # Abstract argumentation involves manipulating sets of arguments so first we
 # define some set operations on arrays that are assumed not to include dups or
-# gaps. Note that these algorithms, especially this naïve library's approach can
-# get computationally expensive.
+# gaps.

 # returns the array of all sub-arrays of S, see https://gist.github.com/joyrexus/5423644
 powerset = (S) ->
@@ -53,19 +52,20 @@
     result.push el if el in B
   result

-# returns an array whose elements are in both arrays A and B
+# returns an array whose elements are in either array A or B
 union = (A, B) ->
   result = A
   for el in B
     result.push el if not (el in A)
   result

-# An ArgumentFramework wraps a map of defeats* that define the argument network
+# An ArgumentFramework wraps a map of defeats* that defines the argument network
 # and provides functions for interrogating that network.
 # Errors are thrown for inconsistent/malformed networks and queries
-# * - Note that the word "defeats" is used widely in the literature, but it may
+#
+# * The word "defeats" is used widely in the literature, but it may
 # not be the best term to use, as it suggests a resolved struggle whereas in
-# this formalisation it is possible for two arguments to simultaneously defeat
+# this formalisation it is possible for two arguments to simultaneously "defeat"
 # one another. It is the job of the algorithms to establish which arguments can
 # be said to defend themselves.  Alternative terms that could be used here
 # include "attack" and "conflict with".


Fix constructor documentation

Fix constructor documentation

--- src/file.coffee
+++ src/file.coffee
@@ -26,7 +26,7 @@
   # Public: Configures a new File instance, no files are accessed.
   #
   # * `filePath` A {String} containing the absolute path to the file
-  # * `symlink` A {Boolean} indicating if the path is a symlink (default: false).
+  # * `symlink` (optional) A {Boolean} indicating if the path is a symlink (default: false).
   constructor: (filePath, @symlink=false, includeDeprecatedAPIs=Grim.includeDeprecatedAPIs) ->
     filePath = path.normalize(filePath) if filePath
     @path = filePath


Fix OVH konnector (display properly fields)

--- client/app/models/konnector.coffee
+++ client/app/models/konnector.coffee
@@ -11,14 +11,17 @@
         fields = @get 'fields'

         # Remove fields where no value is expected.
-        delete fieldValues.loginUrl
-        delete fields.loginUrl

         # Check if there is an empty field
         numFieldValues = Object.keys(fieldValues).length
         numFields = Object.keys(fields).length
+
+        numFieldValues-- if fieldValues.loginUrl
+        numFields-- if fields.loginUrl
+
         noEmptyValue = true
         for field, fieldValue of fields
-            noEmptyValue = noEmptyValue and fieldValues[field]?.length > 0
+            if field isnt 'loginUrl'
+                noEmptyValue = noEmptyValue and fieldValues[field]?.length > 0

         return numFieldValues >= numFields and noEmptyValue


Added test stacksSkipSetupGuide

Added test stacksSkipSetupGuide

--- client/test/lib/teams/teams.coffee
+++ client/test/lib/teams/teams.coffee
@@ -48,3 +48,25 @@
     teamsHelpers.loginTeam(browser)
     teamsHelpers.createStack(browser)
     browser.end()
+
+
+  stacksSkipSetupGuide: (browser) ->
+
+    stackCreatePage       = '.Group-Stack-Templates .get-started'
+    getStartedButton      = "#{stackCreatePage} .header button.green"
+    skipSetupSelector     = '.footer .skip-setup'
+    stackTemplateSelector = '.kdtabhandlecontainer.hide-close-icons .stack-template'
+    saveAndTestButton     = '.buttons button:nth-of-type(5)'
+
+    teamsHelpers.loginTeam(browser)
+    teamsHelpers.startStackCreate(browser)
+
+    browser
+      .waitForElementVisible  getStartedButton, 20000
+      .click                  getStartedButton
+      .waitForElementVisible  skipSetupSelector, 20000
+      .click                  skipSetupSelector
+      .waitForElementVisible  stackTemplateSelector, 20000
+      .assert.containsText    stackTemplateSelector, 'Stack Template'
+      .assert.containsText    saveAndTestButton, 'SAVE & TEST'
+


Modify output layout

Modify output layout

--- src/main.coffee
+++ src/main.coffee
@@ -21,7 +21,9 @@
         else if rawCodeType == "json"
             """
                 module.exports =
+
                 #{rawCode}
+
                 ;

             """


add RGBA support, fixes #1

add RGBA support, fixes #1

--- src/main.coffee
+++ src/main.coffee
@@ -28,7 +28,7 @@
     pixels = []
     i = 0
     while( i < imgData.data.length )
-      pixels.push [imgData.data[i], imgData.data[i+1], imgData.data[i+2]]
+      pixels.push [imgData.data[i], imgData.data[i+1], imgData.data[i+2],imgData.data[i+3]]
       i += 4
     callback?(calcClusters(pixels, config))
   img.src = config.src
@@ -47,10 +47,19 @@
       score += Math.pow(calcDistance(lab, guess), 2)
     score * -1

+  # get 空间平均数
+  # 在此基础修改分量，若 +1 增大，则继续 +1, 若 +1 减小，则 -1，若方向不一致则停止
+
 # pixels should be [[r, g, b], ...]
 calcClusters = (pixels, config) ->
   # convert to lab
-  pixels = pixels.map (rgb) -> color.rgb2lab(rgb)
+  pixels = pixels.map (rgba) ->
+    [r, g, b, a] = rgba
+    rgb = [r, g, b]
+    if a isnt 255
+      a /= 255
+      rgb = rgb.map (elem) -> 255 * (1 - a) + elem * a
+    color.rgb2lab(rgb)
   # init seeds
   centers = seeds.map (rgb) -> color.rgb2lab(rgb)
   # define iter


show launcher after getting rid of windows

--- src/main.coffee
+++ src/main.coffee
@@ -95,7 +95,6 @@

     window.on 'closed', ->
       return unless windows?
-      Launcher.show primaryDisplay
       _windows = windows
       windows = null
       _windows.forEach (w) ->
@@ -104,6 +103,7 @@
         catch ex
           'nothing'
       # app.quit()
+      Launcher.show primaryDisplay

     windows.push window


cleanup

cleanup


fix cant fill density number when query advance mode

--- src/maps.coffee
+++ src/maps.coffee
@@ -136,7 +136,7 @@
     unless $('#search-box:focus').length
         if event.which == 47 # /
             $('#search-box').focus()
-        event.preventDefault()
+            event.preventDefault()

 $(document).ready ->


Add missing highlighting for brackets and commas

--- grammars/viml.cson
+++ grammars/viml.cson
@@ -195,10 +195,15 @@

 			{match: "(?<=\\S)!", name: "storage.modifier.force.viml"}
 			{match: "!(?=\\S)",  name: "keyword.operator.logical.not.viml"}
+			{match: "{",         name: "punctuation.expression.bracket.curly.begin.viml"}
+			{match: "}",         name: "punctuation.expression.bracket.curly.end.viml"}
+			{match: "\\[",       name: "punctuation.expression.bracket.square.begin.viml"}
+			{match: "\\]",       name: "punctuation.expression.bracket.square.end.viml"}
 			{match: "\\(",       name: "punctuation.expression.bracket.round.begin.viml"}
 			{match: "\\)",       name: "punctuation.expression.bracket.round.end.viml"}
 			{match: "\\|",       name: "punctuation.separator.statement.viml"}
-			{match: ":",         name: "punctuation.separator.key-value.viml"}
+			{match: ",",         name: "punctuation.separator.comma.viml"}
+			{match: ":",         name: "punctuation.separator.colon.viml"}
 			{match: "\\.?=",     name: "keyword.operator.assignment.viml"}
 			{match: "\\.",       name: "punctuation.delimiter.property.dot.viml"}
 		]


Cleanup

Cleanup

--- grunt/shrub.coffee
+++ grunt/shrub.coffee
@@ -1,4 +1,3 @@
-path = require 'path'

 module.exports = (grunt, config) ->


Build be green!

Build be green!

--- core/app/backbone/views/fact_relations_view.coffee
+++ core/app/backbone/views/fact_relations_view.coffee
@@ -48,8 +48,8 @@
       collection: @model.getInteractors()

     # Todo: add to initialize?
-    @commentsRegion.show new CommentsListView
-      collection: @model.comments()
+    # @commentsRegion.show new CommentsListView
+    #   collection: @model.comments()

     if @model.relations()
       @factRelationSearchRegion.show new AddEvidenceView


Updated udefine configuration

Updated udefine configuration

--- udefine/config.coffee
+++ udefine/config.coffee
@@ -1,4 +1,3 @@
-do (root = @, name = 'mixer') ->
-  root.udefine.globals[name] = root[name]
-
-  root.udefine.inject[name] = {name, root}+do (name = 'mixer') ->
+  udefine.configure (root) ->
+    root.udefine.inject.add name


:lipstick:

:lipstick:

--- spec/git-spec.coffee
+++ spec/git-spec.coffee
@@ -190,15 +190,15 @@

     describe 'when a local reference exists', ->
       it 'checks the branch out', ->
-        expect(repo.checkoutReference("refs/heads/getHeadOriginal")).toBe true
+        expect(repo.checkoutReference('refs/heads/getHeadOriginal')).toBe true
         expect(repo.getHead()).toBe 'refs/heads/getHeadOriginal'

     describe 'when a local reference doesn\'t exist', ->
       it 'does nothing if branch creation was not specified', ->
-        expect(repo.checkoutReference("refs/heads/whoop-whoop")).toBe false
+        expect(repo.checkoutReference('refs/heads/whoop-whoop')).toBe false

       it 'creates the new branch (if asked to)', ->
-        expect(repo.checkoutReference("refs/heads/whoop-whoop", true)).toBe true
+        expect(repo.checkoutReference('refs/heads/whoop-whoop', true)).toBe true
         expect(repo.getHead()).toBe 'refs/heads/whoop-whoop'

   describe '.checkoutHead(path)', ->


Timestamps adjusted into correct format

Timestamps adjusted into correct format


Adding support for Brightscript

Adding support for Brightscript

The Brightscript is the language created by Roku to allow create channels for their streaming devices:
https://sdkdocs.roku.com/display/sdkdoc/BrightScript+Language+Reference

It's a type of VisualBasic

--- src/sloc.coffee
+++ src/sloc.coffee
@@ -37,7 +37,7 @@
         /--/
       when "erl"
         /\%/
-      when "monkey", "vb"
+      when "brs", "monkey", "vb"
         /'/
       when "nim"
         r =
@@ -245,6 +245,7 @@

 extensions = [
   "asm"
+  "brs"
   "c"
   "cc"
   "clj"


join predicate

join predicate

--- src/grammar.coffee
+++ src/grammar.coffee
@@ -96,13 +96,21 @@
   ]

   Join: [
-    o 'JOIN Table ON Expression',                         -> new Join($2, $4)
-    o 'LEFT JOIN Table ON Expression',                    -> new Join($3, $5, 'LEFT')
-    o 'RIGHT JOIN Table ON Expression',                   -> new Join($3, $5, 'RIGHT')
-    o 'LEFT INNER JOIN Table ON Expression',              -> new Join($4, $6, 'LEFT', 'INNER')
-    o 'RIGHT INNER JOIN Table ON Expression',             -> new Join($4, $6, 'RIGHT', 'INNER')
-    o 'LEFT OUTER JOIN Table ON Expression',              -> new Join($4, $6, 'LEFT', 'OUTER')
-    o 'RIGHT OUTER JOIN Table ON Expression',             -> new Join($4, $6, 'RIGHT', 'OUTER')
+    o 'JOIN Table JoinPredicate',                         -> new Join($2, $3)
+    o 'INNER JOIN Table JoinPredicate',                   -> new Join($2, $4)
+    o 'LEFT JOIN Table JoinPredicate',                    -> new Join($3, $4, 'LEFT')
+    o 'RIGHT JOIN Table JoinPredicate',                   -> new Join($3, $4, 'RIGHT')
+    o 'LEFT INNER JOIN Table JoinPredicate',              -> new Join($4, $5, 'LEFT', 'INNER')
+    o 'RIGHT INNER JOIN Table JoinPredicate',             -> new Join($4, $5, 'RIGHT', 'INNER')
+    o 'LEFT OUTER JOIN Table JoinPredicate',              -> new Join($4, $5, 'LEFT', 'OUTER')
+    o 'RIGHT OUTER JOIN Table JoinPredicate',             -> new Join($4, $5, 'RIGHT', 'OUTER')
+    o 'FULL OUTER JOIN Table JoinPredicate',              -> new Join($4, $5, 'FULL', 'OUTER')
+    o 'FULL JOIN Table JoinPredicate',                    -> new Join($3, $4, 'FULL')
+  ]
+
+  JoinPredicate: [
+    o 'ON Expression',                                 -> $2
+    o 'USING LEFT_PAREN Literal RIGHT_PAREN',           -> new Using($3)
   ]

   WhereClause: [


:memo: Mention apm -v shows the Git version

--- src/install.coffee
+++ src/install.coffee
@@ -173,7 +173,7 @@

             The #{pack.name} package has module dependencies that cannot be installed without Git.

-            You need install Git and make it available on your PATH environment variable in order to install this package.
+            You need install Git and make it available on your path environment variable in order to install this package.
           """
           switch process.platform
             when 'win32'
@@ -186,6 +186,11 @@

                 You can install Git from your OS package manager.
               """
+
+          output += """
+
+            Run apm -v after installing Git to see what version has been detected.
+          """

         callback(output)


Add i as an alias for install

Add i as an alias for install

Signed-off-by: Michelle Tilley <19f7a7eb6786def4c39d6d86c40af5dbc6589663@github.com>

--- src/install.coffee
+++ src/install.coffee
@@ -16,7 +16,7 @@

 module.exports =
 class Install extends Command
-  @commandNames: ['install']
+  @commandNames: ['install', 'i']

   constructor: ->
     @atomDirectory = config.getAtomDirectory()
@@ -34,6 +34,7 @@
              apm install <git_remote>
              apm install <github_username>/<github_project>
              apm install --packages-file my-packages.txt
+             apm i (with any of the previous argument usage)

       Install the given Atom package to ~/.atom/packages/<package_name>.


:lipstick: Rename variables for clarity

:lipstick: Rename variables for clarity

--- src/install.coffee
+++ src/install.coffee
@@ -326,19 +326,19 @@

     @createAtomDirectories()

-    installPackage = (name, callback) =>
-      if name is '.'
+    installPackage = (packageName, callback) =>
+      if packageName is '.'
         @installDependencies(options, callback)
       else
-        atIndex = name.indexOf('@')
+        atIndex = packageName.indexOf('@')
         if atIndex > 0
-          version = name.substring(atIndex + 1)
-          name = name.substring(0, atIndex)
-        @installPackage({name, version}, options, callback)
+          version = packageName.substring(atIndex + 1)
+          packageName = packageName.substring(0, atIndex)
+        @installPackage({packageName, version}, options, callback)

     commands = []
-    names = _.uniq(options.argv._)
-    names.push('.') if names.length is 0
-    names.forEach (name) ->
-      commands.push (callback) -> installPackage(name, callback)
+    packageName = _.uniq(options.argv._)
+    packageName.push('.') if packageName.length is 0
+    packageName.forEach (packageName) ->
+      commands.push (callback) -> installPackage(packageName, callback)
     async.waterfall(commands, callback)


Fixed parsing param and returns tag description bug.

--- src/chalkboard.coffee
+++ src/chalkboard.coffee
@@ -194,7 +194,7 @@
               when "param"
                 argObject.description = argsMatch[3] or ""
                 argObject.name        = argsMatch[2]
-              when "return"
+              when "returns"
                 argObject.description = argsMatch[2] or ""

             value = null
@@ -220,6 +220,20 @@

     else
       if hasComment and not _(currentSection).isEmpty()
+
+        # Check if there is remaining argObject to be cleared
+        if multiLineKey and not _(argObject).isEmpty()
+          do (multiLineKey, currentSection, argObject, definitions) ->
+            keys = multiLineKey.split "."
+            _setAttribute(currentSection,
+              keys[0],
+              argObject,
+              definitions[keys[0]]
+            )
+
+          multiLineKey = ""
+          argObject    = {}
+
         allSections.push currentSection
         currentSection = {}


Allow to set host for API.

Allow to set host for API.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -4,6 +4,13 @@
 OrderDistribution = require '../lib/orderdistribution'
 argv = require('optimist')
   .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret --logDir dir --logLevel level --timeout timeout')
+  .describe('projectKey', 'your SPHERE.IO project-key')
+  .describe('clientId', 'your SPHERE.IO OAuth client id')
+  .describe('clientSecret', 'your SPHERE.IO OAuth client secret')
+  .describe('timeout', 'timeout for requests')
+  .describe('sphereHost', 'SPHERE.IO API host to connecto to')
+  .describe('logLevel', 'log level for file logging')
+  .describe('logDir', 'directory to store logs')
   .default('logLevel', 'info')
   .default('logDir', '.')
   .default('timeout', 60000)
@@ -28,6 +35,8 @@
     client_id: argv.clientId
     client_secret: argv.clientSecret

+options.baseConfig.host = argv.sphereHost if argv.sphereHost?
+
 impl = new OrderDistribution options
 impl.getUnSyncedOrders(impl.masterRest).then (orders) ->
   impl.run orders, (msg) ->


Make work with line numbers

Make work with line numbers

--- src/coffeelint.coffee
+++ src/coffeelint.coffee
@@ -402,6 +402,7 @@
     lintToken : (token) ->
         [type, value, lineNumber] = token

+        lineNumber = lineNumber["first_line"] if lineNumber["first_line"]?
         @tokensByLine[lineNumber] ?= []
         @tokensByLine[lineNumber].push(token)
         # CoffeeScript loses line numbers of interpolations and multi-line


Disable missing rule error.

Disable missing rule error.

It was definitely a mistake. When you run CoffeeLint as a plugin in your
editor it's not always possible to see what the error is.

--- src/coffeelint.coffee
+++ src/coffeelint.coffee
@@ -178,10 +178,11 @@
     # Why can't JSON just support comments?
     for name of userConfig when name not in [ 'coffeescript_error', '_comment' ]
         unless _rules[name]?
-            console.log 'userConfig', userConfig
-            # If you have a typo in your coffeelint.json you may not notice
-            # until you spot a mistake you inteded CoffeeLint to catch.
-            throw new Error "Rule #{name} was not found."
+            # TODO: Figure out a good way to notify the user that they have
+            # configured a rule that doesn't exist. throwing an Error was
+            # definitely a mistake. I probably need a good way to generate lint
+            # warnings for configuration.
+            undefined

     config = mergeDefaultConfig(userConfig)


fixed bug

fixed bug

--- src/wrap.coffee
+++ src/wrap.coffee
@@ -181,7 +181,7 @@
         if typeof ext is 'object'
             fn = ext.wrapper
             ext = ext.extension
-        else if fn
+        if fn
             @extensions.push(ext)
             @filters.push (body, file) =>
                 if file.slice(-ext.length) is ext


incomplete: commit progress

incomplete: commit progress

--- src/xslt.coffee
+++ src/xslt.coffee
@@ -2,11 +2,88 @@
   if (typeof define == 'function' && define.amd?)
     # AMD
     define([], factory)
-  else if (typeof exports == 'object')
+  else if (typeof module?.exports == 'object')
     # CommonJS
     module.exports = factory()
   else
     # global
-    root.xslt = factory()
+    root?.xslt = factory()
 ) this, ->
-  return {}
+
+  isXml = (str) -> /^\s*</.test(str)
+  hasXmlHeader = (str) -> /^\s*<\?/.test(str)
+  needsHeader = (str) -> isXml(str) && !hasXmlHeader(str)
+  xmlHeader = '<?xml version="1.0" ?>'
+  prependHeader = (str) -> xmlHeader + str
+  activeXSupported = ActiveXObject? || 'ActiveXObject' in window
+
+  tryCreateActiveX = (objIds...) ->
+    return null unless activeXSupported
+    for id in objIds
+      try
+        return new ActiveXObject(id)
+    return null
+
+  createDomDoc = ->
+    d = tryCreateActiveX([
+      "Msxml2.FreeThreadedDOMDocument.6.0"
+      "Msxml2.FreeThreadedDOMDocument.3.0"
+      "Msxml2.FreeThreadedDOMDocument"
+      "Microsoft.XMLDOM"
+      "Msxml2.DOMDocument.6.0"
+      "Msxml2.DOMDocument.5.0"
+      "Msxml2.DOMDocument.4.0"
+      "Msxml2.DOMDocument.3.0"
+      "MSXML2.DOMDocument"
+      "MSXML.DOMDocument"
+    ]...)
+    if d?
+      d.async = false
+      null while d.readyState != 4
+    return d
+
+  createXSLTemplate = -> tryCreateActiveX([
+    'Msxml2.XSLTemplate.6.0'
+    'Msxml2.XSLTemplate'
+  ]...)
+
+  manualCreateElement = ->
+    xml = document.createElement('xml')
+    xml.src = xmlHeader
+    document.body.appendChild(xml)
+    res = xml.XMLDocument
+    document.body.removeChild(xml)
+    return res
+
+  newDocument = ->
+    d = null
+    d ?= new DOMParser?()
+    d ?= createDomDoc()
+    d ?= manualCreateElement()
+    d ?= document.implementation?.createDocument?("", 'test', null);
+    return d
+
+
+  strToDoc = (str) ->
+    return null if (typeof str != 'string') or !isXml(str)
+    str = prependHeader(str) if needsHeader(str)
+
+    d = newDocument()
+    if d?.loadXML?
+      d.loadXML(str)
+      if !d.documentElement? || d.parseError?.errorCode != 0
+        throw "loadXML error: #{d.parseError}"
+    else if d?.load?
+      d.load(str)
+    else if d?.parseFromString?
+      d = d.parseFromString(str, 'text/xml')
+      if d?.getElementsByTagName?('parsererror')?.length > 0 || d?.documentElement?.tagName == 'parsererror'
+        throw "Failed to load document from string:\r\n#{d.documentElement.textContent}"
+    return d
+
+
+  return (xmlStr, xsltStr) ->
+
+    if XSLTProcessor? and document?.implementation?.createDocument?
+      processor = new XSLTProcessor()
+


opt: type check

opt: type check

--- src/yaku.coffee
+++ src/yaku.coffee
@@ -208,7 +208,7 @@
 	 * ```
 	###
 	@onUnhandledRejection: (reason, p) ->
-		return if typeof console != $object
+		return if not isObject console

 		$info = 'Unhandled Rejection:'

@@ -255,9 +255,12 @@
 	$tryCatchFn = null
 	$tryErr = { e: null }
 	$noop = {}
-	$function = 'function'
-	$object = 'object'
-	$isNode = typeof process == $object
+
+	isObject = (obj) ->
+		typeof obj == 'object'
+
+	isFunction = (obj) ->
+		typeof obj == 'function'

 	###*
 	 * Release the specified key of an object.
@@ -457,9 +460,9 @@
 	###
 	addHandler = (p1, p2, onFulfilled, onRejected) ->
 		# 2.2.1
-		if typeof onFulfilled == $function
+		if isFunction onFulfilled
 			p2._onFulfilled = onFulfilled
-		if typeof onRejected == $function
+		if isFunction onRejected
 			p2._onRejected = onRejected

 		p1[$hasUnhandledRejection] = false
@@ -560,8 +563,7 @@

 		# 2.3.2
 		# 2.3.3
-		type = typeof x
-		if x != null and (type == $function or type == $object)
+		if x != null and (isFunction(x) or isObject(x))
 			# 2.3.2.1
 			xthen = genTryCatcher(getThen) x

@@ -570,7 +572,7 @@
 				settlePromise p, $rejected, xthen.e
 				return

-			if typeof xthen == $function
+			if isFunction xthen
 				if isLongStackTrace
 					p[$prePromise] = x


add option to bind to specific local address

--- src/connection.coffee
+++ src/connection.coffee
@@ -447,7 +447,15 @@
   connectOnPort: (port) ->
     @socket = new Socket({})
     @socket.setKeepAlive(true, KEEP_ALIVE_INITIAL_DELAY)
-    @socket.connect(port, @config.server)
+
+    connectOpts =
+      host: @config.server
+      port: port
+
+    if @config.options.localAddress
+      connectOpts.localAddress = @config.options.localAddress
+
+    @socket.connect(connectOpts)
     @socket.on('error', @socketError)
     @socket.on('connect', @socketConnect)
     @socket.on('close', @socketClose)


Reparse before instead of after click to avoid duplication bug

--- src/controller.coffee
+++ src/controller.coffee
@@ -1027,6 +1027,8 @@
     if @handleTextInputClick mainPoint, dropletDocument
       state.consumedHitTest = true
       return
+    else if @cursor.document is i and @cursorAtSocket()
+      @setCursor @cursor, ((token) -> token.type isnt 'socketStart')

     hitTestResult = @hitTest mainPoint, dropletDocument

@@ -1071,9 +1073,6 @@

         @redrawMain()
         return
-
-  if @cursorAtSocket()
-    @setCursor @cursor, ((token) -> token.type isnt 'socketStart')

 # If the user lifts the mouse
 # before they have dragged five pixels,


Add class droplet-gutter-line to transition elements for styling convenience

--- src/controller.coffee
+++ src/controller.coffee
@@ -3275,7 +3275,7 @@
       div.style.width = "#{@gutter.offsetWidth}px"
       translatingElements.push div

-      div.className = 'droplet-transitioning-element droplet-transitioning-gutter'
+      div.className = 'droplet-transitioning-element droplet-transitioning-gutter droplet-gutter-line'
       # Add annotation
       if @annotations[line]?
         div.className += ' droplet_' + getMostSevereAnnotationType(@annotations[line])


Add newline at end of file

Add newline at end of file

--- src/controller.coffee
+++ src/controller.coffee
@@ -3170,7 +3170,17 @@

     @setValue_raw value

-  Editor::getValue = -> if @currentlyUsingBlocks then @tree.stringify() else @aceEditor.getValue()
+  Editor::addEmptyLine = (str) ->
+    if str.length is 0 or str[str.length - 1] is '\n'
+      return str
+    else
+      return str + '\n'
+
+  Editor::getValue = ->
+    if @currentlyUsingBlocks
+      return @addEmptyLine @tree.stringify()
+    else
+      @aceEditor.getValue()

   # PUBLIC EVENT BINDING HOOKS
   # ===============================


Fixed a bug with Events.off

Fixed a bug with Events.off

--- src/core/chart.coffee
+++ src/core/chart.coffee
@@ -17,7 +17,7 @@
   # @param [Function, String] callback (Optional) Callback to remove from the listener list.
   #   If this parameter is not provided then all listeners will be removed for the event.
   off: (name, callback) ->
-    return unless Epoch.isObject(@_events[name])
+    return unless Epoch.isArray(@_events[name])
     return delete(@_events[name]) unless callback?
     while (i = @_events[name].indexOf(callback)) >= 0
       @_events[name].splice(i, 1)


Fix indentation for src/core/stats.coffee.

--- src/core/stats.coffee
+++ src/core/stats.coffee
@@ -22,87 +22,88 @@
 {Errors}    = require './errors'

 class _Stats
-        constructor: () ->
+    constructor: () ->

-        renderStats : (res, options) =>
-            res.render 'stats', options
+    # Render the stats page.
+    renderStats : (res, options) =>
+        res.render 'stats', options

-        insertNewDate : (req, res, body, callback, timestamp) =>
-            Request.post {
-                json    :
-                    c_date : timestamp
-                    nb_user_tmp : 1
-                url     : "#{Config.Restapi.Url}/stat"
-                }, (e, r, body) =>
+    insertNewDate : (req, res, body, callback, timestamp) =>
+        Request.post {
+            json    :
+                c_date : timestamp
+                nb_user_tmp : 1
+            url     : "#{Config.Restapi.Url}/stat"
+        }, (e, r, body) =>
+            if e? or r.statusCode > 200
+                Errors.RenderNotFound req, res
+            else
+                callback req, res
+
+    updateNbrTmpUser : (req, res, body, callback) =>
+        body = body[0]
+        body.nb_user_tmp += 1
+        Request.put {
+            json    : body
+            url     : "#{Config.Restapi.Url}/stat/#{body.id}"
+        }, (e, r, body) =>
+            if e? or r.statusCode > 200
+                Errors.RenderNotFound req, res
+            else
+                callback req, res
+
+    countTmpUser : (req, res, callback) =>
+        date = Moment().format "YYYY-MM-DD"
+        timestamp = String(do (new Date(date)).getTime)
+        Request.get "#{Config.Restapi.Url}/stat?c_date=#{timestamp}", (e, r, body) =>
+            if e? or r.statusCode > 200
+                Errors.RenderNotFound req, res
+            else
+                body = JSON.parse body
+                if body.length is 0
+                    @insertNewDate req, res, body, callback, timestamp
+                else
+                    @updateNbrTmpUser req, res, body, callback
+
+    getNumberOfRoomsPerDay: (req, res, body) =>
+        body = JSON.parse body
+        data = {}
+        for rooms in body
+            date = Moment.unix rooms.c_date / 1000
+            key = date.format "YYYY-MM-DD"
+            timestamp = do (new Date(key)).getTime
+            if data[timestamp]?
+                data[timestamp] = data[timestamp] += 1
+            else
+                data[timestamp] = 1
+        tmp = []
+        for timestamp, nb of data
+            tmp.push [Number(timestamp), nb]
+        return tmp
+
+    getNumberOfTmpUsersPerDay: (req, res, body) =>
+        body = JSON.parse body
+        data = []
+        for users in body
+            data.push [Number(users.c_date), users.nb_user_tmp]
+        return data
+
+    statsPage : (req, res) =>
+        Request.get "#{Config.Restapi.Url}/room", (e, r, body) =>
+            if e? or r.statusCode > 200
+                Errors.RenderNotFound req, res
+            else
+                chart1 = JSON.stringify @getNumberOfRoomsPerDay req, res, body
+                Request.get "#{Config.Restapi.Url}/stat", (e, r, body) =>
                     if e? or r.statusCode > 200
                         Errors.RenderNotFound req, res
                     else
-                        callback req, res
-
-        updateNbrTmpUser : (req, res, body, callback) =>
-                body = body[0]
-                body.nb_user_tmp += 1
-                Request.put {
-                    json    : body
-                    url     : "#{Config.Restapi.Url}/stat/#{body.id}"
-                }, (e, r, body) =>
-                    if e? or r.statusCode > 200
-                        Errors.RenderNotFound req, res
-                    else
-                        callback req, res
-
-        countTmpUser : (req, res, callback) =>
-            date = Moment().format "YYYY-MM-DD"
-            timestamp = String(do (new Date(date)).getTime)
-            Request.get "#{Config.Restapi.Url}/stat?c_date=#{timestamp}", (e, r, body) =>
-                if e? or r.statusCode > 200
-                    Errors.RenderNotFound req, res
-                else
-                    body = JSON.parse body
-                    if body.length is 0
-                        @insertNewDate req, res, body, callback, timestamp
-                    else
-                        @updateNbrTmpUser req, res, body, callback
-
-        getNumberOfRoomsPerDay: (req, res, body) =>
-            body = JSON.parse body
-            data = {}
-            for rooms in body
-                date = Moment.unix rooms.c_date / 1000
-                key = date.format "YYYY-MM-DD"
-                timestamp = do (new Date(key)).getTime
-                if data[timestamp]?
-                    data[timestamp] = data[timestamp] += 1
-                else
-                    data[timestamp] = 1
-            tmp = []
-            for timestamp, nb of data
-                tmp.push [Number(timestamp), nb]
-            return tmp
-
-        getNumberOfTmpUsersPerDay: (req, res, body) =>
-             body = JSON.parse body
-             data = []
-             for users in body
-                data.push [Number(users.c_date), users.nb_user_tmp]
-             return data
-
-        statsPage : (req, res) =>
-            Request.get "#{Config.Restapi.Url}/room", (e, r, body) =>
-                if e? or r.statusCode > 200
-                    Errors.RenderNotFound req, res
-                else
-                    chart1 = JSON.stringify @getNumberOfRoomsPerDay req, res, body
-                    Request.get "#{Config.Restapi.Url}/stat", (e, r, body) =>
-                        if e? or r.statusCode > 200
-                            Errors.RenderNotFound req, res
-                        else
-                            chart2 = JSON.stringify @getNumberOfTmpUsersPerDay req, res, body
-                            options =
-                                title           : 'Voicious'
-                                chart1          : chart1
-                                chart2          : chart2
-                            @renderStats res, options
+                        chart2 = JSON.stringify @getNumberOfTmpUsersPerDay req, res, body
+                        options =
+                            title           : 'Voicious'
+                            chart1          : chart1
+                            chart2          : chart2
+                        @renderStats res, options

 exports.Stats   = new _Stats
 exports.Routes  =


Added options to applymarkdown

Added options to applymarkdown

--- src/core/utils.coffee
+++ src/core/utils.coffee
@@ -206,19 +206,18 @@
     s = t
     s

-  applyMarkdown: (text)->
-    # problems with markdown so far:
-    # - links are broken due to textexpansions (images too i guess)
+  applyMarkdown: (text, options = {})->
     return null unless text

-    marked Encoder.htmlDecode(text),
-      gfm       : true
-      pedantic  : false
-      sanitize  : true
-      highlight :(text, lang)->
-        if hljs.getLanguage lang
-        then hljs.highlight(lang,text).value
-        else text
+    options.gfm       ?= true
+    options.pedantic  ?= false
+    options.sanitize  ?= true
+    options.highlight ?= (text, lang) ->
+      if hljs.getLanguage lang
+      then hljs.highlight(lang,text).value
+      else text
+
+    marked Encoder.htmlDecode(text), options

   enterFullscreen: do ->


Fixed a couple things with the component versions view.

--- app/views/editor/component/versions.coffee
+++ app/views/editor/component/versions.coffee
@@ -2,8 +2,8 @@

 module.exports = class SuperVersionsView extends VersionsView
   id: "editor-component-versions-view"
-  url: "/db/component/"
+  url: "/db/level.component/"
   page: "component"

   constructor: (options, @ID) ->
-    super options, ID, require 'models/Component'+    super options, ID, require 'models/LevelComponent'


enable spectate in one team ladder (#6162)

Co-authored-by: smallst_wsl <6c42689ff77371401bf43b7d56b93ac1ca3e92d3@163.com>

--- app/views/ladder/LadderTabView.coco.coffee
+++ app/views/ladder/LadderTabView.coco.coffee
@@ -40,6 +40,7 @@
     @refreshLadder()

     @capitalize = _.string.capitalize
+    @selectedTeam = 'humans'

     # Trying not loading the FP/G+ stuff for now to see if anyone complains they were using it so we can have just two columns.
     #@socialNetworkRes = @supermodel.addSomethingResource('social_network_apis', 0)
@@ -318,13 +319,28 @@
     row = cell.parent()
     table = row.closest('table')
     wasSelected = cell.hasClass 'selected'
-    table.find('.spectate-cell.selected').removeClass 'selected'
-    cell = $(e.target).closest('.spectate-cell').toggleClass 'selected', not wasSelected
-    sessionID = row.data 'session-id'
-    teamID = table.data 'team'
-    @spectateTargets ?= {}
-    @spectateTargets[teamID] = if wasSelected then null else sessionID
-    console.log @spectateTargets, cell, row, table
+    if @teams.length == 2
+      table.find('.spectate-cell.selected').removeClass 'selected'
+      cell = $(e.target).closest('.spectate-cell').toggleClass 'selected', not wasSelected
+      sessionID = row.data 'session-id'
+      teamID = table.data 'team'
+      @spectateTargets ?= {}
+      @spectateTargets[teamID] = if wasSelected then null else sessionID
+      console.log @spectateTargets, cell, row, table
+    else
+      if wasSelected
+        removeClass = if cell.hasClass('selected-humans') then 'selected-humans' else 'selected-ogres'
+        cell = $(e.target).closest('.spectate-cell').removeClass ('selected '+removeClass)
+        teamID = @selectedTeam = if removeClass is 'selected-humans' then 'humans' else 'ogres'
+      else
+        table.find('.spectate-cell.selected.selected-' + @selectedTeam).removeClass ('selected selected-'+@selectedTeam)
+        cell = $(e.target).closest('.spectate-cell').addClass 'selected selected-'+@selectedTeam
+        teamID = @selectedTeam
+        @selectedTeam = if @selectedTeam is 'humans' then 'ogres' else 'humans'
+      sessionID = row.data 'session-id'
+      @spectateTargets ?= {}
+      @spectateTargets[teamID] = if wasSelected then null else sessionID
+      console.log @spectateTargets, cell, row, table

   onLoadMoreLadderEntries: (e) ->
     @ladderLimit ?= 100


ControlBarView handling playing non-latest versions of levels.

--- app/views/play/level/ControlBarView.coffee
+++ app/views/play/level/ControlBarView.coffee
@@ -31,7 +31,7 @@
     @worldName = options.worldName
     @session = options.session
     @level = options.level
-    @levelID = @level.get('slug')
+    @levelID = @level.get('slug') or @level.id
     @spectateGame = options.spectateGame ? false
     @observing = options.session.get('creator') isnt me.id
     super options
@@ -72,7 +72,7 @@
     c.observing = @observing
     @homeViewArgs = [{supermodel: if @hasReceivedMemoryWarning then null else @supermodel}]
     if @level.get('type', true) in ['ladder', 'ladder-tutorial', 'hero-ladder', 'course-ladder']
-      levelID = @level.get('slug').replace /\-tutorial$/, ''
+      levelID = @level.get('slug')?.replace(/\-tutorial$/, '') or @level.id
       @homeLink = '/play/ladder/' + levelID
       @homeViewClass = 'views/ladder/LadderView'
       @homeViewArgs.push levelID
@@ -93,7 +93,7 @@
     else
       @homeLink = '/'
       @homeViewClass = 'views/HomeView'
-    c.editorLink = "/editor/level/#{@level.get('slug')}"
+    c.editorLink = "/editor/level/#{@level.get('slug') or @level.id}"
     c.homeLink = @homeLink
     c


Make default code locking contiguous

Make default code locking contiguous

Lock ‘del’ command too.

--- app/views/play/level/tome/SpellView.coffee
+++ app/views/play/level/tome/SpellView.coffee
@@ -273,6 +273,15 @@
       return true for range in @readOnlyRanges when leftRange.intersects(range)
       false

+    intersectsRight = =>
+      rightRange = @ace.getSelectionRange().clone()
+      if rightRange.end.column < @aceDoc.getLine(rightRange.end.row).length
+        rightRange.setEnd rightRange.end.row, rightRange.end.column + 1
+      else if rightRange.start.row < @aceDoc.getLength() - 1
+        rightRange.setEnd rightRange.end.row + 1, 0
+      return true for range in @readOnlyRanges when rightRange.intersects(range)
+      false
+
     preventReadonly = (next) ->
       return true if intersects()
       next?()
@@ -284,27 +293,10 @@
         wrapper => orig.apply obj, args
       obj[method]

-    finishRange = (row, startRow, startColumn) =>
-      range = new Range startRow, startColumn, row, @aceSession.getLine(row).length - 1
-      range.start = @aceDoc.createAnchor range.start
-      range.end = @aceDoc.createAnchor range.end
-      range.end.$insertRight = true
-      @readOnlyRanges.push range
-
-    # Create a read-only range for each chunk of text not separated by an empty line
     @readOnlyRanges = []
-    startRow = startColumn = null
-    for row in [0...@aceSession.getLength()]
-      unless /^\s*$/.test @aceSession.getLine(row)
-        unless startRow? and startColumn?
-          startRow = row
-          startColumn = 0
-      else
-        if startRow? and startColumn?
-          finishRange row - 1, startRow, startColumn
-          startRow = startColumn = null
-    if startRow? and startColumn?
-      finishRange @aceSession.getLength() - 1, startRow, startColumn
+    lines = @aceDoc.getAllLines()
+    lastRow = row for line, row in lines when not /^\s*$/.test(line)
+    @readOnlyRanges.push new Range 0, 0, lastRow, lines[lastRow].length - 1 if lastRow?

     # Override write operations that intersect with default code
     interceptCommand @ace, 'onPaste', preventReadonly
@@ -317,6 +309,9 @@
         @zatanna?.off?()
         return false
       if e.command.name in ['Backspace', 'throttle-backspaces'] and intersectsLeft()
+        @zatanna?.off?()
+        return false
+      if e.command.name is 'del' and intersectsRight()
         @zatanna?.off?()
         return false
       if e.command.name in ['enter-skip-delimiters', 'Enter', 'Return']


should fix (#6254)

should fix (#6254)

--- app/views/play/level/tome/SpellView.coffee
+++ app/views/play/level/tome/SpellView.coffee
@@ -1008,7 +1008,7 @@
       @fetchTokenForSource().then (token) =>
         # TODO: This is janky for those languages with a delay
         @spell.transpile token
-      @cast(false, false, true)
+        @cast(false, false, true)
     else if (endOfLine or beginningOfLine) and not incompleteThis
       @preload()


activity: register channels by name

activity: register channels by name

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -196,22 +196,29 @@
   forwardMessageEvents : forwardMessageEvents

   registerAndOpenChannels = (socialApiChannels)->
+    {socialapi} = KD.singletons
+
+    map = {}
+
     getCurrentGroup (group)->
       for socialApiChannel in socialApiChannels
-        # lock
-        name = "socialapi.#{socialApiChannel.groupName}-#{socialApiChannel.typeConstant}-#{socialApiChannel.name}"
-        continue  if KD.singletons.socialapi.openedChannels[name]
-        KD.singletons.socialapi.openedChannels[name] = {}
+        {typeConstant, name} = socialApiChannel
+        channelName = "#{typeConstant}-#{name}"
+        continue  if socialapi.openedChannels[channelName]
+        socialapi.openedChannels[channelName] = {} # placeholder to  duplicate registration
+        map[channelName] = socialApiChannel

         subscriptionData =
           serviceType: 'socialapi'
           group      : group.slug
-          channelType: socialApiChannel.typeConstant
-          channelName: socialApiChannel.name
+          channelType: typeConstant
+          channelName: name
           isExclusive: yes

         KD.remote.subscribe name, subscriptionData, (brokerChannel)->
-          KD.singletons.socialapi.openedChannels[brokerChannel.name] = brokerChannel
+          {channelType, channelName} = brokerChannel.authenticationInfo
+          channelName = "#{channelType}-#{channelName}"
+          socialapi.openedChannels[channelName] = brokerChannel
           forwardMessageEvents brokerChannel, socialApiChannel, [
             "MessageAdded",
             "MessageRemoved"


terminal: fetch account's group associated VMs in "VM selection" modal

--- client/Terminal/vmselectionview.coffee
+++ client/Terminal/vmselectionview.coffee
@@ -45,4 +45,6 @@
       @emit "VMSelected", vm
       @destroy()

-    @listController.instantiateListItems KD.getSingleton('vmController').vms
+    KD.singleton("vmController").fetchGroupVMs no, (err, vms) =>
+      return  if KD.showError err
+      @listController.instantiateListItems vms


Fix floating % positioning.

Fix floating % positioning.

--- client/app/js/views/battle_view.coffee
+++ client/app/js/views/battle_view.coffee
@@ -190,7 +190,7 @@
     percentText += "%"
     $hp = $('<span/>').addClass("percentage #{kind}").text(percentText)
     $hp.hide().appendTo(@$('.battle_pane'))
-    x -= ($sprite.width() - $hp.width()) / 2
+    x -= $hp.width() / 2
     y -= $sprite.height() / 3
     $hp.css(position: 'absolute', top: y, left: x).show()
     if percent >= 0


[codeclean] remove silent option for onReceiveRawMessage

--- client/app/stores/message_store.coffee
+++ client/app/stores/message_store.coffee
@@ -59,7 +59,7 @@

     initFilters()

-    onReceiveRawMessage = (message, silent = false) ->
+    onReceiveRawMessage = (message) ->
         # create or update
         if not message.attachments?
             message.attachments = []
@@ -83,14 +83,15 @@
         message = Immutable.Map message
         _messages = _messages.set message.get('id'), message

-        @emit 'change' unless silent

     ###
         Defines here the action handlers.
     ###
     __bindHandlers: (handle) ->

-        handle ActionTypes.RECEIVE_RAW_MESSAGE, onReceiveRawMessage
+        handle ActionTypes.RECEIVE_RAW_MESSAGE, (message) ->
+            onReceiveRawMessage message
+            @emit 'change'

         handle ActionTypes.RECEIVE_RAW_MESSAGES, (messages) ->

@@ -113,7 +114,7 @@
             if messages.count? and messages.mailboxID?
                 messages = messages.messages.sort __sortFunction

-            onReceiveRawMessage message, true for message in messages
+            onReceiveRawMessage message for message in messages
             @emit 'change'

         handle ActionTypes.REMOVE_ACCOUNT, (accountID) ->
@@ -125,16 +126,16 @@
             @emit 'change'

         handle ActionTypes.MESSAGE_SEND, (message) ->
-            onReceiveRawMessage message, true
+            onReceiveRawMessage message

         handle ActionTypes.MESSAGE_DELETE, (message) ->
-            onReceiveRawMessage message, true
+            onReceiveRawMessage message

         handle ActionTypes.MESSAGE_BOXES, (message) ->
-            onReceiveRawMessage message, true
+            onReceiveRawMessage message

         handle ActionTypes.MESSAGE_FLAG, (message) ->
-            onReceiveRawMessage message, true
+            onReceiveRawMessage message

         handle ActionTypes.SELECT_ACCOUNT, ->
             initFilters()


Fixed: the first day of the week should be monday

--- client/app/views/alarmform_view.coffee
+++ client/app/views/alarmform_view.coffee
@@ -54,7 +54,7 @@
                 field: @$('#date-control')
                 placement: 'bottom'

-        @dateField.datepicker().on 'changeDate', ->
+        @dateField.datepicker({weekStart: 1}).on 'changeDate', ->
             $(@).datepicker 'hide'

         @timeField.timepicker


Trying to fix global mood poll

Trying to fix global mood poll

--- scripts/awesome.team.coffee
+++ scripts/awesome.team.coffee
@@ -114,7 +114,7 @@
     for room, board of robot.brain.data._private.board
       buttonName = "mood_" + robot.brain.data._private.team[room]._id
       robot.emit 'slack.attachment',
-        message: robot.messageRoom room
+        channel: room
         content:
           text: "How was your day today?"
           fallback: "Oh no! Something went horribly wrong!"


misc: Reorganize exports

misc: Reorganize exports

--- src/js/misc.coffee
+++ src/js/misc.coffee
@@ -2,15 +2,17 @@

 DEFAULT_RETRY_DELAY = 5000

+misc = {}
+
 # prioritize objects on the right
-merge = (objects...) ->
+misc.merge = (objects...) ->
   res = {}
   for obj in objects
     for key, value of obj
        res[key] = value
   res

-uniqBy = (arr, key) ->
+misc.uniqBy = (arr, key) ->
     seen = {}
     arr.filter (item)->
       k = key(item)
@@ -20,19 +22,19 @@
         seen[k] = true
         true

-groupBy = (arr, key) ->
+misc.groupBy = (arr, key) ->
   grouped = {}
   (grouped[key e] ?= []).push e for e in arr
   grouped

-flatten = (arr) -> Array::concat(arr...)
+misc.flatten = (arr) -> Array::concat(arr...)

-arrayWithout = (arr, item) ->
+misc.arrayWithout = (arr, item) ->
   arr.filter (element) => element != item

-spawn = (func) -> window.setTimeout func, 0
+misc.spawn = (func) -> window.setTimeout func, 0

-retry = (func, callback, times = 10, delay = DEFAULT_RETRY_DELAY) ->
+misc.retry = (func, callback, times = 10, delay = DEFAULT_RETRY_DELAY) ->
   func (args...) =>
     err = args[0]
     if err
@@ -41,18 +43,10 @@
       else
         log.error "retry: #{err}"
         log.info "retry: rescheduling in the next #{delay} ms..."
-        spawn () => (retry func, callback, times - 1, delay)
+        misc.spawn () => (retry func, callback, times - 1, delay)
         return
     callback args...

-colorsAvailable = Platform.version() == "basalt"
+misc.colorsAvailable = Platform.version() == "basalt"

-module.exports =
-  merge: merge
-  uniqBy: uniqBy
-  groupBy: groupBy
-  flatten: flatten
-  arrayWithout: arrayWithout
-  colorsAvailable: colorsAvailable
-  spawn: spawn
-  retry: retry
+module.exports = misc


Make image hints test work in puppeteer

Make image hints test work in puppeteer

These images need dimensions to be hintable

--- tests/dom_tests/dom_tests.coffee
+++ tests/dom_tests/dom_tests.coffee
@@ -330,8 +330,10 @@

     setup ->
       initializeModeState()
-      testContent = "
-        " + ""
+      testContent = "" +
+        "" +
+        "" +
+        ""
       document.getElementById("test-div").innerHTML = testContent
       @linkHints = activateLinkHintsMode()


shorten NAME_TO_QTYPE -> NAME_QTYPE

shorten NAME_TO_QTYPE -> NAME_QTYPE

--- src/lib/dns.coffee
+++ src/lib/dns.coffee
@@ -257,7 +257,7 @@

         resolve: (path, options, cb) ->
             req = new Packet()
-            req.question.push dns2.Question {name: path, type: options.type if options?.type? and _.has dns2.consts.NAME_TO_QTYPE,options.type}
+            req.question.push dns2.Question {name: path, type: options.type if options?.type? and _.has NAME_QTYPE,options.type}
             @oldDNSLookup req, (packet, code) ->
                 code = {code:code, name:RCODE_NAME[code]} if code
                 cb code, packet


improve test coverage

improve test coverage

--- spec/project-folder-spec.coffee
+++ spec/project-folder-spec.coffee
@@ -44,6 +44,9 @@
 describe "project-folder", ->
   [main, view, filterEditorElement, workspaceElement] = []

+  tempHome = fs.realpathSync(temp.mkdirSync('home'))
+  configPath = joinPath(tempHome, 'project-folder.cson')
+
   # Normal
   normalRoot = getPath('normal')
   normalDir1 = getPath('normal/dir-1')
@@ -52,7 +55,6 @@

   # Git
   gitRoot = fs.realpathSync(temp.mkdirSync('git'))
-  tempHome = fs.realpathSync(temp.mkdirSync('home'))
   gitRootSource = getPath('git')
   wrench.copyDirSyncRecursive(gitRootSource, gitRoot, forceDelete: true)

@@ -81,6 +83,7 @@

   beforeEach ->
     addCustomMatchers(this)
+    setConfig('configPath', configPath)
     fixturesDir = getProjectPaths()[0]
     atom.project.removePath(fixturesDir)

@@ -258,7 +261,6 @@
       expect(atom.open).toHaveBeenCalledWith({pathsToOpen: [normalDir1], newWindow: true})

   describe "user defined project-group", ->
-    configPath = joinPath(tempHome, 'project-folder.cson')
     userConfigEditor = null

     ensureSelectListItems = (expectedItems) ->
@@ -269,7 +271,6 @@
       expect(items).toEqual(expectedItems)

     beforeEach ->
-      setConfig('configPath', configPath)
       waitsForPromise ->
         main.openConfig()
       runs ->
@@ -340,7 +341,6 @@
             ]
           """
         userConfigEditor.setText(userConfigText)
-        expect(view.groups).toBe(null)
         userConfigEditor.save()
         expect(view.groups).not.toBe(null)

@@ -377,3 +377,42 @@
         dispatchCommand(workspaceElement, 'project-folder:remove')
         dispatchCommand(filterEditorElement, 'core:confirm')
         ensureProjectPaths(dirs: [], panelIsVisible: false)
+
+      it "show up on removal list as long as at least one member was loaded", ->
+        addProject(normalDir1, normalDir2, gitDir1, gitDir2)
+        dispatchCommand(workspaceElement, 'project-folder:remove')
+        expect(view).toHaveClass('remove')
+
+        itemGroupSample1 = {dir: 'sample1', type: 'group', dirs: [normalDir1, normalDir2]}
+        itemGroupSample2 = {dir: 'sample2', type: 'group', dirs: [gitDir1, gitDir2]}
+        itemDirNormalDir1 = {dir: normalDir1, type: 'directory'}
+        itemDirNormalDir2 = {dir: normalDir2, type: 'directory'}
+        itemDirGitDir1 = {dir: gitDir1, type: 'directory'}
+        itemDirGitDir2 = {dir: gitDir2, type: 'directory'}
+
+        ensureSelectListItems [
+          itemGroupSample1
+          itemGroupSample2
+          itemDirNormalDir1
+          itemDirNormalDir2
+          itemDirGitDir1
+          itemDirGitDir2
+        ]
+
+        view.remove(itemDirNormalDir1)
+        view.remove(itemDirGitDir1)
+        ensureSelectListItems [
+          itemGroupSample1
+          itemGroupSample2
+          itemDirNormalDir2
+          itemDirGitDir2
+        ]
+
+        view.remove(itemDirGitDir2)
+        ensureSelectListItems [
+          itemGroupSample1
+          itemDirNormalDir2
+        ]
+
+        view.remove(itemDirNormalDir2)
+        ensureSelectListItems []