Update grammar

Update grammar

The grammar was converted to CSON by the conversion script added in
f3574068575a2a7b2c67e71a0e7416f9a6596ffe

--- grammars/powershell.cson
+++ grammars/powershell.cson
@@ -1,784 +1,691 @@
-'scopeName': 'source.powershell'
-'name': 'PowerShell'
-'comment': 'Windows PowerShell language'
-'fileTypes': [
-  'ps1'
-  'psm1'
-  'psd1'
-]
-'firstLineMatch': '^#!/.*\\bpowershell\\b'
-'foldingStartMarker': '^\\s*(function)\\s+([.a-zA-Z0-9_ <]+)\\s*(\\((.*)\\))?\\s*:|\\{\\s*$|\\(\\s*$|\\[\\s*$|^\\s*"""(?=.)(?!.*""")'
-'foldingStopMarker': '^\\s*\\}|^\\s*\\]|^\\s*\\)|^\\s*"""\\s*$'
-'patterns': [
-  {
-    'include': '#escaped_char'
-  }
-  {
-    'captures':
-      '1':
-        'name': 'punctuation.storage.type.begin.powershell'
-      '3':
-        'name': 'punctuation.storage.type.end.powershell'
-    'match':'(\\[)([A-Za-z0-9_\\.]+)(\\])'
-    'name':'storage.type.powershell'
-  }
-  {
-    'captures':
-      '1':
-        'name': 'punctuation.variable.begin.powershell'
-    'match': '(\\$)(?i:(null|true|false|\\$|\\?|\\^|_|args|consolefilename|error|eventsubscriber|eventargs|event|executioncontext|foreach|host|home|input|lastexitcode|matches|myinvocation|nestedpromptlevel|ofs|pid|profile|psboundparameters|pscmdlet|pscommandpath|psculture|psdebuggingcontext|pshome|psitem|psscriptroot|psuiculture|pssenderinfo|psversiontable|pwd|sender|shellid|stacktrace|this))'
-    'comment': 'Powershell automatic variables.'
-    'name': 'variable.language.powershell'
-  }
-  {
-    'captures':
-      '1':
-        'name': 'punctuation.variable.begin.powershell'
-    'match': '(\\$)[a-zA-Z_][a-zA-Z0-9_]*?\\b'
-    'name': 'variable.other.powershell'
-  }
-  {
-    'match': '\\B(?i:(-bAnd|-bOr|-bXor|-bNot|-shl|-sh))\\b'
-    'name': 'keyword.operator.bitwise.powershell'
-  }
-  {
-    'match': '\\B(?i:-([i|c]?(eq|lt|gt|le|ge|ne|notlike|like|match|notmatch|contains|notcontains|in|notin|replace)))\\b'
-    'name': 'keyword.operator.comparison.powershell'
-  }
-  {
-    'comment': 'keyword operators that evaluate to True or False'
-    'match': '\\B(?i:-(and|not|or|xor))\\b'
-    'name': 'keyword.operator.logical.powershell'
-  }
-  {
-    'comment': 'logical negation operator'
-    'match': '!'
-    'name': 'keyword.operator.logical.powershell'
-  }
-  {
-    'captures':
-      '1':
-        'name': 'punctuation.definition.comment.powershell'
-    'match': '\\s*(#).*$'
-    'name': 'comment.line.number-sign.powershell'
-  }
-  {
-    'begin': '^\\s*<#'
-    'captures':
-      '1':
-        'name': 'punctuation.definition.comment.powershell'
-    'end': '.*#>'
-    'name': 'comment.block.powershell'
-  }
-  {
-    'begin': '\\s*@"'
-    'captures':
-      '1':
-        'name': 'punctuation.definition.herestring.powershell'
-    'end': '^"@'
-    'name': 'constant.string.multiline.doublequoted'
-  }
-  {
-    'begin': '\\s*@\''
-    'captures':
-      '1':
-        'name': 'punctuation.definition.herestring.powershell'
-    'end': '^\'@'
-    'name': 'constant.string.multiline.singlequoted'
-  }
-  {
-    'match': '\\b(?i:(0x\\h*)L)'
-    'name': 'constant.numeric.integer.long.hexadecimal.powershell'
-  }
-  {
-    'match': '\\b(?i:(0x\\h*))'
-    'name': 'constant.numeric.integer.hexadecimal.powershell'
-  }
-  {
-    'match': '\\b(?i:(((\\d+(\\.(?=[^a-zA-Z_])\\d*)?|(?<=[^0-9a-zA-Z_])\\.\\d+)(e[\\-\\+]?\\d+)?))J)'
-    'name': 'constant.numeric.complex.powershell'
-  }
-  {
-    'match': '\\b(?i:(\\d+\\.\\d*(e[\\-\\+]?\\d+)?))(?=[^a-zA-Z_])'
-    'name': 'constant.numeric.float.powershell'
-  }
-  {
-    'match': '(?<=[^0-9a-zA-Z_])(?i:(\\.\\d+(e[\\-\\+]?\\d+)?))'
-    'name': 'constant.numeric.float.powershell'
-  }
-  {
-    'match': '\\b(?i:(\\d+e[\\-\\+]?\\d+))'
-    'name': 'constant.numeric.float.powershell'
-  }
-  {
-    'match': '\\b(?i:([1-9]+[0-9]*|0)L)'
-    'name': 'constant.numeric.integer.long.decimal.powershell'
-  }
-  {
-    'match': '\\b([1-9]+[0-9]*|0)(?i:kb|mb|gb)'
-    'name': 'constant.numeric.integer.bytes.powershell'
-  }
-  {
-    'match': '\\b([1-9]+[0-9]*|0)'
-    'name': 'constant.numeric.integer.decimal.powershell'
-  }
-  {
-    'comment': 'keywords that delimit special blocks'
-    'match': '\\b(?i:(begin|data|dynamicparam|end|filter|inlinescript|parallel|process|sequence|workflow))\\b'
-    'name': 'keyword.control.flow.powershell'
-  }
-  {
-    'comment': 'keywords that delimit flow blocks'
-    'match': '\\b(?i:(catch|do|else|elseif|finally|for|foreach|if|in|switch|trap|try|until|while))\\b'
-    'name': 'keyword.control.flow.powershell'
-  }
-  {
-    'comment': 'keywords that alter flow from within a block'
-    'match': '\\b(?i:(break|continue|throw|return|exit))\\b'
-    'name': 'keyword.control.flow.powershell'
-  }
-  {
-    'comment': 'cmdlets'
-    'match': '\\b([a-zA-Z_]*-[a-zA-Z_]*)\\b'
-    'name': 'keyword.cmdlet.powershell'
-  }
-  {
-    'captures':
-      '1':
-        'name': 'keyword.cmdlet.alias.powershell'
-    'comment': 'cmdlet aliases'
-    'match': '\\b(ac|asnp|cat|cd|chdir|clc|clear|clhy|cli|clp|\n                       cls|clv|compare|copy|cp|cpi|cpp|cvpa|dbp|del|diff|\n                       dir|ebp|echo|epal|epcsv|epsn|erase|etsn|exsn|fc|fl|\n                       foreach|ft|fw|gal|gbp|gc|gci|gcm|gcs|gdr|ghy|gi|gjb|\n                       gl|gm|gmo|gp|gps|group|gsn|gsnp|gsv|gu|gv|gwmi|h|\n                       history|icm|iex|ihy|ii|ipal|ipcsv|ipmo|ipsn|ise|\n                       iwmi|kill|lp|ls|man|md|measure|mi|mount|move|mp|mv|\n                       nal|ndr|ni|nmo|nsn|nv|ogv|oh|popd|ps|pushd|pwd|r|\n                       rbp|rcjb|rd|rdr|ren|ri|rjb|rm|rmdir|rmo|rni|rnp|rp|\n                       rsn|rsnp|rv|rvpa|rwmi|sajb|sal|saps|sasv|sbp|sc|\n                       select|set|si|sl|sleep|sort|sp|spjb|spps|spsv|start|\n                       sv|swmi|tee|type|where|wjb|write)\\b'
-    'name': 'keyword.cmdlet.alias.powershell'
-  }
-  {
-    'captures':
-      '1':
-        'name': 'keyword.other.powershell'
-    'comment': 'keywords that haven\'t fit into other groups (yet).'
-    'match': '\\b(?i:(from|param))\\b'
-  }
-  {
-    'match': '\\+\\=|-\\=|\\*\\=|/\\=|//\\=|%\\=|&\\=|\\|\\=|\\^\\=|>>\\=|<<\\=|\\*\\*\\='
-    'name': 'keyword.operator.assignment.augmented.powershell'
-  }
-  {
-    'match': '\\+|\\-|\\*|\\*\\*|/|//|%|<<|>>|&|\\||\\^|~'
-    'name': 'keyword.operator.arithmetic.powershell'
-  }
-  {
-    'match': '\\='
-    'name': 'keyword.operator.assignment.powershell'
-  }
-  {
-    'begin': '^\\s*([Ff]unction)\\s+(?=[A-Za-z_][A-Za-z0-9_]*\\s*(\\(?))'
-    'beginCaptures':
-      '1':
-        'name': 'storage.type.function.powershell'
-    'end': '(\\)?)\\s*(?:(.*$\\n?))'
-    'endCaptures':
-      '1':
-        'name': 'punctuation.definition.parameters.end.powershell'
-      '2':
-        'name': 'punctuation.section.function.begin.powershell'
-      '3':
-        'name': 'invalid.illegal.missing-section-begin.powershell'
-    'name': 'meta.function.powershell'
-    'patterns': [
-      {
-        'begin': '(?=[A-Za-z_][A-Za-z0-9_]*)'
-        'contentName': 'entity.name.function.powershell'
-        'end': '(?![A-Za-z0-9_])'
-        'patterns': [
+{
+  fileTypes: [
+    "ps1"
+    "psm1"
+    "psd1"
+  ]
+  name: "PowerShell"
+  patterns: [
+    {
+      begin: "<#"
+      beginCaptures:
+        0:
+          name: "punctuation.start.definition.comment.block.powershell"
+
+      end: "#>"
+      endCaptures:
+        0:
+          name: "punctuation.end.definition.comment.block.powershell"
+
+      name: "comment.block.powershell"
+      patterns: [include: "#commentEmbeddedDocs"]
+    }
+    {
+      begin: "(?<![\\\\-])#"
+      end: "$"
+      name: "comment.line.number-sign.powershell"
+      patterns: [include: "#commentEmbeddedDocs"]
+    }
+    {
+      match: "[2-6]>&1|>>|>|<<|<|>|>\\||[1-6]>|[1-6]>>"
+      name: "keyword.operator.redirection.powershell"
+    }
+    {
+      include: "#commands"
+    }
+    {
+      include: "#variable"
+    }
+    {
+      include: "#interpolatedStringContent"
+    }
+    {
+      include: "#function"
+    }
+    {
+      include: "#attribute"
+    }
+    {
+      include: "#type"
+    }
+    {
+      begin: "(?<!(?<!`)\")\""
+      end: "\"(?!\")"
+      name: "string.quoted.double.powershell"
+      patterns: [
+        {
+          include: "#variableNoProperty"
+        }
+        {
+          include: "#doubleQuotedStringEscapes"
+        }
+        {
+          include: "#interpolation"
+        }
+        {
+          match: "`\\s*$"
+          name: "keyword.other.powershell"
+        }
+      ]
+    }
+    {
+      comment: "Needed to parse stuff correctly in 'argument mode'. (See about_parsing.)"
+      include: "#doubleQuotedStringEscapes"
+    }
+    {
+      begin: "(?<!')'"
+      end: "'(?!')"
+      name: "string.quoted.single.powershell"
+      patterns: [
+        match: "''"
+        name: "constant.character.escape.powershell"
+      ]
+    }
+    {
+      begin: "\\@\"(?=$)"
+      end: "^\"@"
+      name: "string.quoted.double.heredoc.powershell"
+      patterns: [
+        {
+          include: "#variableNoProperty"
+        }
+        {
+          include: "#doubleQuotedStringEscapes"
+        }
+        {
+          include: "#interpolation"
+        }
+      ]
+    }
+    {
+      begin: "\\@'(?=$)"
+      end: "^'@"
+      name: "string.quoted.single.heredoc.powershell"
+      patterns: [
+        match: "''"
+        name: "constant.character.escape.powershell"
+      ]
+    }
+    {
+      include: "#numericConstant"
+    }
+    {
+      begin: "@\\("
+      captures:
+        0:
+          name: "keyword.other.powershell"
+
+      end: "\\)"
+      name: "meta.group.array-expression.powershell"
+      patterns: [include: "$self"]
+    }
+    {
+      begin: "\\$\\("
+      captures:
+        0:
+          name: "keyword.other.powershell"
+
+      comment: "TODO: move to repo; make recursive."
+      end: "\\)"
+      name: "meta.group.complex.subexpression.powershell"
+      patterns: [include: "$self"]
+    }
+    {
+      match: "(?<!\\w)-([ci]?[lg][te]|eq|ne)"
+      name: "keyword.operator.logical.powershell"
+    }
+    {
+      match: "(?i:([\\S&&[^<>\"/\\|?*]])+)(?i:\\.(?i:exe|cmd|bat|ps1))"
+      name: "support.function.powershell"
+    }
+    {
+      match: "(?<!\\w)((?i:begin|break|catch|continue|data|define|do|dynamicparam|else|elseif|end|exit|filter|finally|for|foreach(?!-object)|from|if|in|inlinescript|parallel|param|process|return|switch|throw|trap|try|until|using|var|where(?!=-object)|while)|%|\\?)(?!\\w)"
+      name: "keyword.control.powershell"
+    }
+    {
+      captures:
+        1:
+          name: "storage.type.powershell"
+
+        2:
+          name: "entity.name.function"
+
+      comment: "capture should be entity.name.type, but it doesn't provide a good color in the default schema."
+      match: "(?<!\\w)((?i:class)|%|\\?)(?:\\s)+((?:\\p{L}|\\d|_|-|)+)\\b"
+    }
+    {
+      match: "(?<!\\w)-(?i:is(?:not)?|as)\\b"
+      name: "keyword.operator.comparison.powershell"
+    }
+    {
+      match: "(?<!\\w)-(?i:[ic]?(?:eq|ne|[gl][te]|(?:not)?(?:like|match|contains|in)|replace))(?!\\p{L})"
+      name: "keyword.operator.comparison.powershell"
+    }
+    {
+      match: "(?<!\\w)-(?i:join|split)(?!\\p{L})|!"
+      name: "keyword.operator.unary.powershell"
+    }
+    {
+      match: "(?<!\\w)-(?i:and|or|not|xor)(?!\\p{L})|!"
+      name: "keyword.operator.logical.powershell"
+    }
+    {
+      match: "(?<!\\w)-(?i:band|bor|bnot|bxor)(?!\\p{L})"
+      name: "keyword.operator.bitwise.powershell"
+    }
+    {
+      match: "(?<!\\w)-(?i:f)(?!\\p{L})"
+      name: "keyword.operator.string-format.powershell"
+    }
+    {
+      match: "[+%*/-]?=|[+/*%-]"
+      name: "keyword.operator.assignment.powershell"
+    }
+    {
+      match: "\\|{2}|&{2}|;"
+      name: "keyword.other.statement-separator.powershell"
+    }
+    {
+      match: "&|(?<!\\w)\\.(?= )|`|,|\\|"
+      name: "keyword.operator.other.powershell"
+    }
+    {
+      comment: "This is very imprecise, is there a syntax for 'must come after...' "
+      match: "(?<!\\s|^)\\.\\.(?=\\d|\\(|\\$)"
+      name: "keyword.operator.range.powershell"
+    }
+  ]
+  repository:
+    attribute:
+      begin: "\\[(\\p{L}|\\.|``\\d+)+(?=\\()"
+      beginCaptures:
+        0:
+          name: "entity.name.tag"
+
+        1:
+          name: "entity.name.tag"
+
+      end: "\\]"
+      endCaptures:
+        0:
+          name: "entity.name.tag"
+
+      patterns: [
+        begin: "\\("
+        end: "\\)"
+        name: "entity.other.attribute-name"
+        patterns: [
           {
-            'include': '#entity_name_function'
+            captures:
+              0:
+                name: "entity.other.attribute.parameter.powershell"
+
+              1:
+                name: "constant.language.powershell"
+
+              2:
+                name: "variable.other.powershell"
+
+            comment: "really we should match the known attributes first"
+            match: "(\\w+)\\s*=?([^\"']*?|'[^']*?'|\"[^\"]*?\")?(?=,|\\))"
+            name: "entity.other.attribute-name.powershell"
+          }
+          {
+            include: "#variable"
           }
         ]
-      }
-      {
-        'begin': '(\\()'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.parameters.begin.powershell'
-        'contentName': 'meta.function.parameters.powershell'
-        'end': '(?=\\))'
-        'patterns': [
-          {
-            'include': '#keyword_arguments'
-          }
-          {
-            'captures':
-              '1':
-                'name': 'variable.parameter.function.powershell'
-              '2':
-                'name': 'punctuation.separator.parameters.powershell'
-            'match': '\\b([a-zA-Z_][a-zA-Z_0-9]*)\\s*(?:(,)|(?=[\\n\\)]))'
-          }
-        ]
-      }
-    ]
-  }
-  {
-    'begin': '^\\s*(function)\\s+(?=[A-Za-z_][A-Za-z0-9_]*)'
-    'beginCaptures':
-      '1':
-        'name': 'storage.type.function.powershell'
-    'end': '(\\()|\\s*($\\n?|#.*$\\n?)'
-    'endCaptures':
-      '1':
-        'name': 'punctuation.definition.parameters.begin.powershell'
-      '2':
-        'name': 'invalid.illegal.missing-parameters.powershell'
-    'name': 'meta.function.powershell'
-    'patterns': [
-      {
-        'begin': '(?=[A-Za-z_][A-Za-z0-9_]*)'
-        'contentName': 'entity.name.function.powershell'
-        'end': '(?![A-Za-z0-9_])'
-        'patterns': [
-          {
-            'include': '#entity_name_function'
-          }
-        ]
-      }
-    ]
-  }
-  {
-    'begin': '^\\s*(?=@\\s*[A-Za-z_][A-Za-z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*\\s*\\()'
-    'comment': 'a decorator may be a function call which returns a decorator.'
-    'end': '(\\))'
-    'endCaptures':
-      '1':
-        'name': 'punctuation.definition.arguments.end.powershell'
-    'name': 'meta.function.decorator.powershell'
-    'patterns': [
-      {
-        'begin': '(?=(@)\\s*[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)*\\s*\\()'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.decorator.powershell'
-        'contentName': 'entity.name.function.decorator.powershell'
-        'end': '(?=\\s*\\()'
-        'patterns': [
-          {
-            'include': '#dotted_name'
-          }
-        ]
-      }
-      {
-        'begin': '(\\()'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.arguments.begin.powershell'
-        'contentName': 'meta.function.decorator.arguments.powershell'
-        'end': '(?=\\))'
-        'patterns': [
-          {
-            'include': '#keyword_arguments'
-          }
-          {
-            'include': '$self'
-          }
-        ]
-      }
-    ]
-  }
-  {
-    'begin': '^\\s*(?=@\\s*[A-Za-z_][A-Za-z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*)'
-    'contentName': 'entity.name.function.decorator.powershell'
-    'end': '(?=\\s|$\\n?|#)'
-    'name': 'meta.function.decorator.powershell'
-    'patterns': [
-      {
-        'begin': '(?=(@)\\s*[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*)'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.decorator.powershell'
-        'end': '(?=\\s|$\\n?|#)'
-        'patterns': [
-          {
-            'include': '#dotted_name'
-          }
-        ]
-      }
-    ]
-  }
-  {
-    'begin': '(?<=\\)|\\])\\s*(\\()'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.arguments.begin.powershell'
-    'contentName': 'meta.function-call.arguments.powershell'
-    'end': '(\\))'
-    'endCaptures':
-      '1':
-        'name': 'punctuation.definition.arguments.end.powershell'
-    'name': 'meta.function-call.powershell'
-    'patterns': [
-      {
-        'include': '#keyword_arguments'
-      }
-      {
-        'include': '$self'
-      }
-    ]
-  }
-  {
-    'begin': '(?=[A-Za-z_][A-Za-z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*\\s*\\()'
-    'end': '(\\))'
-    'endCaptures':
-      '1':
-        'name': 'punctuation.definition.arguments.end.powershell'
-    'name': 'meta.function-call.powershell'
-    'patterns': [
-      {
-        'begin': '(?=[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)*\\s*\\()'
-        'end': '(?=\\s*\\()'
-        'patterns': [
-          {
-            'include': '#dotted_name'
-          }
-        ]
-      }
-      {
-        'begin': '(\\()'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.arguments.begin.powershell'
-        'contentName': 'meta.function-call.arguments.powershell'
-        'end': '(?=\\))'
-        'patterns': [
-          {
-            'include': '#keyword_arguments'
-          }
-          {
-            'include': '$self'
-          }
-        ]
-      }
-    ]
-  }
-  {
-    'begin': '(?=[A-Za-z_][A-Za-z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*\\s*\\[)'
-    'end': '(\\])'
-    'endCaptures':
-      '1':
-        'name': 'punctuation.definition.arguments.end.powershell'
-    'name': 'meta.item-access.powershell'
-    'patterns': [
-      {
-        'begin': '(?=[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)*\\s*\\[)'
-        'end': '(?=\\s*\\[)'
-        'patterns': [
-          {
-            'include': '#dotted_name'
-          }
-        ]
-      }
-      {
-        'begin': '(\\[)'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.arguments.begin.powershell'
-        'contentName': 'meta.item-access.arguments.powershell'
-        'end': '(?=\\])'
-        'patterns': [
-          {
-            'include': '$self'
-          }
-        ]
-      }
-    ]
-  }
-  {
-    'begin': '(?<=\\)|\\])\\s*(\\[)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.arguments.begin.powershell'
-    'contentName': 'meta.item-access.arguments.powershell'
-    'end': '(\\])'
-    'endCaptures':
-      '1':
-        'name': 'punctuation.definition.arguments.end.powershell'
-    'name': 'meta.item-access.powershell'
-    'patterns': [
-      {
-        'include': '$self'
-      }
-    ]
-  }
-  {
-    'captures':
-      '1':
-        'name': 'storage.type.function.powershell'
-    'match': '\\b(def|lambda)\\b'
-  }
-  {
-    'include': '#line_continuation'
-  }
-  {
-    'include': '#string_quoted_single'
-  }
-  {
-    'include': '#string_quoted_double'
-  }
-  {
-    'include': '#dotted_name'
-  }
-  {
-    'begin': '(\\()'
-    'end': '(\\))'
-    'patterns': [
-      {
-        'include': '$self'
-      }
-    ]
-  }
-  {
-    'captures':
-      '1':
-        'name': 'punctuation.definition.list.begin.powershell'
-      '2':
-        'name': 'meta.empty-list.powershell'
-      '3':
-        'name': 'punctuation.definition.list.end.powershell'
-    'match': '(\\[)(\\s*(\\]))\\b'
-  }
-  {
-    'begin': '(\\[)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.list.begin.powershell'
-    'end': '(\\])'
-    'endCaptures':
-      '1':
-        'name': 'punctuation.definition.list.end.powershell'
-    'name': 'meta.structure.list.powershell'
-    'patterns': [
-      {
-        'begin': '(?<=\\[|\\,)\\s*(?![\\],])'
-        'contentName': 'meta.structure.list.item.powershell'
-        'end': '\\s*(?:(,)|(?=\\]))'
-        'endCaptures':
-          '1':
-            'name': 'punctuation.separator.list.powershell'
-        'patterns': [
-          {
-            'include': '$self'
-          }
-        ]
-      }
-    ]
-  }
-  {
-    'captures':
-      '1':
-        'name': 'punctuation.definition.tuple.begin.powershell'
-      '2':
-        'name': 'meta.empty-tuple.powershell'
-      '3':
-        'name': 'punctuation.definition.tuple.end.powershell'
-    'match': '(\\()(\\s*(\\)))'
-    'name': 'meta.structure.tuple.powershell'
-  }
-]
-'repository':
-  'escaped_char':
-    'match': '(`n)|(`")|(`\\$)|(`\')|(`a)|(`b)|(`r)|(`t)|(`f)|(`0)|(`v)|(--%)|(``)|(`\\S)'
-    'name': 'constant.character.escape.powershell'
-  'constant_placeholder':
-    'match': '(?i:%(\\([a-z_]+\\))?#?0?\\-?[ ]?\\+?([0-9]*|\\*)(\\.([0-9]*|\\*))?[hL]?[a-z%])'
-    'name': 'constant.other.placeholder.powershell'
-  'dotted_name':
-    'begin': '(?=[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)*)'
-    'end': '(?![A-Za-z0-9_\\.])'
-    'patterns': [
-      {
-        'begin': '(\\.)(?=[A-Za-z_][A-Za-z0-9_]*)'
-        'end': '(?![A-Za-z0-9_])'
-        'patterns': [
-          {
-            'include': '#generic_names'
-          }
-        ]
-      }
-      {
-        'begin': '(?<!\\.)(?=[A-Za-z_][A-Za-z0-9_]*)'
-        'end': '(?![A-Za-z0-9_])'
-        'patterns': [
-          {
-            'include': '#generic_names'
-          }
-        ]
-      }
-    ]
-  'entity_name_class':
-    'patterns': [
-      {
-        'include': '#generic_names'
-      }
-    ]
-  'entity_name_function':
-    'patterns': [
-      {
-        'include': '#generic_names'
-      }
-    ]
-  'function_name':
-    'patterns': [
-      {
-        'include': '#generic_names'
-      }
-    ]
-  'generic_names':
-    'match': '[A-Za-z_][A-Za-z0-9_]*'
-  'keyword_arguments':
-    'begin': '\\b([a-zA-Z_][a-zA-Z_0-9]*)\\s*(=)(?!=)'
-    'beginCaptures':
-      '1':
-        'name': 'variable.parameter.function.powershell'
-      '2':
-        'name': 'keyword.operator.assignment.powershell'
-    'end': '\\s*(?:(,)|(?=$\\n?|[\\)]))'
-    'endCaptures':
-      '1':
-        'name': 'punctuation.separator.parameters.powershell'
-    'patterns': [
-      {
-        'include': '$self'
-      }
-    ]
-  'line_continuation':
-    'captures':
-      '1':
-        'name': 'punctuation.separator.continuation.line.powershell'
-    'match': '(`)(\\s*)$\\n?'
-  'regular_expressions':
-    'comment': 'Changed disabled to 1 to turn off syntax highlighting in “r” strings.'
-    'disabled': 0
-    'patterns': [
-      {
-        'include': 'source.regexp.powershell'
-      }
-    ]
-  'embedded-variable' :
-    'patterns': [
-      {
-        'captures':
-          '1':
-            'name': 'embedded.punctuation.variable.begin.powershell'
-        'match': '(?<!`)(\\$)[a-zA-Z_][a-zA-Z0-9_]*?\\b'
-        'name': 'embedded.variable.other.powershell'
-      }
-    ]
-  'string_quoted_double':
-    'patterns': [
-      {
-        'begin': '(")(?=\\s*(SELECT|INSERT|UPDATE|DELETE|CREATE|REPLACE|ALTER))'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.string.begin.powershell'
-        'comment': 'double quoted string'
-        'end': '((?<=")(")|")'
-        'endCaptures':
-          '1':
-            'name': 'punctuation.definition.string.end.powershell'
-          '2':
-            'name': 'meta.empty-string.double.powershell'
-        'name': 'string.quoted.double.block.sql.powershell'
-        'patterns': [
-          {
-            'include': '#constant_placeholder'
-          }
-          {
-            'include': '#escaped_char'
-          }
-        ]
-      }
-      {
-        'begin': '(")(?=\\s*(SELECT|INSERT|UPDATE|DELETE|CREATE|REPLACE|ALTER))'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.string.begin.powershell'
-        'comment': 'double quoted string'
-        'end': '((?<=")(")|")|(\\n)'
-        'endCaptures':
-          '1':
-            'name': 'punctuation.definition.string.end.powershell'
-          '2':
-            'name': 'meta.empty-string.double.powershell'
-          '3':
-            'name': 'invalid.illegal.unclosed-string.powershell'
-        'name': 'string.quoted.double.single-line.sql.powershell'
-        'patterns': [
-          {
-            'include': '#constant_placeholder'
-          }
-          {
-            'include': '#escaped_char'
-          }
-        ]
-      }
-      {
-        'begin': '(""")'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.string.begin.powershell'
-        'comment': 'double quoted string'
-        'end': '((?<=""")(")""|""")'
-        'endCaptures':
-          '1':
-            'name': 'punctuation.definition.string.end.powershell'
-          '2':
-            'name': 'meta.empty-string.double.powershell'
-        'name': 'string.quoted.double.block.powershell'
-        'patterns': [
-          {
-            'include': '#constant_placeholder'
-          }
-          {
-            'include': '#escaped_char'
-          }
-        ]
-      }
-      {
-        'begin': '(")'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.string.begin.powershell'
-        'comment': 'double quoted string'
-        'end': '((?<=")(")|")|(\\n)'
-        'endCaptures':
-          '1':
-            'name': 'punctuation.definition.string.end.powershell'
-          '2':
-            'name': 'meta.empty-string.double.powershell'
-          '3':
-            'name': 'invalid.illegal.unclosed-string.powershell'
-        'name': 'string.quoted.double.single-line.powershell'
-        'patterns': [
-          {
-            'include': '#constant_placeholder'
-          }
-          {
-            'include': '#escaped_char'
-          }
-          {
-            'include': '#embedded-variable'
-          }
-        ]
-      }
-    ]
-  'string_quoted_single':
-    'patterns': [
-      {
-        'captures':
-          '1':
-            'name': 'punctuation.definition.string.begin.powershell'
-          '2':
-            'name': 'punctuation.definition.string.end.powershell'
-          '3':
-            'name': 'meta.empty-string.single.powershell'
-        'match': '(?<!\')(\')((\'))(?!\')'
-        'name': 'string.quoted.single.single-line.powershell'
-      }
-      {
-        'begin': '(\'\'\')(?=\\s*(SELECT|INSERT|UPDATE|DELETE|CREATE|REPLACE|ALTER))'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.string.begin.powershell'
-        'comment': 'single quoted string'
-        'end': '((?<=\'\'\')(\')\'\'|\'\'\')'
-        'endCaptures':
-          '1':
-            'name': 'punctuation.definition.string.end.powershell'
-          '2':
-            'name': 'meta.empty-string.single.powershell'
-        'name': 'string.quoted.single.block.powershell'
-        'patterns': [
-          {
-            'include': '#constant_placeholder'
-          }
-          {
-            'include': '#escaped_char'
-          }
-        ]
-      }
-      {
-        'begin': '(\')(?=\\s*(SELECT|INSERT|UPDATE|DELETE|CREATE|REPLACE|ALTER))'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.string.begin.powershell'
-        'comment': 'single quoted string'
-        'end': '(\')|(\\n)'
-        'endCaptures':
-          '1':
-            'name': 'punctuation.definition.string.end.powershell'
-          '2':
-            'name': 'invalid.illegal.unclosed-string.powershell'
-        'name': 'string.quoted.single.single-line.powershell'
-        'patterns': [
-          {
-            'include': '#constant_placeholder'
-          }
-          {
-            'include': '#escaped_char'
-          }
-        ]
-      }
-      {
-        'begin': '(\'\'\')'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.string.begin.powershell'
-        'comment': 'single quoted string'
-        'end': '((?<=\'\'\')(\')\'\'|\'\'\')'
-        'endCaptures':
-          '1':
-            'name': 'punctuation.definition.string.end.powershell'
-          '2':
-            'name': 'meta.empty-string.single.powershell'
-        'name': 'string.quoted.single.block.powershell'
-        'patterns': [
-          {
-            'include': '#constant_placeholder'
-          }
-          {
-            'include': '#escaped_char'
-          }
-        ]
-      }
-      {
-        'begin': '(\')'
-        'beginCaptures':
-          '1':
-            'name': 'punctuation.definition.string.begin.powershell'
-        'comment': 'single quoted string'
-        'end': '(\')|(\\n)'
-        'endCaptures':
-          '1':
-            'name': 'punctuation.definition.string.end.powershell'
-          '2':
-            'name': 'invalid.illegal.unclosed-string.powershell'
-        'name': 'string.quoted.single.single-line.powershell'
-        'patterns': [
-          {
-            'include': '#constant_placeholder'
-          }
-          {
-            'include': '#escaped_char'
-          }
-        ]
-      }
-    ]
-  'strings':
-    'patterns': [
-      {
-        'include': '#string_quoted_double'
-      }
-      {
-        'include': '#string_quoted_single'
-      }
-      {
-        'include': '#escaped_char'
-      }
-    ]
+      ]
+
+    commands:
+      patterns: [
+        {
+          comment: "Verb-Noun pattern:"
+          match: "(?:(\\p{L}|\\d|_|-|\\\\|\\:)*\\\\)?\\b(?i:Add|Approve|Assert|Backup|Block|Checkpoint|Clear|Close|Compare|Complete|Compress|Confirm|Connect|Convert|ConvertFrom|ConvertTo|Copy|Debug|Deny|Disable|Disconnect|Dismount|Edit|Enable|Enter|Exit|Expand|Export|Find|Format|Get|Grant|Group|Hide|Import|Initialize|Install|Invoke|Join|Limit|Lock|Measure|Merge|Mount|Move|New|Open|Optimize|Out|Ping|Pop|Protect|Publish|Push|Read|Receive|Redo|Register|Remove|Rename|Repair|Request|Reset|Resize|Resolve|Restart|Restore|Resume|Revoke|Save|Search|Select|Send|Set|Show|Skip|Split|Start|Step|Stop|Submit|Suspend|Switch|Sync|Test|Trace|Unblock|Undo|Uninstall|Unlock|Unprotect|Unpublish|Unregister|Update|Use|Wait|Watch|Write)\\-.+?(?:\\.(?i:exe|cmd|bat|ps1))?\\b"
+          name: "support.function.powershell"
+        }
+        {
+          comment: "Builtin cmdlets with reserved verbs"
+          match: "(?<!\\w)(?i:foreach-object)(?!\\w)"
+          name: "support.function.powershell"
+        }
+      ]
+
+    commentEmbeddedDocs:
+      patterns: [
+        {
+          captures:
+            1:
+              name: "constant.string.documentation.powershell"
+
+            2:
+              name: "keyword.operator.documentation.powershell"
+
+          match: "(?i:\\s*(\\.)(SYNOPSIS|DESCRIPTION|EXAMPLE|INPUTS|OUTPUTS|NOTES|LINK|COMPONENT|FUNCTIONALITY))"
+          name: "comment.documentation.embedded.powershell"
+        }
+        {
+          captures:
+            1:
+              name: "constant.string.documentation.powershell"
+
+            2:
+              name: "keyword.operator.documentation.powershell"
+
+            3:
+              name: "keyword.operator.documentation.powershell"
+
+          match: "(?i:\\s*(\\.)(PARAMETER|FORWARDHELPTARGETNAME|FORWARDHELPCATEGORY|REMOTEHELPRUNSPACE|EXTERNALHELP)\\s+([a-z0-9-_]+))"
+          name: "comment.documentation.embedded.powershell"
+        }
+        {
+          captures:
+            1:
+              name: "constant.string.documentation.powershell"
+
+            2:
+              name: "keyword.operator.documentation.powershell"
+
+            3:
+              name: "string.quoted.double.heredoc.powershell"
+
+          match: "(?i:requires\\s+-(Version\\s+\\d(.\\d+)?|Assembly\\s+(.*)|Module\\s+(.*)|PsSnapIn\\s+(.*)|ShellId\\s+(.*)))"
+          name: "comment.documentation.embedded.powershell"
+        }
+      ]
+
+    doubleQuotedStringEscapes:
+      patterns: [
+        {
+          match: "`[0abnfrvt\"'$`]"
+          name: "constant.character.escape.powershell"
+        }
+        {
+          match: "\"\""
+          name: "constant.character.escape.powershell"
+        }
+      ]
+
+    function:
+      begin: "((?i:function|configuration|workflow))\\s+((?:\\p{L}|\\d|_|-|\\.)+)"
+      beginCaptures:
+        0:
+          name: "meta.function"
+
+        1:
+          name: "storage.type"
+
+        2:
+          name: "entity.name.function"
+
+      end: "\\{|\\("
+
+    interpolatedStringContent:
+      begin: "\\("
+      beginCaptures:
+        0:
+          name: "keyword.other.powershell"
+
+      contentName: "interpolated.simple.source.powershell"
+      end: "\\)"
+      endCaptures:
+        0:
+          name: "keyword.other.powershell"
+
+      patterns: [
+        {
+          include: "$self"
+        }
+        {
+          include: "#interpolation"
+        }
+        {
+          include: "#interpolatedStringContent"
+        }
+      ]
+
+    interpolation:
+      begin: "(\\$)\\("
+      beginCaptures:
+        0:
+          name: "keyword.other.powershell"
+
+      contentName: "interpolated.complex.source.powershell"
+      end: "\\)"
+      endCaptures:
+        0:
+          name: "keyword.other.powershell"
+
+      patterns: [
+        {
+          include: "$self"
+        }
+        {
+          include: "#interpolation"
+        }
+        {
+          include: "#interpolatedStringContent"
+        }
+      ]
+
+    numericConstant:
+      patterns: [
+        {
+          captures:
+            1:
+              name: "keyword.operator.math.powershell"
+
+            2:
+              name: "support.constant.powershell"
+
+            3:
+              name: "keyword.other.powershell"
+
+          match: "(?<!\\w)(?i:(0x)([a-f0-9]+)((?i:L)?(?i:[kmgtp]b)?))(?!\\w)"
+          name: "constant.numeric.hexadecimal.powershell"
+        }
+        {
+          captures:
+            1:
+              name: "support.constant.powershell"
+
+            2:
+              name: "keyword.operator.math.powershell"
+
+            3:
+              name: "support.constant.powershell"
+
+            4:
+              name: "keyword.other.powershell"
+
+            5:
+              name: "keyword.other.powershell"
+
+          match: "(?<!\\w)(?i:(\\d*\\.?\\d+)(?:((?i:E)[+-]?)(\\d+))?((?i:[DL])?)((?i:[kmgtp]b)?))(?!\\w)"
+          name: "constant.numeric.scientific.powershell"
+        }
+      ]
+
+    scriptblock:
+      begin: "\\{"
+      end: "\\}"
+      name: "meta.scriptblock.powershell"
+      patterns: [include: "$self"]
+
+    type:
+      begin: "\\["
+      beginCaptures:
+        0:
+          name: "entity.other.attribute-name"
+
+      comment: "name should be entity.name.type but default schema doesn't have a good color for it"
+      end: "\\]"
+      endCaptures:
+        0:
+          name: "entity.other.attribute-name"
+
+      patterns: [
+        {
+          match: "(\\p{L}|\\.|``\\d+)+?"
+          name: "entity.other.attribute-name"
+        }
+        {
+          include: "$self"
+        }
+      ]
+
+    variable:
+      patterns: [
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "constant.language.powershell"
+
+          comment: "These are special constants."
+          match: "(\\$)(?i:(False|Null|True))\\b"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "support.constant.variable.powershell"
+
+            3:
+              name: "entity.name.function.invocation.powershell"
+
+          comment: "These are the other built-in constants."
+          match: "(\\$)(?i:(Error|ExecutionContext|Host|Home|PID|PsHome|PsVersionTable|ShellID))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?\\b"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "support.constant.automatic.powershell"
+
+            3:
+              name: "entity.name.function.invocation.powershell"
+
+          comment: "Automatic variables are not constants, but they are read-only. In monokai (default) color schema support.variable doesn't have color, so we use constant."
+          match: "(\\$)(?i:(\\$|\\^|\\?|_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?\\b"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "variable.language.powershell"
+
+            3:
+              name: "entity.name.function.invocation.powershell"
+
+          comment: "Style preference variables as language variables so that they stand out."
+          match: "(\\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|ProgressPreference|PsCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|VerbosePreference|WarningPreference|WhatIfPreference))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?\\b"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "storage.modifier.scope.powershell"
+
+            3:
+              name: "variable.other.normal.powershell"
+
+            4:
+              name: "entity.name.function.invocation.powershell"
+
+          match: "(?i:(\\$)(global|local|private|script|using|workflow):((?:\\p{L}|\\d|_)+))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "storage.modifier.scope.powershell"
+
+            3:
+              name: "variable.other.readwrite.powershell"
+
+            4:
+              name: "keyword.other.powershell"
+
+            5:
+              name: "entity.name.function.invocation.powershell"
+
+          match: "(?i:(\\$\\{)(global|local|private|script|using|workflow):([^}]*[^}`])(\\}))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "support.variable.drive.powershell"
+
+            3:
+              name: "variable.other.readwrite.powershell"
+
+            4:
+              name: "entity.name.function.invocation.powershell"
+
+          match: "(?i:(\\$)((?:\\p{L}|\\d|_)+:)?((?:\\p{L}|\\d|_)+))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "support.variable.drive.powershell"
+
+            3:
+              name: "variable.other.readwrite.powershell"
+
+            4:
+              name: "keyword.other.powershell"
+
+            5:
+              name: "entity.name.function.invocation.powershell"
+
+          match: "(?i:(\\$\\{)((?:\\p{L}|\\d|_)+:)?([^}]*[^}`])(\\}))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?"
+        }
+      ]
+
+    variableNoProperty:
+      patterns: [
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "constant.language.powershell"
+
+          comment: "These are special constants."
+          match: "(\\$)(?i:(False|Null|True))\\b"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "support.constant.variable.powershell"
+
+            3:
+              name: "entity.name.function.invocation.powershell"
+
+          comment: "These are the other built-in constants."
+          match: "(\\$)(?i:(Error|ExecutionContext|Host|Home|PID|PsHome|PsVersionTable|ShellID))\\b"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "support.variable.automatic.powershell"
+
+            3:
+              name: "entity.name.function.invocation.powershell"
+
+          comment: "Automatic variables are not constants, but they are read-only..."
+          match: "(\\$)(?i:(\\$|\\^|\\?|_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This))\\b"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "variable.language.powershell"
+
+            3:
+              name: "entity.name.function.invocation.powershell"
+
+          comment: "Style preference variables as language variables so that they stand out."
+          match: "(\\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|ProgressPreference|PsCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|VerbosePreference|WarningPreference|WhatIfPreference))\\b"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "storage.modifier.scope.powershell"
+
+            3:
+              name: "variable.other.normal.powershell"
+
+            4:
+              name: "entity.name.function.invocation.powershell"
+
+          match: "(?i:(\\$)(global|local|private|script|using|workflow):((?:\\p{L}|\\d|_)+))"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "storage.modifier.scope.powershell"
+
+            3:
+              name: "variable.other.readwrite.powershell"
+
+            4:
+              name: "keyword.other.powershell"
+
+            5:
+              name: "entity.name.function.invocation.powershell"
+
+          match: "(?i:(\\$\\{)(global|local|private|script|using|workflow):([^}]*[^}`])(\\}))"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "support.variable.drive.powershell"
+
+            3:
+              name: "variable.other.readwrite.powershell"
+
+            4:
+              name: "entity.name.function.invocation.powershell"
+
+          match: "(?i:(\\$)((?:\\p{L}|\\d|_)+:)?((?:\\p{L}|\\d|_)+))"
+        }
+        {
+          captures:
+            1:
+              name: "keyword.other.powershell"
+
+            2:
+              name: "support.variable.drive.powershell"
+
+            3:
+              name: "variable.other.readwrite.powershell"
+
+            4:
+              name: "keyword.other.powershell"
+
+            5:
+              name: "entity.name.function.invocation.powershell"
+
+          match: "(?i:(\\$\\{)((?:\\p{L}|\\d|_)+:)?([^}]*[^}`])(\\}))"
+        }
+      ]
+
+  scopeName: "source.powershell"
+}


scroll to top of entry

scroll to top of entry

--- editor.coffee
+++ editor.coffee
@@ -96,9 +96,14 @@
   editor.on 'viewportChange', ->
     $output.css 'padding-bottom': ($code.height() + 60 ) + 'px'

-  scroll_to_result = ->
+  scroll_to_result = ($result)->
+    top = if $result?
+      $result.offset().top
+    else
+      $(document).height()
+
     setTimeout ->
-      $('html, body').scrollTop $(document).height()
+      $('html, body').scrollTop top
     , 10

   window.create_ns = (context) ->
@@ -470,10 +475,10 @@
     context =
       $result: $result
       success: ->
-        scroll_to_result()
+        scroll_to_result $entry
         _lead_finished
       failure: ->
-        scroll_to_result()
+        scroll_to_result $entry
         _lead_finished

     ns = create_ns context


fixed order of home-dir expansion and project resolution

The uncrutify config path was first resolved against the current project folder.
This would `expand-home-dir` to not expand the home folder.

This patch should fix issues #747 and #818.

--- src/beautifiers/uncrustify/index.coffee
+++ src/beautifiers/uncrustify/index.coffee
@@ -37,6 +37,8 @@
         editor = atom.workspace.getActiveTextEditor()
         if editor?
           basePath = path.dirname(editor.getPath())
+          # Expand Home Directory in Config Path
+          configPath = expandHomeDir(configPath)
           # console.log(basePath);
           configPath = path.resolve(basePath, configPath)
           resolve configPath
@@ -44,9 +46,6 @@
           reject(new Error("No Uncrustify Config Path set! Please configure Uncrustify with Atom Beautify."))
     )
     .then((configPath) =>
-
-      # Expand Home Directory in Config Path
-      configPath = expandHomeDir(configPath)

       # Select Uncrustify language
       lang = "C" # Default is C


Only iterate over coverageDescriptions if there are any

Signed-off-by: Marko Locher <9efe9a5a1da5f41a4eb7599f2715dc24abf5bbc8@locher.at>

--- src/plugins/eowcs.coffee
+++ src/plugins/eowcs.coffee
@@ -12,8 +12,9 @@

                 datasets = []
                 response = WCS.Core.Parse.parse(response.responseXML)
-                for coverage in response.coverageDescriptions
-                    datasets.push([ new Date(coverage.timePeriod[0]), new Date(coverage.timePeriod[1]) ])
+                if(response.coverageDescriptions? and response.coverageDescriptions.length > 0)
+                    for coverage in response.coverageDescriptions
+                        datasets.push([ new Date(coverage.timePeriod[0]), new Date(coverage.timePeriod[1]) ])

                 callback(@options.dataset, datasets)
             )


CoffeeScript syntax fix

CoffeeScript syntax fix

--- src/plugins/image.coffee
+++ src/plugins/image.coffee
@@ -242,28 +242,4 @@
         uploadUrl: @options.uploadUrl
         imageWidget: @
         entity: @options.entity
-
-    ###
-    insertImage = () ->
-    # This may need to insert an image that does not have the same URL as
-    # the preview image, since it may be a different size
-      # Check if we have a selection and fall back to @lastSelection otherwise
-      try
-        if not widget.options.editable.getSelection()
-        throw new Error "SelectionNotSet"
-      catch error
-        widget.options.editable.restoreSelection(widget.lastSelection)
-
-      document.execCommand "insertImage", null, jQuery(this).attr('src')
-      img = document.getSelection().anchorNode.firstChild
-      jQuery(img).attr "alt", jQuery(".caption").value
-
-      triggerModified = () ->
-        widget.element.trigger "hallomodified"
-      window.setTimeout triggerModified, 100
-      widget._closeDialog()
-
-      addImage = "##{widget.options.uuid}-#{widget.widgetName-addimage"
-      @options.dialog.find(".halloimage-activeImage, addImage).click insertImage
-    ###
  )(jQuery)


Use full URL for reporting_url [#131441319]

--- src/code/providers/lara-provider.coffee
+++ src/code/providers/lara-provider.coffee
@@ -228,7 +228,9 @@
         metadata.providerData = _.merge({}, docStoreMetadata, { url: runStateUrl })

         # build the reporting_url
-        codapUrl = window.location.origin or "${window.location.protocol}//${window.location.host}"
+        codapUrl = if window.location.origin \
+                    then "#{window.location.origin}#{window.location.pathname}" \
+                    else "#{window.location.protocol}//#{window.location.host}#{window.location.pathname}"
         reportUrlLaraParams =
           recordid: createResponse.id
           accessKeys:


Get correct step units when we have collectors

This changes `simulation-store` to pass back the user-selected units
whenever there is a collector in he graph, and not just while running
a period-simulation, as before.

The only effect of this should be that, when we click the Graph button
on a node, we will correctly send CODAP the correct units for the
time axis of the graph.

[#133192489]

--- src/code/stores/simulation-store.coffee
+++ src/code/stores/simulation-store.coffee
@@ -102,7 +102,7 @@
     @_runSimulation()

   stepUnits: ->
-    if @settings.isRecordingPeriod
+    if @settings.graphHasCollector
       @settings.stepUnits
     else
       TimeUnits.defaultUnit # "STEPS" when not specified or not running time interval


Add a few levels

Add a few levels


spelling

spelling

--- src/scripts/drush.coffee
+++ src/scripts/drush.coffee
@@ -157,7 +157,7 @@
     else
       msg.send "'drush " + msg.match[1] + "' is and invalid command. Please try again."

-# Instanciate the drush interface
+# Instantiate the drush interface
 drush = drush_interface()

 # Hook in with hobot


Remove unnecessary deactivation handler

Remove unnecessary deactivation handler

References: atom/tree-view#967

--- lib/archive-editor.coffee
+++ lib/archive-editor.coffee
@@ -2,7 +2,7 @@

 fs = require 'fs-plus'
 Serializable = require 'serializable'
-{Emitter, File} = require 'atom'
+{Disposable, Emitter, File} = require 'atom'
 FileIcons = require './file-icons'

 isPathSupported = (filePath) ->
@@ -38,7 +38,7 @@

   @consumeFileIcons: (service) ->
     FileIcons.setService(service)
-    @fileIconsDisposable = service.onWillDeactivate ->
+    new Disposable ->
       FileIcons.resetService()

   getPath: ->


Import SelectListView instead of SelectList

--- lib/bookmarks-view.coffee
+++ lib/bookmarks-view.coffee
@@ -1,9 +1,9 @@
 path = require 'path'

-{$$, SelectList} = require 'atom'
+{$$, SelectListView} = require 'atom'

 module.exports =
-class BookmarksView extends SelectList
+class BookmarksView extends SelectListView
   @viewClass: -> "#{super} bookmarks-view overlay from-top"

   filterKey: 'filterText'


postgres: test patch access control

postgres: test patch access control

--- src/smc-hub/test/postgres-user-queries-patches.coffee
+++ src/smc-hub/test/postgres-user-queries-patches.coffee
@@ -97,3 +97,135 @@
                 expect(x.patches[0].time).toEqual(t3)
                 done(err)

+describe 'access control tests on patches table -- ', ->
+    before(setup)
+    after(teardown)
+
+    # SETUP
+    accounts = projects = string_id = undefined
+    path = 'a.txt'
+    it 'creates 3 accounts', (done) ->
+        create_accounts 3, (err, x) -> accounts=x; done(err)
+    it 'creates 2 projects', (done) ->
+        create_projects 2, accounts[0], (err, x) -> projects=x; done(err)
+    it 'creates a syncstring', (done) ->
+        string_id = db.sha1(projects[0], path)
+        db.user_query
+            account_id : accounts[0]
+            query : {syncstrings:{project_id:projects[0], path:path, users:accounts}}
+            cb    : done
+    t0 = misc.minutes_ago(10)
+    patch0 = misc.to_json({a:'patch'})
+    it 'creates a patch', (done) ->
+        db.user_query
+            account_id : accounts[0]
+            query      : {patches:{string_id:string_id, time:t0, user:0, patch:patch0}}
+            cb         : done
+
+    it 'tries to read as anon to patches table and fails', (done) ->
+        db.user_query
+            query : {patches:{string_id:null, time:null, user:null, patch:null}}
+            cb    : (err) ->
+                expect(err).toEqual("anonymous get queries not allowed for table 'patches'")
+                done()
+
+    it 'tries to write as anon to patches table and fails', (done) ->
+        db.user_query
+            query : {patches:{string_id:string_id, time:new Date(), user:0, patch:patch0}}
+            cb    : (err) ->
+                expect(err).toEqual("no anonymous set queries")
+                done()
+
+    it 'tries to write as user not on the project and fails', (done) ->
+        db.user_query
+            account_id : accounts[1]
+            query : {patches:{string_id:string_id, time:new Date(), user:0, patch:patch0}}
+            cb    : (err) ->
+                expect(err).toEqual("user must be an admin")
+                done()
+
+    it 'tries to write as different project and fails', (done) ->
+        db.user_query
+            project_id : projects[1]
+            query : {patches:{string_id:string_id, time:new Date(), user:0, patch:patch0}}
+            cb    : (err) ->
+                expect(err).toEqual("project not allowed to write to syncstring in different project")
+                done()
+
+    it 'makes account1 an admin', (done) ->
+        db.make_user_admin(account_id:accounts[1], cb:done)
+
+    it 'tries to write as admin and succeeds', (done) ->
+        db.user_query
+            account_id : accounts[1]
+            query : {patches:{string_id:string_id, time:misc.minutes_ago(2), user:0, patch:patch0}}
+            cb    : done
+
+    it 'makes account2 a collab', (done) ->
+        db.add_user_to_project(project_id:projects[0], account_id:accounts[2], cb:done)
+
+    it 'tries to write as collab and succeeds', (done) ->
+        db.user_query
+            account_id : accounts[2]
+            query      : {patches:{string_id:string_id, time:misc.minutes_ago(3), user:0, patch:patch0}}
+            cb         : done
+
+    it 'tries to write as same project and succeeds', (done) ->
+        db.user_query
+            project_id : projects[0]
+            query : {patches:{string_id:string_id, time:misc.minutes_ago(1), user:0, patch:patch0}}
+            cb    : (err) ->
+                done(err)
+
+    it 'tries to write negative user number and fails', (done) ->
+        db.user_query
+            project_id : projects[0]
+            query : {patches:{string_id:string_id, time:misc.minutes_ago(4), user:-1, patch:patch0}}
+            cb    : (err) ->
+                expect(err).toEqual('postgresql error: new row for relation "patches" violates check constraint "patches_user_check"')
+                done()
+
+    it 'tries to write invalid string_id and fails', (done) ->
+        db.user_query
+            project_id : projects[0]
+            query : {patches:{string_id:'sage', time:misc.minutes_ago(4), user:0, patch:patch0}}
+            cb    : (err) ->
+                expect(err).toEqual('no such syncstring')
+                done()
+
+    it 'tries to write invalid time and fails', (done) ->
+        db.user_query
+            project_id : projects[0]
+            query : {patches:{string_id:string_id, time:'sage', user:0, patch:patch0}}
+            cb    : (err) ->
+                expect(err).toEqual('postgresql error: invalid input syntax for type timestamp: "sage"')
+                done()
+
+    it 'tries to write invalid sent type and fails', (done) ->
+        db.user_query
+            project_id : projects[0]
+            query : {patches:{string_id:string_id, time:misc.minutes_ago(4), user:0, sent:'sage', patch:patch0}}
+            cb    : (err) ->
+                expect(err).toEqual('postgresql error: invalid input syntax for type timestamp: "sage"')
+                done()
+
+    it 'tries to write invalid prev type and fails', (done) ->
+        db.user_query
+            project_id : projects[0]
+            query : {patches:{string_id:string_id, time:misc.minutes_ago(4), user:0, prev:'sage', patch:patch0}}
+            cb    : (err) ->
+                expect(err).toEqual('postgresql error: invalid input syntax for type timestamp: "sage"')
+                done()
+
+    it 'tries to change past author and fails', (done) ->
+        db.user_query
+            account_id : accounts[1]
+            query      : {patches:{string_id:string_id, time:t0, user:1, patch:patch0}}
+            cb         : (err) ->
+                expect(err).toEqual('you may not change the author of a patch from 0 to 1')
+                done()
+
+
+
+
+


Implemented ability to join world with a character

--- src/wrathnet/expansions/wotlk/handlers/world-handler.coffee
+++ src/wrathnet/expansions/wotlk/handlers/world-handler.coffee
@@ -15,6 +15,7 @@
   # Imports
   BigNum = WrathNet.crypto.BigNum
   Crypt = WrathNet.crypto.Crypt
+  GUID = WrathNet.datastructures.GUID
   ObjectUtil = WrathNet.utils.ObjectUtil
   SHA1 = WrathNet.crypto.hash.SHA1
   WorldOpcode = WrathNet.expansions.wotlk.enums.WorldOpcode
@@ -55,6 +56,17 @@
       @_crypt.encrypt(new Uint8Array(packet.buffer, 0, WorldPacket.HEADER_SIZE_OUTGOING))

     return super(packet)
+
+  # Attempts to join world with given character
+  join: (character) ->
+    if character
+      console.info 'joining world with', character.toString()
+
+      wp = new WorldPacket(WorldOpcode.CMSG_PLAYER_LOGIN, WorldPacket.HEADER_SIZE_OUTGOING + GUID.LENGTH)
+      wp.writeGUID(character.guid)
+      return @send wp
+
+    return false

   # Data received handler
   dataReceived: (socket) ->


Fix #1

Fix #1

--- lib/linter-crystal.coffee
+++ lib/linter-crystal.coffee
@@ -23,7 +23,7 @@
   constructor: (editor)->
     super(editor)

-    atom.config.observe 'linter-crystal.crystalExecutablePath', =>
+    @pathListen = atom.config.observe 'linter-crystal.crystalExecutablePath', =>
       @executablePath = atom.config.get 'linter-crystal.crystalExecutablePath'

   lintFile: (filePath, callback) ->
@@ -32,6 +32,6 @@
     super(filePath, callback)

   destroy: ->
-    atom.config.unobserve 'linter-crystal.crystalExecutablePath'
+    @pathListen.dispose()

 module.exports = LinterCrystal


Add: method to update product type. Update: made enum validation option configurable.

--- lib/product-import.coffee
+++ lib/product-import.coffee
@@ -57,6 +57,7 @@
       created: 0
       updated: 0
       failed: 0
+      productTypeUpdated: 0
       errorDir: @errorDir

   summaryReport: (filename) ->
@@ -148,14 +149,15 @@

   _createOrUpdate: (productsToProcess, existingProducts) ->
     debug 'Products to process: %j', {toProcess: productsToProcess, existing: existingProducts}
-
+    enumUpdateActions = {}
     posts = _.map productsToProcess, (prodToProcess) =>
       existingProduct = @_isExistingEntry(prodToProcess, existingProducts)
       if existingProduct?
         @_fetchSameForAllAttributesOfProductType(prodToProcess.productType)
         .then (sameForAllAttributes) =>
-          @enumValidator.validateProduct(prodToProcess, @_cache.productType[prodToProcess.productType.id])
-          .then =>
+          @_validateEnums(prodToProcess, @_cache.productType[prodToProcess.productType.id])
+          .then (updateActions) =>
+            if updateActions and _.size(updateActions.actions) > 0 then @_updateEnumUpdateActions(enumUpdateActions, updateActions)
             @_prepareUpdateProduct(prodToProcess, existingProduct).then (preparedProduct) =>
               synced = @sync.buildActions(preparedProduct, existingProduct, sameForAllAttributes)
               if synced.shouldUpdate()
@@ -165,12 +167,47 @@
       else
         @_prepareNewProduct(prodToProcess)
         .then (product) =>
-          @enumValidator.validateProduct(product, @_cache.productType[product.productType.id])
-          .then =>
+          @_validateEnums(product, @_cache.productType[product.productType.id])
+          .then (updateActions) =>
+            if updateActions and _.size(updateActions) > 0 then @_updateEnumUpdateActions(enumUpdateActions, updateActions)
             @client.products.create(product)

-    debug 'About to send %s requests', _.size(posts)
-    Promise.settle(posts)
+    @_updateProductType(enumUpdateActions)
+    .then ->
+      debug 'About to send %s requests', _.size(posts)
+      Promise.settle(posts)
+
+  _validateEnums: (product, productType) =>
+    if @ensureEnums
+      @enumValidator.validateProduct(product, productType)
+    else
+      Promise.resolve()
+
+  _updateProductType: (enumUpdateActions) =>
+    new Promise (resolve) =>
+      if _.isEmpty(enumUpdateActions)
+        resolve()
+      else
+        @logger.info "Updating product type(s): #{_.keys(enumUpdateActions)}"
+        Promise.all [
+          for productTypeId in _.keys(enumUpdateActions)
+            updateRequest =
+              version: @_cache.productType[productTypeId].version
+              actions: enumUpdateActions[productTypeId]
+            @client.productTypes.byId(@_cache.productType[productTypeId].id).update(updateRequest)
+            .then (updatedProductType) =>
+              @_cache.productType[productTypeId] = updatedProductType.body
+              @_summary.productTypeUpdated++
+        ]
+        .then ->
+          resolve()
+
+
+  _updateEnumUpdateActions: (enumUpdateActions, updateActions) ->
+    if enumUpdateActions[updateActions.productTypeId]
+      enumUpdateActions[updateActions.productTypeId] = enumUpdateActions[updateActions.productTypeId].concat(updateActions.actions)
+    else
+      enumUpdateActions[updateActions.productTypeId] = updateActions.actions

   _fetchSameForAllAttributesOfProductType: (productType) =>
     new Promise (resolve) =>


Added has to Many

Added has to Many

--- lib/relations/many.coffee
+++ lib/relations/many.coffee
@@ -76,7 +76,9 @@
       return callback(new Error "Model not found. Id #{@foreign_key}") if not models.length
       callback(null, models)

-  itemId: (model, item) ->
+  has: (model, key, item) ->
+    collection = model.attributes[key]
+    return !!collection.get(Utils.itemId(item))

   add: (model, item) ->
     collection = model.get(@key)


Hide config settings defined by language packages

--- lib/settings-panel.coffee
+++ lib/settings-panel.coffee
@@ -189,6 +189,9 @@
     return if name is 'disabledPackages' # Handled in the Packages panel
     return if name is 'customFileTypes'

+  if namespace is 'editor'
+    return if name in ['commentStart', 'commentEnd', 'increaseIndentPattern', 'decreaseIndentPattern', 'foldEndPattern'] # There's no global default for these, they are defined by language packages
+
   @div class: 'control-group', =>
     @div class: 'controls', =>
       schema = atom.config.getSchema("#{namespace}.#{name}")


Live update in all modes

Live update in all modes

--- lib/show-todo-view.coffee
+++ lib/show-todo-view.coffee
@@ -1,4 +1,4 @@
-{CompositeDisposable, TextEditor} = require 'atom'
+{CompositeDisposable} = require 'atom'
 {ScrollView} = require 'atom-space-pen-views'
 path = require 'path'
 fs = require 'fs-plus'
@@ -53,9 +53,6 @@
     @disposables.add pane.observeFlexScale (flexScale) =>
       @savePaneFlex(flexScale)

-    @disposables.add atom.config.observe 'todo-show.liveRefresh', (newValue) =>
-      @liveRefresh = newValue
-
     @disposables.add @model.onDidChangeSearchScope @setScopeButtonState
     @disposables.add @model.onDidStartSearch @startLoading
     @disposables.add @model.onDidFinishSearch @stopLoading
@@ -68,7 +65,7 @@
       @searchCount.text "#{nPaths} paths searched..."

     @disposables.add atom.workspace.onDidChangeActivePaneItem (item) =>
-      if item instanceof TextEditor and @model.scope is 'active'
+      if item?.constructor.name is 'TextEditor' and @model.scope is 'active'
         @model.search()

     @disposables.add atom.workspace.onDidAddTextEditor ({textEditor}) =>
@@ -76,6 +73,9 @@

     @disposables.add atom.workspace.onDidDestroyPaneItem ({item}) =>
       @model.search() if @model.scope is 'open'
+
+    @disposables.add atom.workspace.observeTextEditors (editor) =>
+      @disposables.add editor.onDidSave => @model.search()

     @scopeButton.on 'click', @toggleSearchScope
     @optionsButton.on 'click', @toggleOptions


Fixing issue with some getObject requests on feeds giving 400 or 500 errors due to missing Accept header

--- lib/utils/retsHttp.coffee
+++ lib/utils/retsHttp.coffee
@@ -46,10 +46,14 @@
       failCallback(error)
     else if responseCallback
       responseCallback(response)
+  request = {}
   if client.settings.method == 'POST'
-    stream = retsSession(form: queryOptions)
+    request.form = queryOptions
   else
-    stream = retsSession(qs: queryOptions)
+    request.qs = queryOptions
+  if methodName == 'getObject'
+    request.headers = { Accept: 'text/xml' }
+  stream = retsSession(request)
   stream.on 'error', errorHandler
   stream.on 'response', responseHandler


Improve stylus specs, and restore original stylus method after tests

--- test/units/config/initializers/templates/stylus.spec.coffee
+++ test/units/config/initializers/templates/stylus.spec.coffee
@@ -11,10 +11,15 @@

   beforeEach ->
     @stylusRenderCalled = false
-    safeStylus = require.cache[@stylusPath].exports
-    require.cache[@stylusPath].exports = =>
-      render: (tpl) =>
+    @safeStylus = require.cache[@stylusPath].exports
+    require.cache[@stylusPath].exports = (tpl) =>
+      render: (cb) =>
         @stylusRenderCalled = true
+        cb? null, 'irrelevant'
+
+  afterEach ->
+    require.cache[@stylusPath].exports = @safeStylus
+

   it 'should exist', ->
     expect(initializer).toBeDefined()
@@ -33,7 +38,8 @@

       describe 'when called', ->
         it 'should have called the stylus render method', ->
-          @render 'foo'
+          result = @render 'foo'
           expect(@stylusRenderCalled).toBeTruthy()
+          expect(result).toBe('irrelevant')


'npm run bundle' fixed

'npm run bundle' fixed

--- bundle.coffee
+++ bundle.coffee
@@ -21,7 +21,7 @@
 		verbose: true
 		path: "index.coffee"
 		test: (req) ->
-			/^(?:neft\-|\.)/.test(req)
+			/^(?:src\/|\.)/.test(req)
 	}, (err, bundle) ->
 		if err
 			return console.error err?.stack or err


Fix unitpay hook response

Fix unitpay hook response

--- server/api/setupApi.coffee
+++ server/api/setupApi.coffee
@@ -1351,7 +1351,7 @@
         success = true
         amount = data.orderSum
         await processPayment(order, success, amount, req.query)
-        res.send('OK')
+        res.json({result: {message: "OK"}})

     app.post '/api/paymentNotify', wrap (req, res) ->
         logger.info("paymentNotify #{req.body.OrderId}")


Update shit-candidates-say.coffee

Update shit-candidates-say.coffee

--- src/shit-candidates-say.coffee
+++ src/shit-candidates-say.coffee
@@ -29,6 +29,7 @@
     "candidate: I run a horse farm full time so I need to work remotely.",
     "candidate: I have to go, I have another phone interview in a minute.",
     "candidate: I created a charity because it's the only way children's hospitals will let me in."
+    "candidate: Stop! Look no further you have found the Perl developer that you need!"
   ]

 module.exports = (robot) ->


add git log

add git log

--- src/smc-webapp/editor_git.cjsx
+++ src/smc-webapp/editor_git.cjsx
@@ -128,6 +128,17 @@
                     @setState(git_diff : '')
                 else
                     @setState(git_diff : output.stdout)
+
+    update_log : () =>
+        @setState(git_log : 'updating...')
+        @exec
+            cmd  : "git"
+            args : ['log', '-20']
+            cb   : (err, output) =>
+                if err
+                    @setState(git_log : '')
+                else
+                    @setState(git_log : output.stdout)

     set_tab : (tab) =>
         @setState(tab:tab)
@@ -137,6 +148,8 @@
         if tab == 'commit'
             @update_status()
             @update_diff()
+        if tab == 'log'
+            @update_log()

 exports.init_redux = init_redux = (redux, project_id, filename) ->
@@ -160,6 +173,7 @@
             commit_message        : rtypes.string
             git_status            : rtypes.string
             git_diff              : rtypes.string
+            git_log               : rtypes.string

     propTypes :
         actions : rtypes.object
@@ -177,6 +191,14 @@
              title="Commit" tip="This tab lists all ">

                 <Octicon name="git-commit"/> Commit
+
+        </Tip>
+
+    render_log_header : ->
+        <Tip delayShow=1300
+             title="Log" tip="This tab lists all ">
+
+                <Octicon name="history"/> Log

         </Tip>

@@ -262,7 +284,24 @@
         <Panel header={head}>
             {
{@props.git_commit_return}
 if @props.git_commit_return}
         </Panel>
-
+
+    render_log_panel : ->
+        head =
+
+                git log
+                <Space/> <Space/>
+                <Button onClick={=>@props.actions.update_log()}>
+                    Refresh
+                </Button>
+                <Button onClick={=>@props.actions.setState(git_log:'')}>
+                    Clear
+                </Button>
+
+
+        <Panel header={head}>
+            {
{@props.git_log}
 if @props.git_log}
+        </Panel>
+
     render_status : ->
         head =

@@ -319,6 +358,15 @@
                 </Col>
             </Row>

+
+    render_log : ->
+

+            <Row>
+                <Col sm=12>
+                    {@render_log_panel()}
+                </Col>
+            </Row>
+

     render : ->

@@ -332,6 +380,10 @@

                     {@render_commit()}
                 </Tab>
+                <Tab eventKey={'log'} title={@render_log_header()}>
+

+                    {@render_log()}
+                </Tab>
             </Tabs>


Added cityId (aka location id) as an option to set location. This should solved sporadic location not found error some users had, issue #30 Fixed mapping for zone 3 as part of _mapZoneToKey()

--- http-app-protocol.coffee
+++ http-app-protocol.coffee
@@ -50,7 +50,7 @@
     _mapZoneToKey: (command) ->
       switch command[...2]
         when 'Z2' then return 'zone2'
-        when 'Z3' then return 'zone2'
+        when 'Z3' then return 'zone3'
         else return 'main'

     _triggerResponse: (command, param) ->


Fix errors

Fix errors

--- app/eventbrite.coffee
+++ app/eventbrite.coffee
@@ -12,14 +12,16 @@
 # @param [string] url
 # @return id or undefined
 _getEventId = (url) ->
-	id = (new RegExp(/eventbrite\.com\/e\/[A-Za-z0-9-]*-(\d+)+/g)).exec(url)?[1] or= undefined
+	id = (new RegExp(/eventbrite\.com\/e\/[A-Za-z0-9-]*-(\d+)+/g)).exec(url)?[1]
+	id ?= undefined
 	return id

 # Get ID for an eventbrite organiser
 # @param [string] url
 # @return id or undefined
 _getOrganiserId = (url) ->
-	id = (new RegExp(/eventbrite\.com\/o\/[A-Za-z0-9-]*-(\d+)+/g)).exec(url)?[1] or= undefined
+	id = (new RegExp(/eventbrite\.com\/o\/[A-Za-z0-9-]*-(\d+)+/g)).exec(url)?[1]
+	id ?= undefined
 	return id

 # Get events for an organiser
@@ -55,9 +57,9 @@
 						title: event.title
 						location:
 							address: "#{event.venue.address}, #{event.venue.city}, #{event.venue.country}"
-								name: event.venue.name
-								lon: event.venue.longitude
-								lat: event.venue.latitude
+							name: event.venue.name
+							lon: event.venue.longitude
+							lat: event.venue.latitude

 			db.set
 				events: events
@@ -73,6 +75,7 @@
 # @option query [function] onError
 # @private
 _getEvent = (query) ->
+	console.log 'eventbrite._getEvent'
 	eventbrite.event_get({id: query.eventId}, (err, data) ->
 		db.set
 			events: [
@@ -91,12 +94,22 @@
 	)

 exports.handle = (query) ->
-	if id = query.eventId = @_getEventId(query.url)
+	console.log 'eventbrite.handle'
+	eventId = _getEventId(query.url)
+	console.log eventId
+	organiserId = _getOrganiserId(query.url)
+
+	if eventId
+		console.log "eventId: #{eventId}"
+		query.eventId = eventId
 		_getEvent query
-		return true
-	else if query.organiserId = @_getOrganiserId(query.url)
+		#return true
+
+	else if organiserId
+		console.log "organiserId: #{eventId}"
+		query.organiserId = organiserId
 		_getOrganiserId query
-		return true
+		#return true

 	return false


Handle failed querySelector

Handle failed querySelector

--- src/turbolinks/snapshot.coffee
+++ src/turbolinks/snapshot.coffee
@@ -27,7 +27,7 @@
     @getSetting("cache-control")

   hasAnchor: (anchor) ->
-    @body.querySelector("##{anchor}")?
+    try @body.querySelector("##{anchor}")?

   isPreviewable: ->
     @getCacheControlValue() isnt "no-preview"


Fixed key_card_key field.

Fixed key_card_key field.

--- app/assets/javascripts/reservation_popups.js.coffee
+++ app/assets/javascripts/reservation_popups.js.coffee
@@ -182,7 +182,7 @@
     )
     $.getJSON("/reservations/#{@element.data("id")}.json", (result) =>
       @popup.find("#reserver_user_onid").val(result.user_onid)
-      @popup.find("#reserver_key_card_key").val(result.key_card?.key? || "")
+      @popup.find("#reserver_key_card_key").val(result.key_card?.key || "")
       @popup.find("#reserver_description").val(result.description)
       @popup.find("#update-cancel-button").html(result.cancel_string)
       @popup.find("#update-cancel-button a").text("Cancel Reservation")


fixed typo in param name

fixed typo in param name

--- app/assets/javascripts/trade/query_params.js.coffee
+++ app/assets/javascripts/trade/query_params.js.coffee
@@ -2,7 +2,7 @@
   selectedQueryParamNames: [
     {
       name: "selectedTaxonConcepts"
-      param: 'appendices[]'
+      param: 'taxon_concepts_ids[]'
     },
     {
       name: "selectedAppendixValues"


expose mimeType from upload API to Javascript API

--- app/assets/javascripts/uploadcare/files/base.coffee
+++ app/assets/javascripts/uploadcare/files/base.coffee
@@ -25,6 +25,7 @@
       @cdnUrlModifiers = null
       @isImage = null
       @imageInfo = null
+      @mimeType = null
       @s3Bucket = null
       @sourceInfo.source ||= @sourceName

@@ -83,6 +84,7 @@
       @fileSize = data.size
       @isImage = data.is_image
       @imageInfo = data.image_info
+      @mimeType = data.mime_type
       @isStored = data.is_stored
       @s3Bucket = data.s3_bucket
       if data.default_effects
@@ -122,6 +124,7 @@
       isStored: @isStored
       isImage: not @s3Bucket and @isImage
       originalImageInfo: @imageInfo
+      mimeType: @mimeType
       originalUrl: if @fileId then urlBase else null
       cdnUrl: if @fileId
           "#{urlBase}#{@cdnUrlModifiers or ''}"


Simplify code to check form changes

Simplify code to check form changes

--- app/assets/javascripts/watch_form_changes.js.coffee
+++ app/assets/javascripts/watch_form_changes.js.coffee
@@ -6,22 +6,21 @@
     if($("[data-watch-form-message]").length)
       return $("[data-watch-form-message]").data("watch-form-message")

+  hasChanged: ->
+    App.WatchFormChanges.forms().is ->
+      $(this).serialize() != $(this).data("watchChanges")
+
   checkChanges: ->
-    changes = false
-    App.WatchFormChanges.forms().each ->
-      form = $(this)
-      if form.serialize() != form.data("watchChanges")
-        changes = true
-    if changes
-      return confirm(App.WatchFormChanges.msg())
+    if App.WatchFormChanges.hasChanged()
+      confirm(App.WatchFormChanges.msg())
     else
-      return true
+      true

   initialize: ->
     if App.WatchFormChanges.forms().length == 0 || App.WatchFormChanges.msg() == undefined
       return

-    $(document).off("page:before-change").on("page:before-change", -> App.WatchFormChanges.checkChanges())
+    $(document).off("page:before-change").on("page:before-change", App.WatchFormChanges.checkChanges)

     App.WatchFormChanges.forms().each ->
       form = $(this)


fit horizontal legends more snugly

fit horizontal legends more snugly

--- bokehjs/src/coffee/models/annotations/legend.coffee
+++ bokehjs/src/coffee/models/annotations/legend.coffee
@@ -32,20 +32,23 @@
     ctx = @plot_view.canvas_view.ctx
     ctx.save()
     @label_props.set_value(ctx)
-    text_widths = _.map(legend_names, (txt) -> ctx.measureText(txt).width)
+    @text_widths = {}
+    for name in legend_names
+      @text_widths[name] = _.max([ctx.measureText(name).width, label_width])
     ctx.restore()

-    @max_label_width = _.max([_.max(text_widths), label_width])
+    max_label_width = _.max(_.values(@text_widths))

     if @mget("orientation") == "vertical"
       @legend_height = (
         legend_names.length * @max_label_height + (1 + legend_names.length) * legend_spacing
       )
-      @legend_width = @max_label_width + glyph_width + 3 * legend_spacing
+      @legend_width = max_label_width + glyph_width + 3 * legend_spacing
     else
-      @legend_width = (
-        legend_names.length * (@max_label_width + glyph_width + legend_spacing) + (1 + legend_names.length) * legend_spacing
-      )
+      @legend_width = 0
+      for name, width of @text_widths
+        @legend_width += (_.max([width, label_width]) + glyph_width + legend_spacing)
+      @legend_width += (1 + legend_names.length) * legend_spacing
       @legend_height = @max_label_height + 2 * legend_spacing

     location = @mget('location')
@@ -115,6 +118,7 @@
     legend_spacing = @mget('legend_spacing')
     N = @mget("legends").length

+    xoffset = 0
     for [legend_name, glyphs], idx in @mget("legends")
       if orientation == "vertical"
         yoffset = idx * @legend_height / N
@@ -123,11 +127,11 @@
         y1 = @box_coords[1] + yoffset + legend_spacing
         y2 = y1 + glyph_height
       else
-        xoffset = idx * @legend_width / N
         x1 = @box_coords[0] + xoffset + legend_spacing
         x2 = x1 + glyph_width
         y1 = @box_coords[1] + legend_spacing
         y2 = y1 + glyph_height
+        xoffset += @text_widths[legend_name] + legend_spacing + glyph_width

       tx = x2 + legend_spacing
       ty = y1 + @max_label_height / 2.0


ドラッグ開始時にゴミ箱のフタが開くように変更

ドラッグ開始時にゴミ箱のフタが開くように変更

--- views/application.coffee
+++ views/application.coffee
@@ -29,15 +29,14 @@
   $("#trashbox").droppable({
     tolerance: 'touch',

-    over: (event, ui) ->
+    activate: (event, ui) ->
       $('#trashbox-img').attr('src', '/img/TrashBox_Opened.png')

-    out: (event, ui) ->
+    deactivate: (event, ui) ->
       $('#trashbox-img').attr('src', '/img/TrashBox_Closed.png')

     drop: (event, ui) ->
       ui.draggable.remove()
-      $('#trashbox-img').attr('src', '/img/TrashBox_Closed.png')
       $('#trashbox-img').effect('bounce', {}, 150)
   })


revert changes to encode_path -- see https://github.com/sagemathinc/cocalc/pull/2401

--- src/smc-util/misc.coffee
+++ src/smc-util/misc.coffee
@@ -1174,8 +1174,8 @@

 # encode a UNIX path, which might have # and % in it.
 exports.encode_path = (path) ->
-    return encodeURIComponent(path)  # also escapes # and ?
-
+    path = encodeURI(path)  # doesn't escape # and ?, since they are special for urls (but not unix paths)
+    return path.replace(/#/g,'%23').replace(/\?/g,'%3F')

 # This adds a method _call_with_lock to obj, which makes it so it's easy to make it so only
 # one method can be called at a time of an object -- all calls until completion


Activity: removed jquery specific implementation

hope it doesn't break sth else /cc @szkl

--- client/Social/Activity/views/inputview.coffee
+++ client/Social/Activity/views/inputview.coffee
@@ -80,7 +80,7 @@

   keyDown: (event) ->
     super event
-    return  if event.isPropagationStopped()
+    # return  if event.isPropagationStopped()
     switch event.which
       when 13 # Enter
         KD.utils.stopDOMEvent event


Hide if slug is koding

Hide if slug is koding

--- client/app/MainApp/sidebar/groupavatar.coffee
+++ client/app/MainApp/sidebar/groupavatar.coffee
@@ -2,7 +2,7 @@

   constructor:(options = {}, data)->

-    options.cssClass = 'group-avatar-drop'
+    options.cssClass = 'group-avatar-drop hidden'
     groupsController = KD.getSingleton 'groupsController'
     super options, groupsController.getCurrentGroupData()

@@ -10,15 +10,15 @@

   render:(slug, group)->
     if group
+
+      @$().css backgroundImage : \
+        "url(#{group.avatar or 'http://lorempixel.com/60/60/?' + @utils.getRandomNumber()})"
+
       @setTooltip
         title : """You are now in #{group.title} group.
-
 Click here to see group's homepage"""
+
 Click here to see group's homepage."""

-      if slug is 'koding'
-        @$().css backgroundImage : "url(images/logos/50.png)"
-      else
-        @$().css backgroundImage : \
-          "url(#{group.avatar or 'http://lorempixel.com/60/60/?' + @utils.getRandomNumber()})"
+      @show() unless slug is 'koding'

   click:->
     super


Improved: prevent useless DOM refresh

Improved: prevent useless DOM refresh

--- client/app/components/tooltips-manager.coffee
+++ client/app/components/tooltips-manager.coffee
@@ -10,6 +10,9 @@

 module.exports = React.createClass
     displayName: 'TooltipManager'
+
+    # The tooltip's content should not change so we prevent any refresh.
+    shouldComponentUpdate: -> return false

     render: ->


Set alignment class on wrapper

Set alignment class on wrapper

--- client/lanes/components/shared/FormGroup.cjsx
+++ client/lanes/components/shared/FormGroup.cjsx
@@ -16,6 +16,7 @@
     render: ->
         className = _.classnames(className, @props.className, "field"
             editing: @props.editing
+            "align-#{@props.align}": @props.align
             display: false == @props.editing
             'has-error': @isFieldValueInvalid()
         )


fix „show all tags“ button

fix „show all tags“ button

--- client/views/pages/dashboard/dashboard.coffee
+++ client/views/pages/dashboard/dashboard.coffee
@@ -176,6 +176,7 @@

 	getAllTagsPath: (tags) ->
 		tags = tags.map (tag) -> tag._id
+		tags = tags.filter (t) -> Roles.userIsInRole Meteor.userId(), Permissions.participant, t._id

 		FlowRouter.path 'shifts', { projectId:@_id, language:TAPi18n.getLanguage() }, showTags: tags.join('_')


activity: invoke `ActivityLikeSummaryView::updateActors` when view is appended

--- client/Social/Activity/views/likesummaryview.coffee
+++ client/Social/Activity/views/likesummaryview.coffee
@@ -71,6 +71,7 @@

     super

+    @updateActors()

   pistachio: ->


Don't create video start button initially

--- client/ide/lib/views/chat/idechatmessagepane.coffee
+++ client/ide/lib/views/chat/idechatmessagepane.coffee
@@ -29,9 +29,6 @@

     isHost = not @isInSession

-    if isVideoFeatureEnabled() and isHost
-      @createStartVideoButton()
-
     @define 'visible', => @getDelegate().visible

     @on 'AddedParticipant', @bound 'participantAdded'


EditorPane: Remove collaborative string instance from drive when file is deleted.

--- client/ide/lib/workspace/panes/ideeditorpane.coffee
+++ client/ide/lib/workspace/panes/ideeditorpane.coffee
@@ -120,6 +120,8 @@
     @file.on 'fs.save.finished', =>
       ace.removeModifiedFromTab()
       @updateFileModifiedTime()
+
+    @file.on 'fs.delete.finished', @bound 'removeCollaborativeString'

     @file.on 'FileContentsNeedsToBeRefreshed', =>
       ace.fetchContents (err, content) =>
@@ -397,11 +399,10 @@
     ace = @getAce()

     ace.ready =>
-
-        @setContent string.getText(), no
-
-        if ace.contentChanged = ace.isCurrentContentChanged()
-          ace.emit 'FileContentChanged'
+      @setContent string.getText(), no
+
+      if ace.contentChanged = ace.isCurrentContentChanged()
+        ace.emit 'FileContentChanged'

   listenCollaborativeStringChanges: ->
@@ -427,6 +428,9 @@
     return  unless changedString is string

     @applyChange change
+
+
+  removeCollaborativeString: -> @rtm.delete 'string', @getFile().path

   isChangedByMe: (change) -> return change.isLocal


EditorPane: Clear content change flag if file is saved.

--- client/ide/lib/workspace/panes/ideeditorpane.coffee
+++ client/ide/lib/workspace/panes/ideeditorpane.coffee
@@ -117,21 +117,21 @@

     @file.on 'FileContentsNeedsToBeRefreshed', =>
       ace.fetchContents (err, content) =>
-        @updateContent content
+        @updateContent content, yes
         ace.removeModifiedFromTab()
         @updateFileModifiedTime()
         @contentChangedWarning?.destroy()
         @contentChangedWarning = null

-  updateContent: (content) ->
+  updateContent: (content, isSaved = no) ->

     scrollTop = @getAceScrollTop()
     cursor    = @getCursor()
     @setContent content, no
     @setCursor cursor
     @setAceScrollTop scrollTop
-    @getAce().lastSavedContents = content
+    @getAce().lastSavedContents = content  if isSaved

   handleAutoSave: ->


Don't allow user to pause plan, if it's expensed vm.

--- client/landing/app/MainApp/paymentcontroller.coffee
+++ client/landing/app/MainApp/paymentcontroller.coffee
@@ -463,7 +463,12 @@
           unless err
             vmController.createGroupVM type, plan.code

-  createDeleteConfirmationModal: (subscription, cb)->
+  createDeleteConfirmationModal: (subscription, type, cb)->
+
+    pauseWarning = ""
+    if type isnt "expensed"
+      pauseWarning = """
You can 'pause' your plan instead, and continue using it
+                        until #{dateFormat subscription.renew }.
"""

     if subscription.status is 'canceled'
       content = """
Removing this VM will destroy all the data in
@@ -476,8 +481,7 @@
                    this VM including all other users in filesystem. Please
                    be careful this process cannot be undone.

-
You can 'pause' your plan instead, and continue using it
-                   until #{dateFormat subscription.renew }.

+                   #{pauseWarning}

What do you want to do?
"""

@@ -507,7 +511,7 @@
             modal.destroy()
             cb yes

-    if subscription.status isnt 'canceled'
+    if subscription.status isnt 'canceled' and type isnt "expensed"
       modal.buttons.Pause.show()

   deleteVM: (vmInfo, callback)->
@@ -516,7 +520,10 @@
     if vmInfo.planOwner.indexOf("user_") > -1
       type = "user"
     else
-      type = "group"
+      if vmInfo.type is "expensed"
+        type = "expensed"
+      else
+        type = "group"

     @getSubscriptionInfo group, type, vmInfo.planCode, (subscription)=>
-      @createDeleteConfirmationModal subscription, callback+      @createDeleteConfirmationModal subscription, type, callback


Check if promise exists before trying to access transaction.

--- community/browser/app/scripts/services/Frame.coffee
+++ community/browser/app/scripts/services/Frame.coffee
@@ -69,7 +69,7 @@
             intrPromise = intrFn(query, $q.defer())
             @terminate = =>
               @isTerminating = yes
-              intrPromise.transaction?.rollback()?.then( =>
+              intrPromise?.transaction?.rollback()?.then( =>
                 @isTerminating = no
               )


added default value for Site_Name back

added default value for Site_Name back

--- packages/rocketchat-lib/settings/server/startup.coffee
+++ packages/rocketchat-lib/settings/server/startup.coffee
@@ -28,7 +28,7 @@
 	RocketChat.settings.add 'Accounts_ManuallyApproveNewUsers', false, { type: 'boolean', group: 'Accounts' }

 	RocketChat.settings.addGroup 'General'
-	RocketChat.settings.add 'Site_Name', '', { type: 'string', group: 'General', public: true }
+	RocketChat.settings.add 'Site_Name', 'Rocket.Chat', { type: 'string', group: 'General', public: true }

 	RocketChat.settings.addGroup 'API'
 	RocketChat.settings.add 'API_Analytics', '', { type: 'string', group: 'API', public: true }


Set small default margin for positionedRegion

--- core/app/backbone/factlink/positioned_region.coffee
+++ core/app/backbone/factlink/positioned_region.coffee
@@ -48,7 +48,7 @@
     bindElDimensions = @_dimensionsOf(@$bindEl)
     bindElPosition = @_bindElPosition()
     offset = @_offsets()
-    margin = @options.margin || 0
+    margin = @options.margin || 2

     switch @options.side
       when 'left'


Server: replace daisy with async.series in create sso token handler

--- servers/lib/server/handlers/api/createssotoken.coffee
+++ servers/lib/server/handlers/api/createssotoken.coffee
@@ -1,12 +1,12 @@
 { argv }  = require 'optimist'
 KONFIG    = require('koding-config-manager').load("main.#{argv.c}")
+async     = require 'async'

-{ daisy }                          = require 'bongo'
-{ checkAuthorizationBearerHeader } = require '../../helpers'
 { sendApiError
   verifyApiToken
   sendApiResponse
   checkApiAvailability }           = require './helpers'
+{ checkAuthorizationBearerHeader } = require '../../helpers'

 apiErrors = require './errors'

@@ -24,30 +24,30 @@

   queue = [

-    ->
+    (next) ->
       verifyApiToken token, (err, apiToken_) ->
         return sendApiError res, err  if err

         apiToken = apiToken_
-        queue.next()
+        next()

-    ->
+    (next) ->
       # checking if user exists
       JAccount.one { 'profile.nickname' : username }, (err, account_) ->
         return sendApiError res, apiErrors.internalError    if err
         return sendApiError res, apiErrors.invalidUsername  unless account_
         account = account_
-        queue.next()
+        next()

-    ->
+    (next) ->
       # checking if user is a member of the group of api token
       client = { connection : { delegate : account } }
       account.checkGroupMembership client, apiToken.group, (err, isMember) ->
         return sendApiError res, apiErrors.internalError    if err
         return sendApiError res, apiErrors.notGroupMember   unless isMember
-        queue.next()
+        next()

-    ->
+    (next) ->
       data    = { username, group : apiToken.group }
       options = { expiresInMinutes : 60 }
       token   = JUser.createJWT data, options
@@ -59,11 +59,12 @@
       port       = if publicPort in ['80', '443'] then '' else ":#{publicPort}"

       loginUrl = "#{protocol}://#{host}#{port}/#{URL}"
-      return sendApiResponse res, { token, loginUrl }
+      sendApiResponse res, { token, loginUrl }
+      next()

   ]

-  daisy queue
+  async.series queue


Animating buffered clicks

Animating buffered clicks

--- client.coffee
+++ client.coffee
@@ -4,11 +4,6 @@

 	Snippets of code in both client.coffee and server.coffee have been adapted from the Happening documentation and existing Happening plugins: Example, Photohunt and BombDefuse

-###
-
-
-###
-done = Obs.create true Obs.observe !-> diff = player.get('score') - localPlayer.peek('score') return if diff is 0 done.set false setInterval (1/diff), !-> localPlayer.incr 'score' if player.get('score') is localPlayer.peek('score') done.set true
 ###

 Comments = require 'comments'
@@ -20,31 +15,31 @@
 App = require 'app'
 Plugin = require 'plugin'

+BUFFER_TIME = 1000
+
 exports.render = ->
-	###
 	# create local copy of the scores
 	localScores = Obs.create {}
+	copied = {}
 	Obs.observe !->
 		Db.shared.iterate 'counters', (v) !->
+			# just copy once!
+			return if copied[v.key()]
+			copied[v.key()] = true
 			localScores.set 'counters', v.key(), v.peek()

-	done = Obs.create true
+	buffer = Obs.create 0
+	# keep app user's local score synced with buffer
 	Obs.observe !->
-		diff = player.get('score') - localPlayer.peek('score')
-		return if diff is 0
-		done.set false
-		setInterval (1/diff), !->
-			localPlayer.incr 'score'
-			if player.get('score') is localPlayer.peek('score') done.set true
-	###
-	buffer = Obs.create 0
+		newVal = Db.shared.get('counters', App.userId()) + buffer.get()
+		localScores.set 'counters', App.userId(), newVal

 	Ui.card !->
 		renderButton buffer

 		renderFunny()

-	renderScores buffer
+	renderScores localScores

 renderButton = (buffer) !->
 	buffering = Obs.create false
@@ -60,7 +55,7 @@

 	Obs.observe !->
 		return unless buffering.get()
-		Obs.onTime 1000, !->
+		Obs.onTime BUFFER_TIME, !->
 			bufferedClicks = buffer.peek()
 			buffer.set(0)

@@ -75,26 +70,49 @@
 		if funny?
 			Dom.div !->
 				Dom.animate
-            			create:
-            				opacity: 1 # target
-            				initial:
-                  				opacity: 0
-            			remove:
-            				opacity: 0 # target
-            				initial:
-                  				opacity: 1
-            			content: !->
+						create:
+							opacity: 1 # target
+							initial:
+								opacity: 0
+						remove:
+							opacity: 0 # target
+							initial:
+								opacity: 1
+						content: !->
 						Dom.style fontSize: '150%', textAlign: 'center', padding: "30px 5px 15px"
 						Dom.text funny

-renderScores = (buffer) !->
+renderScores = (localScores) !->
+	done = Obs.create true
+
+	# make a list of all the player ids
+	playerList = Obs.create {}
+	Obs.observe !->
+		Db.shared.iterate 'counters', (counter) !->
+			userId = + counter.key()
+			return if userId is App.userId()
+			playerList.set userId, true
+
+	# animate scores when receiving someone's buffered clicks
+	playerList.iterate (userId) !->
+		done.get() # subscribe
+
+		realCounter = Db.shared.ref('counters', userId.key())
+		localCounter = localScores.ref('counters', userId.key())
+		diff = realCounter.get() - localCounter.peek()
+		return if diff is 0
+		done.set false
+		stepTime = Math.round(BUFFER_TIME/diff)
+		Obs.interval stepTime, !->
+			localCounter.incr()
+			if realCounter.peek() <= localCounter.peek()
+				done.set true
+
 	Ui.list !->
 		Dom.style margin: '0 15px'
-		Db.shared.iterate 'counters',
-			((counter) !-> renderScore(counter, buffer)),
-			(counter) -> -(counter.get() + if (counter.key() is App.userId()) then buffer.get() else 0)
+		localScores.iterate 'counters', renderScore, (counter) -> -counter.get()

-renderScore = (counter, buffer) !->
+renderScore = (counter) !->
 	Ui.item !->
 		userId = +counter.key()
 		if userId is App.userId()
@@ -106,7 +124,4 @@
 			Dom.style marginLeft: '10px', Flex: 1
 			Dom.text App.userName(userId)
 		Dom.div !->
-			if userId is App.userId()
-				Dom.text counter.get() + buffer.get()
-			else
-				Dom.text counter.get()
+			Dom.text counter.get()


Show beacon info on mobile

Show beacon info on mobile

Doubleclick and rightclick(contextmenu) will toggle the popup on markers

--- client.coffee
+++ client.coffee
@@ -767,6 +767,10 @@
 						.setContent("Beacon owned by team " + Db.shared.peek('colors', beacon.peek('owner'), 'name') + "." +
 							"

lat: " + location.lat + "
long: " + location.lng)
 					marker.bindPopup(popup)
+					markerClick = () ->
+						beaconMarkers[beacon.key()].togglePopup()
+					marker.on('dbclick', markerClick)
+					marker.on('contextmenu', markerClick)
 				marker.addTo(map)
 				beaconMarkers[beacon.key()] = marker
 				#log 'Added marker, marker list: ', beaconMarkers
@@ -895,6 +899,10 @@
 					});
 					marker = L.marker(latLngObj, {icon: locationIcon})
 					marker.bindPopup("This is your current location." + "
Accuracy: " + state.get('accuracy') + 'm')
+					markerClick = () ->
+						marker.togglePopup()
+					marker.on('dbclick', markerClick)
+					marker.on('contextmenu', markerClick)
 					marker.addTo(map)
 					window.beaconCurrentLocation = marker
 					# Info bar (testing purposes)


Minor documentation edits.

Minor documentation edits.

--- src/js/jquery.extension.hlf.core.coffee
+++ src/js/jquery.extension.hlf.core.coffee
@@ -97,8 +97,8 @@
     #   `toString`, and `defaults`. It can optionally have a `noConflict`
     #   procedure.
     #
-    # - An __apiClass__ definition and/or an `apiMixins` collection. It will get
-    #   modified with base API additions. A `mixinFilter` can be provided to
+    # - An __apiClass__ definition and/or an __apiMixins__ collection. It will
+    #   get modified with base API additions. A `mixinFilter` can be provided to
     #   limit the mixins in the collection that get applied during instantiation.
     #   If provided, the `apiMixins` collection must have a `base` mixin, which
     #   will get the `apiAdditions`. Also note that `apiClass` and `apiMixins`
@@ -140,12 +140,12 @@
       # triggered.
       #
       # With the command route, if there is a plugin instance and it can
-      # `handleCommand`, call the method, but invoke `userInfo` if needed
+      # __handleCommand__, call the method, but invoke `userInfo` if needed
       # beforehand. With the normal route, if there is a plugin instance and no
       # arguments are provided we assume the call is to access the instance,
       # not reset it.
       #
-      # Otherwise if the instance exists, it is returned. `asSharedInstance`
+      # Otherwise if the instance exists, it is returned. __asSharedInstance__
       # will decide what the plugin instance's main element will be. The idea is
       # several elements all share the same plugin instance.
       #


Remove unnecessary return

Remove unnecessary return

--- lib/user-utilities.coffee
+++ lib/user-utilities.coffee
@@ -85,7 +85,6 @@
           resolve
             dev: @filterActivePackages(packages.dev)
             user: @filterActivePackages(packages.user)
-      return

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


Small rename

Small rename

--- core/app/backbone/models/fact_relation.coffee
+++ core/app/backbone/models/fact_relation.coffee
@@ -7,7 +7,7 @@
     $.ajax
       url: @url() + "/opinion/" + type
       success: (data) =>
-        mp_track "Evidence: opinionate",
+        mp_track "Evidence: Opinionate",
           type: type
           evidence_id: @id


JMachine: ::addUser helper to re-enable asOwner flag since we need it for user remove flow

--- workers/social/lib/social/models/computeproviders/machine.coffee
+++ workers/social/lib/social/models/computeproviders/machine.coffee
@@ -197,8 +197,6 @@

   addUser = (users, options) ->

-    # asOwner option is disabled for now ~ GG
-    # passing False for owner
     { user, asOwner, permanent } = options

     newUsers = excludeUser { users, user, permanent }
@@ -211,7 +209,7 @@
       newUsers.push {
         id       : user.getId()
         username : user.username
-        owner    : no
+        owner    : asOwner
         approved : no
         permanent
       }