Update for unicode identifiers

Update for unicode identifiers

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -1,3 +1,13 @@
+# Regex used names in general:
+# [\\w&&[^0-9]]\\w{0,31}
+# Regex used for locals:
+# \\w{1,31}
+# Regex used for globals, except for when defining globals
+# [\\w&&[^0-9_]]\\w{0,31}|_\\w{1,31}
+# Regex used when defining globals
+# [\\w&&[^0-9_]]\\w{0,31}
+# Regex used when it could be local or global
+# \\w{1,32}
 'scopeName': 'source.stata'
 'name': 'Stata'
 'fileTypes': [
@@ -64,22 +74,18 @@
     ]
   }
   {
+    'match': '\\b(foreach)\\s+((?!in|of).+)\\s+(in|of var(list|lis|li|l)?|of new(list|lis|li|l)?|of num(list|lis|li|l)?)\\b'
     'captures':
       '1':
         'name': 'keyword.control.flow.stata'
       '2':
         'patterns': [
-          {include: '#macro-reserved_names'}
-          {
-            'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
-            'name': 'entity.name.type.class.stata'
-          }
+          {include: '#macro-local-identifiers'}
           {include: '#macro-local'}
           {include: '#macro-global'}
         ]
       '3':
         'name': 'keyword.control.flow.stata'
-    'match': '\\b(foreach)\\s+((?!in|of).+)\\s+(in|of var(list|lis|li|l)?|of new(list|lis|li|l)?|of num(list|lis|li|l)?)\\b'
   }
   {
     'begin': '\\b(foreach)\\s+((?!in|of).+)\\s+(of loc(al|a)?|of glo(bal|ba|b)?)\\b\\s*'
@@ -88,11 +94,7 @@
         'name': 'keyword.control.flow.stata'
       '2':
         'patterns': [
-          {include: '#macro-reserved_names'}
-          {
-            'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
-            'name': 'entity.name.type.class.stata'
-          }
+          {include: '#macro-local-identifiers'}
           {include: '#macro-local'}
           {include: '#macro-global'}
         ]
@@ -100,11 +102,7 @@
         'name': 'keyword.control.flow.stata'
     'end': '(?=\\s*\\{)'
     'patterns': [
-      {include: '#macro-reserved_names'}
-      {
-        'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
-        'name': 'entity.name.type.class.stata'
-      }
+      {include: '#macro-local-identifiers'}
       {include: '#macro-local'}
       {include: '#macro-global'}
     ]
@@ -126,11 +124,7 @@
           {include: '#macro-global'}
         ]
     'patterns': [
-      {include: '#macro-reserved_names'}
-      {
-        'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
-        'name': 'entity.name.type.class.stata'
-      }
+      {include: '#macro-local-identifiers'}
       {include: '#macro-local'}
       {include: '#macro-global'}
     ]
@@ -158,11 +152,11 @@
   'match': '\\b(qui(etly|etl|et|e)?|n(oisily|oisil|oisi|ois|oi|o)?|cap(ture|tur|tu|t)?)\\b:?'
   }
   {
-    'match': '\\s*(pr(ogram|ogra|ogr|og|o)?)\\s+((di(r)?|drop|l(ist|is|i)?)\\s+)([A-Za-z_][A-Za-z0-9_]{0,31})'
+    'match': '\\s*(pr(ogram|ogra|ogr|og|o)?)\\s+((di(r)?|drop|l(ist|is|i)?)\\s+)([\\w&&[^0-9]]\\w{0,31})'
     'captures':
       '1':
         'name': 'storage.type.function.stata'
-      '2':
+      '3':
         'name': 'storage.type.function.stata'
       '7':
         'name': 'entity.name.function.stata'
@@ -179,7 +173,7 @@
       {include: '#macro-local'}
       {include: '#macro-global'}
       {
-        'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+        'match': '[\\w&&[^0-9]]\\w{0,31}'
         'name': 'entity.name.function.stata'
       }
       {
@@ -189,7 +183,7 @@
     ]
   }
   {
-    'match': '\\b(form(at|a)?)\\s*([A-Za-z_][A-Za-z0-9_]{0,31})*\\s*(%)(\-)?(0)?([0-9]+)(\.)([0-9]+)(e|f|g)(c)?'
+    'match': '\\b(form(at|a)?)\\s*([\\w&&[^0-9]]\\w{0,31})*\\s*(%)(\-)?(0)?([0-9]+)(\.)([0-9]+)(e|f|g)(c)?'
     'captures':
       '1': 'keyword.functions.data.stata.test'
   }
@@ -268,11 +262,7 @@
             'beginCaptures':
               '1':
                 'patterns': [
-                  {include: '#macro-reserved_names'}
-                  {
-                    'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
-                    'name': 'entity.name.type.class.stata'
-                  }
+                  {include: '#macro-local-identifiers'}
                   {include: '#macro-local'}
                   {include: '#macro-global'}
                 ]
@@ -300,11 +290,7 @@
               {include: '#builtin_variables'}
             ]
           }
-          {
-            'comment': 'true/false options'
-            'match': '[A-Za-z_0-9]+'
-            'name': 'entity.name.type.class.stata'
-          }
+          {include: '#macro-local-identifiers'} # true/false options
           {include: '#constants'}
           {include: '#operators'}
           {include: '#string-compound'}
@@ -352,7 +338,7 @@
         'name': 'support.type.stata'
       '5':
         'patterns': [
-          {include: '#reserved_names'}
+          {include: '#reserved-names'}
           {include: '#macro-local'}
         ]
       '7':
@@ -369,7 +355,7 @@
         'name': 'support.type.stata'
   }
   {
-    'match': '\\b(la(bel|be|b)?)\\s+(var(iable|iabl|iab|ia|i)?)\\s+([a-zA-Z_][0-9a-zA-Z_]+)\\s+(`")(.+)("\')'
+    'match': '\\b(la(bel|be|b)?)\\s+(var(iable|iabl|iab|ia|i)?)\\s+([\\w&&[^0-9]]\\w{0,31})\\s+(`")(.+)("\')'
     'captures':
       '1':
         'name': 'keyword.functions.data.stata'
@@ -397,7 +383,7 @@
         'name': 'punctuation.definition.string.begin.stata'
   }
   {
-    'match': '\\b(la(bel|be|b)?)\\s+(var(iable|iabl|iab|ia|i)?)\\s+([a-zA-Z_][0-9a-zA-Z_]+)\\s+(")(.+)(")'
+    'match': '\\b(la(bel|be|b)?)\\s+(var(iable|iabl|iab|ia|i)?)\\s+([\\w&&[^0-9]]\\w{0,31})\\s+(")(.+)(")'
     'captures':
       '1':
         'name': 'keyword.functions.data.stata'
@@ -527,7 +513,7 @@
   'functions':
     'patterns': [
       {
-        'begin': '\\b((abbrev|abs|acos|acosh|asin|asinh|atan|atan2|atanh|autocode|betaden|binomial|binomialp|binomialtail|binormalbofd|byteorder|c|cauchy|cauchyden|cauchytail|Cdhms|ceil|char|chi2|chi2den|chi2tail|Chms|cholesky|chop|clip|clock|Clock|cloglog|Cmdyhms|cofC|Cofc|cofd|Cofd|coleqnumb|collatorlocale|collatorversion|colnfreeparms|colnumb|colsof|comb|cond|corr|cos|cosh|daily|date|day|det|dgammapda|dgammapdada|dgammapdadx|dgammapdx|dgammapdxdx|dhms|diag|diag0cnt|digamma|dofb|dofc|dofC|dofh|dofm|dofq|dofw|dofy|dow|doy|dunnettprob|e|el|epsdouble|epsfloat|exp|exponential|exponentialden|exponentialtail|F|Fden|fileexists|fileread|filereaderror|filewrite|float|floor|fmtwidth|Ftail|gammaden|gammap|gammaptail|get|hadamard|halfyear|halfyearly|hh|hhC|hms|hofd|hours|hypergeometric|hypergeometricp|I|ibeta|ibetatail|igaussian|igaussianden|igaussiantail|indexnot|inlist|inrange|int|inv|invbinomial|invbinomialtail|invcauchy|invcauchytail|invchi2|invchi2tail|invcloglog|invdunnettprob|invexponential|invexponentialtail|invF|invFtail|invgammap|invgammaptail|invibeta|invibetatail|invigaussian|invigaussiantail|invlaplace|invlaplacetail|invlogistic|invlogistictail|invlogit|invnbinomial|invnbinomialtail|invnchi2|invnchi2tail|invnF|invnFtail|invnibeta|invnormal|invnt|invnttail|invpoisson|invpoissontail|invsym|invt|invttail|invtukeyprob|invweibull|invweibullph|invweibullphtail|invweibulltail|irecode|issymmetric|itrim|J|laplace|laplaceden|laplacetail|length|ln|lncauchyden|lnfactorial|lngamma|lnigammaden|lnigaussianden|lniwishartden|lnlaplaceden|lnmvnormalden|lnnormal|lnnormalden|lnwishartden|log|log10|logistic|logisticden|logistictail|logit|lower|ltrim|matmissing|matrix|matuniform|max|maxbyte|maxdouble|maxfloat|maxint|maxlong|mdy|mdyhms|mi|min|minbyte|mindouble|minfloat|minint|minlong|minutes|missing|mm|mmC|mod|mofd|month|monthly|mreldif|msofhours|msofminutes|msofseconds|nbetaden|nbinomial|nbinomialp|nbinomialtail|nchi2|nchi2den|nchi2tail|nF|nFden|nFtail|nibeta|normal|normalden|npnchi2|npnF|npnt|nt|ntden|nttail|nullmat|plural|poisson|poissonp|poissontail|proper|qofd|quarter|quarterly|r|rbeta|rbinomial|rcauchy|rchi2|real|recode|regexs|reldif|replay|return|reverse|rexponential|rgamma|rhypergeometric|rigaussian|rlaplace|rlogistic|rnbinomial|rnormal|round|roweqnumb|rownfreeparms|rownumb|rowsof|rpoisson|rt|rtrim|runiform|runiformint|rweibull|rweibullph|s|scalar|seconds|sign|sin|sinh|smallestdouble|soundex|sqrt|ss|ssC|string|stritrim|strlen|strlower|strltrim|strmatch|strofreal|strpos|strproper|strreverse|strrpos|strrtrim|strtoname|strtrim|strupper|subinstr|subinword|substr|sum|sweep|t|tan|tanh|tc|tC|td|tden|th|tin|tm|tobytes|tq|trace|trigamma|trim|trunc|ttail|tukeyprob|tw|twithin|uchar|udstrlen|udsubstr|uisdigit|uisletter|upper|ustrcompare|ustrcompareex|ustrfix|ustrfrom|ustrinvalidcnt|ustrleft|ustrlen|ustrlower|ustrltrim|ustrnormalize|ustrpos|ustrregexs|ustrreverse|ustrright|ustrrpos|ustrrtrim|ustrsortkey|ustrsortkeyex|ustrtitle|ustrto|ustrtohex|ustrtoname|ustrtrim|ustrunescape|ustrupper|ustrword|ustrwordcount|usubinstr|usubstr|vec|vecdiag|week|weekly|weibull|weibullden|weibullph|weibullphden|weibullphtail|weibulltail|wofd|word|wordbreaklocale|wordcount|year|yearly|yh|ym|yofd|yq|yw)|([A-Za-z_][A-Za-z0-9_]{0,31}))(\\()'
+        'begin': '\\b((abbrev|abs|acos|acosh|asin|asinh|atan|atan2|atanh|autocode|betaden|binomial|binomialp|binomialtail|binormalbofd|byteorder|c|cauchy|cauchyden|cauchytail|Cdhms|ceil|char|chi2|chi2den|chi2tail|Chms|cholesky|chop|clip|clock|Clock|cloglog|Cmdyhms|cofC|Cofc|cofd|Cofd|coleqnumb|collatorlocale|collatorversion|colnfreeparms|colnumb|colsof|comb|cond|corr|cos|cosh|daily|date|day|det|dgammapda|dgammapdada|dgammapdadx|dgammapdx|dgammapdxdx|dhms|diag|diag0cnt|digamma|dofb|dofc|dofC|dofh|dofm|dofq|dofw|dofy|dow|doy|dunnettprob|e|el|epsdouble|epsfloat|exp|exponential|exponentialden|exponentialtail|F|Fden|fileexists|fileread|filereaderror|filewrite|float|floor|fmtwidth|Ftail|gammaden|gammap|gammaptail|get|hadamard|halfyear|halfyearly|hh|hhC|hms|hofd|hours|hypergeometric|hypergeometricp|I|ibeta|ibetatail|igaussian|igaussianden|igaussiantail|indexnot|inlist|inrange|int|inv|invbinomial|invbinomialtail|invcauchy|invcauchytail|invchi2|invchi2tail|invcloglog|invdunnettprob|invexponential|invexponentialtail|invF|invFtail|invgammap|invgammaptail|invibeta|invibetatail|invigaussian|invigaussiantail|invlaplace|invlaplacetail|invlogistic|invlogistictail|invlogit|invnbinomial|invnbinomialtail|invnchi2|invnchi2tail|invnF|invnFtail|invnibeta|invnormal|invnt|invnttail|invpoisson|invpoissontail|invsym|invt|invttail|invtukeyprob|invweibull|invweibullph|invweibullphtail|invweibulltail|irecode|issymmetric|itrim|J|laplace|laplaceden|laplacetail|length|ln|lncauchyden|lnfactorial|lngamma|lnigammaden|lnigaussianden|lniwishartden|lnlaplaceden|lnmvnormalden|lnnormal|lnnormalden|lnwishartden|log|log10|logistic|logisticden|logistictail|logit|lower|ltrim|matmissing|matrix|matuniform|max|maxbyte|maxdouble|maxfloat|maxint|maxlong|mdy|mdyhms|mi|min|minbyte|mindouble|minfloat|minint|minlong|minutes|missing|mm|mmC|mod|mofd|month|monthly|mreldif|msofhours|msofminutes|msofseconds|nbetaden|nbinomial|nbinomialp|nbinomialtail|nchi2|nchi2den|nchi2tail|nF|nFden|nFtail|nibeta|normal|normalden|npnchi2|npnF|npnt|nt|ntden|nttail|nullmat|plural|poisson|poissonp|poissontail|proper|qofd|quarter|quarterly|r|rbeta|rbinomial|rcauchy|rchi2|real|recode|regexs|reldif|replay|return|reverse|rexponential|rgamma|rhypergeometric|rigaussian|rlaplace|rlogistic|rnbinomial|rnormal|round|roweqnumb|rownfreeparms|rownumb|rowsof|rpoisson|rt|rtrim|runiform|runiformint|rweibull|rweibullph|s|scalar|seconds|sign|sin|sinh|smallestdouble|soundex|sqrt|ss|ssC|string|stritrim|strlen|strlower|strltrim|strmatch|strofreal|strpos|strproper|strreverse|strrpos|strrtrim|strtoname|strtrim|strupper|subinstr|subinword|substr|sum|sweep|t|tan|tanh|tc|tC|td|tden|th|tin|tm|tobytes|tq|trace|trigamma|trim|trunc|ttail|tukeyprob|tw|twithin|uchar|udstrlen|udsubstr|uisdigit|uisletter|upper|ustrcompare|ustrcompareex|ustrfix|ustrfrom|ustrinvalidcnt|ustrleft|ustrlen|ustrlower|ustrltrim|ustrnormalize|ustrpos|ustrregexs|ustrreverse|ustrright|ustrrpos|ustrrtrim|ustrsortkey|ustrsortkeyex|ustrtitle|ustrto|ustrtohex|ustrtoname|ustrtrim|ustrunescape|ustrupper|ustrword|ustrwordcount|usubinstr|usubstr|vec|vecdiag|week|weekly|weibull|weibullden|weibullph|weibullphden|weibullphtail|weibulltail|wofd|word|wordbreaklocale|wordcount|year|yearly|yh|ym|yofd|yq|yw)|([\\w&&[^0-9]]\\w{0,31}))(\\()'
         'beginCaptures':
           '2':
             'name': 'support.function.builtin.stata'
@@ -545,7 +531,7 @@
           {include: '#functions'}
           {include: '#subscripts'}
           {
-            'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+            'match': '[\\w&&[^0-9]]\\w{0,31}'
             'name': 'variable.parameter.function.stata'
           }
           {include: '#constants'}
@@ -736,7 +722,7 @@
             'name': 'invalid.illegal.name.stata'
       }
       {
-        'match': '(?<!\\w)(@[A-za-z0-9]+)\\b'
+        'match': '(?<!\\w)(@\\w+)\\b'
         'captures':
           '1':
             'name': 'keyword.docblockr.stata'
@@ -770,16 +756,13 @@
   'macro-commands':
     'patterns': [
       {
-        'begin': '\\b(loc(al|a)?|gl(obal|oba|ob|o)?)\\s+([a-zA-Z0-9_\'`\\$\\(\\)\\{\\}]+)\\s*(?=:|=)'
+        'begin': '\\b(loc(al|a)?)\\s+([\\w\'`\\$\\(\\)\\{\\}]+)\\s*(?=:|=)'
         'beginCaptures':
           '1':
             'name': 'keyword.macro.stata'
-          '4':
-            'patterns': [
-              {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
-                'name': 'entity.name.type.class.stata'
-              }
+          '3':
+            'patterns': [
+              {include: '#macro-local-identifiers'}
               {include: '#macro-local'}
               {include: '#macro-global'}
             ]
@@ -808,15 +791,15 @@
         ]
       }
       {
-        'begin': '\\b(gl(obal|oba|ob|o)?)\\s+(?=[a-zA-Z_`\\$])'
+        'begin': '\\b(gl(obal|oba|ob|o)?)\\s+(?=[\\w`\\$])'
         'beginCaptures':
           '1':
             'name': 'keyword.macro.stata'
         'end': '(\\})|(?=\\"|\\s|\\n|/|,|=)'
         'patterns': [
-          {include: '#macro-reserved_names'}
-          {
-            'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+          {include: '#reserved-names'}
+          {
+            'match': '[\\w&&[^0-9_]]\\w{0,31}'
             'name': 'entity.name.type.class.stata'
           }
           {include: '#macro-local'}
@@ -824,7 +807,7 @@
         ]
       }
       {
-        'begin': '\\b(loc(al|a)?)\\s+(\\+\\+|\\-\\-)?(?=[a-zA-Z_`\\$])'
+        'begin': '\\b(loc(al|a)?)\\s+(\\+\\+|\\-\\-)?(?=[\\w`\\$])'
         'beginCaptures':
           '1':
             'name': 'keyword.macro.stata'
@@ -832,21 +815,13 @@
             'name': 'keyword.operator.arithmetic.stata'
         'end': '(?=\\"|\\s|\\n|/|,|=)'
         'patterns': [
-          {
-            'match': '[^a-zA-Z0-9_\'`\\$\\(\\)]'
-            'name': 'invalid.illegal.name.stata'
-          }
-          {
-            'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
-            'name': 'entity.name.type.class.stata'
-          }
+          {include: '#macro-local-identifiers'}
           {include: '#macro-local'}
           {include: '#macro-global'}
         ]
       }
       {
         'begin': '\\b(tempvar|tempname|tempfile)\\s*(?=\\s)'
-        'comment': 'The \\s*(?=\\s) means that if you have `tempvar\\n`, the next line won\'t be colored as a macro. This is because the newline character is whitespace, so you have to let the \\n be caught by the end pattern.'
         'beginCaptures':
           '1':
             'name': 'keyword.macro.stata'
@@ -857,18 +832,13 @@
             'end': '\\n'
             'name': 'comment.block.stata'
           }
-          {include: '#macro-reserved_names'}
-          {
-            'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
-            'name': 'entity.name.type.class.stata'
-          }
+          {include: '#macro-local-identifiers'}
           {include: '#macro-local'}
           {include: '#macro-global'}
         ]
       }
       {
         'begin': '\\b(ma(cro|cr|c)?)\\s+(drop|l(ist|is|i)?)\\s*(?=\\s)'
-        'comment': 'The \\s*(?=\\s) means that if you have `tempvar\\n`, the next line won\'t be colored as a macro. This is because the newline character is whitespace, so you have to let the \\n be caught by the end pattern.'
         'beginCaptures':
           '0':
             'name': 'keyword.macro.stata'
@@ -888,7 +858,7 @@
           {include: '#macro-local'}
           {include: '#comments'}
           {
-            'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+            'match': '\\w{1,31}'
             'name': 'entity.name.type.class.stata'
           }
         ]
@@ -950,7 +920,7 @@
             'name': 'keyword.macro.extendedfcn.stata'
       }
       {
-        'match': '(word|piece)\\s+([\\s0-9`\'A-Za-z_]+)\\s+(of)'
+        'match': '(word|piece)\\s+([\\s`\'\\w]+)\\s+(of)'
         'captures':
           '1':
             'name': 'keyword.macro.extendedfcn.stata'
@@ -963,7 +933,7 @@
             'name': 'keyword.macro.extendedfcn.stata'
       }
       {
-        'begin': '\\b(subinstr\\s+(loc(al|a)?|gl(obal|oba|ob|o)?))\\s+([A-Za-z_][A-Za-z0-9_]{0,31})'
+        'begin': '\\b(subinstr\\s+(loc(al|a)?|gl(obal|oba|ob|o)?))\\s+(\\w{1,32})'
         'end': '(?=//|\\n)'
         'beginCaptures':
           '1':
@@ -976,7 +946,7 @@
           {include: '#string-compound'}
           {include: '#string-regular'}
           {
-            'match': '(count|coun|cou|co|c)(\\()(local|loca|loc|global|globa|glob|glo|gl)\\s+([A-Za-z_][A-Za-z0-9_]{0,31})(\\))'
+            'match': '(count|coun|cou|co|c)(\\()(local|loca|loc|global|globa|glob|glo|gl)\\s+(\\w{1,32})(\\))'
             'captures':
               '1':
                 'name': 'support.function.builtin.stata'
@@ -1012,7 +982,7 @@
           {include: '#macro-local'}
           {include: '#macro-global'}
           {
-            'match': '[^\\\\`\'\\$\\{\\}]+'
+            'match': '\\w{1,31}'
             'name': 'entity.name.type.class.stata'
           }
         ]
@@ -1075,7 +1045,7 @@
           {include: '#macro-local'}
           {include: '#macro-global'}
           {
-            'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+            'match': '\\w{1,31}'
             'name': 'entity.name.type.class.stata'
           }
         ]
@@ -1118,7 +1088,7 @@
           {include: '#macro-local'}
           {include: '#macro-global'}
           {
-            'match': '[^\\\\`\'\\$\\{\\}\\"\\s\\n/,]+'
+            'match': '[\\w&&[^0-9_]]\\w{0,31}|_\\w{1,31}'
             'name': 'entity.name.type.class.stata'
           }
         ]
@@ -1136,16 +1106,16 @@
           '0':
             'name': 'punctuation.definition.string.end.stata'
         'patterns': [
-          {
-            'include': '#macro-local'
-          }
-          {
-            'include': '#macro-global'
-          }
-          {
-            'match': '[^`\'\\$\\{\\}\\"\\s\\n/,]+'
+          {include: '#macro-local'}
+          {include: '#macro-global'}
+          {
+            'match': '[\\w&&[^0-9_]]\\w{0,31}|_\\w{1,31}'
             'name': 'entity.name.type.class.stata'
           }
+          {
+            'match': '.'
+            'name': 'comment.line.stata'
+          }
         ]
       }
       {
@@ -1153,7 +1123,7 @@
         'beginCaptures':
           '0':
             'name': 'punctuation.definition.string.begin.stata'
-        'end': '(?=\\"|\\s|\\n|/|\\\\|,|\\$)'
+        'end': '(?!\\w)'
         'endCaptures':
           '1':
             'name': 'punctuation.definition.string.end.stata'
@@ -1161,7 +1131,7 @@
           {include: '#macro-local'}
           {include: '#macro-global'}
           {
-            'match': '[^`\'\\$\\{\\}\\"\\s\\n/\\\\,]+'
+            'match': '[\\w&&[^0-9_]]\\w{0,31}|_\\w{1,31}'
             'name': 'entity.name.type.class.stata'
           }
         ]
@@ -1187,7 +1157,7 @@
         'name': 'constant.numeric.integer.decimal.stata'
       }
       {
-        'match': '(?<![a-zA-Z0-9])(\\.(?![\\./]))(?![a-zA-Z0-9])'
+        'match': '(?<![\\w])(\\.(?![\\./]))(?![\\w])'
         'name': 'constant.language.missing.stata'
       }
       {
@@ -1198,11 +1168,11 @@
   'factorvariables':
     'patterns': [
       {
-        'match': '\\b(i|c|o)\\.(?=[a-zA-Z_]|\\([a-zA-Z_])'
+        'match': '\\b(i|c|o)\\.(?=[\\w&&[^0-9]]|\\([\\w&&[^0-9]])'
         'name': 'constant.language.factorvars.stata'
       }
       {
-        'match': '\\b(i?b)((\\d+)|n)\\.(?=[a-zA-Z_]|\\([a-zA-Z_])'
+        'match': '\\b(i?b)((\\d+)|n)\\.(?=[\\w&&[^0-9]]|\\([\\w&&[^0-9]])'
         'captures':
           '0':
             'name': 'constant.language.factorvars.stata'
@@ -1212,7 +1182,7 @@
             ]
       }
       {
-        'match': '\\b(i?b)(\\()(#\\d+|first|last|freq)(\\))\\.(?=[a-zA-Z_]|\\([a-zA-Z_])'
+        'match': '\\b(i?b)(\\()(#\\d+|first|last|freq)(\\))\\.(?=[\\w&&[^0-9]]|\\([\\w&&[^0-9]])'
         'captures':
           '0':
             'name': 'constant.language.factorvars.stata'
@@ -1227,7 +1197,7 @@
             'name': 'keyword.operator.parentheses.stata'
       }
       {
-        'match': '\\b(i?o?)(\\d+)\\.(?=[a-zA-Z_]|\\([a-zA-Z_])'
+        'match': '\\b(i?o?)(\\d+)\\.(?=[\\w&&[^0-9]]|\\([\\w&&[^0-9]])'
         'captures':
           '0':
             'name': 'constant.language.factorvars.stata'
@@ -1237,7 +1207,7 @@
             ]
       }
       {
-        'match': '\\b(i?o?)(\\()(.*?)(\\))(\\.)(?=[a-zA-Z_]|\\([a-zA-Z_])'
+        'match': '\\b(i?o?)(\\()(.*?)(\\))(\\.)(?=[\\w&&[^0-9]]|\\([\\w&&[^0-9]])'
         'captures':
           '1':
             'name': 'constant.language.factorvars.stata'
@@ -1262,12 +1232,12 @@
       }
       {
         'comment': 'match division operator but not path separator'
-        'match': '(?<![a-zA-Z.])/(?![a-zA-Z.]|$)'
+        'match': '(?<![\\w.&&[^0-9]])/(?![\\w.&&[^0-9]]|$)'
         'name': 'keyword.operator.arithmetic.stata'
       }
       {
         'comment': 'match division operator but not path separator'
-        'match': '(?<![a-zA-Z.])\\\\(?![a-zA-Z.]|$)'
+        'match': '(?<![\\w.&&[^0-9]])\\\\(?![\\w.&&[^0-9]]|$)'
         'name': 'keyword.operator.matrix.addrow.stata'
       }
       {
@@ -1380,7 +1350,7 @@
     'patterns': [
       {
         'comment': 'highlight expressions, like [_n], when using subscripts on a variable'
-        'begin': '(?<=[0-9a-zA-Z_\'])(\\[)'
+        'begin': '(?<=[\\w\'])(\\[)'
         'beginCaptures':
           '1':
             'name': 'punctuation.definition.parameters.begin.stata'
@@ -1399,26 +1369,39 @@
         ]
       }
     ]
-  'reserved_names':
-    'patterns': [
-      {
-        'match': '\\b(_all|_b|byte|_coef|_cons|double|float|if|in|int|long|_n|_N|_pi|_pred|_rc|_skip|str[0-9]+|strL|using|with|[0-9][A-Za-z0-9_]+|[A-Za-z_][A-Za-z0-9_]{32,})\\b'
+  'reserved-names':
+    # Not to be used somewhere a local could be
+    'patterns': [
+      {
+        'match': '\\b(_all|_b|byte|_coef|_cons|double|float|if|in|int|long|_n|_N|_pi|_pred|_rc|_skip|str[0-9]+|strL|using|with)\\b'
         'name': 'invalid.illegal.name.stata'
       }
       {
-        'match': '[^a-zA-Z0-9_\'`\\$\\(\\)\\s]'
+        'match': '[^\\w\'`\\$\\(\\)\\s]'
         'name': 'invalid.illegal.name.stata'
       }
-    ]
-  'macro-reserved_names':
-    'patterns': [
-      {
-        'match': '\\b([A-Za-z_][A-Za-z0-9_]{32,})\\b'
+      {
+        'match': '[0-9][\\w]{31,}'
         'name': 'invalid.illegal.name.stata'
       }
       {
-        'match': '[^a-zA-Z0-9_\'`\\$\\(\\)\\s]'
+        'match': '\\w{33,}'
         'name': 'invalid.illegal.name.stata'
+      }
+    ]
+  'macro-local-identifiers':
+    'patterns': [
+      {
+        'match': '[^\\w\'`\\$\\(\\)\\s]'
+        'name': 'invalid.illegal.name.stata'
+      }
+      {
+        'match': '\\w{32,}'
+        'name': 'invalid.illegal.name.stata'
+      }
+      {
+        'match': '\\w{1,31}'
+        'name': 'entity.name.type.class.stata'
       }
     ]
   'ascii-regex-functions':
@@ -1439,7 +1422,7 @@
               {include: '#macro-global'}
               {include: '#functions'}
               {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'match': '[\\w&&[^0-9]]\\w{0,31}'
                 'name': 'variable.parameter.function.stata'
               }
               {include: '#comments-triple-slash'}
@@ -1479,7 +1462,7 @@
               {include: '#macro-global'}
               {include: '#functions'}
               {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'match': '[\\w&&[^0-9]]\\w{0,31}'
                 'name': 'variable.parameter.function.stata'
               }
               {include: '#comments-triple-slash'}
@@ -1517,7 +1500,7 @@
               {include: '#macro-global'}
               {include: '#functions'}
               {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'match': '[\\w&&[^0-9]]\\w{0,31}'
                 'name': 'variable.parameter.function.stata'
               }
               {include: '#comments'}
@@ -1546,7 +1529,7 @@
               {include: '#macro-global'}
               {include: '#functions'}
               {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'match': '[\\w&&[^0-9]]\\w{0,31}'
                 'name': 'variable.parameter.function.stata'
               }
               {include: '#comments-triple-slash'}
@@ -1570,7 +1553,7 @@
               {include: '#macro-global'}
               {include: '#functions'}
               {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'match': '[\\w&&[^0-9]]\\w{0,31}'
                 'name': 'variable.parameter.function.stata'
               }
               {include: '#comments'}
@@ -1597,7 +1580,7 @@
               {include: '#macro-global'}
               {include: '#functions'}
               {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'match': '[\\w&&[^0-9]]\\w{0,31}'
                 'name': 'variable.parameter.function.stata'
               }
               {include: '#comments-triple-slash'}
@@ -1614,8 +1597,9 @@
         'name': 'keyword.control.anchor.stata'
       }
       {
-        'match': '\\$(?![a-zA-Z\\{])'
+        'match': '\\$(?![a-zA-Z_\\{])'
         'name': 'keyword.control.anchor.stata'
+        'comment': 'matched when not a global, but must be ascii'
       }
       {
         'match': '[\\?\\+\\*]'
@@ -1653,8 +1637,7 @@
       {include: '#macro-local'}
       {include: '#macro-global'}
       {
-        # 'comment' 'NOTE: Error if I have \.+ No idea why but it works fine it seems with just \.'
-        # Also extremely weird but if I have the previous 'comment' line uncommented, everything inside the regexm(, "...") goes white.
+        'comment': 'NOTE: Error if I have \.+ No idea why but it works fine it seems with just \.'
         'match': '.'
         'name': 'string.quoted.stata'
       }
@@ -1717,7 +1700,7 @@
               {include: '#macro-global'}
               {include: '#functions'}
               {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'match': '[\\w&&[^0-9]]\\w{0,31}'
                 'name': 'variable.parameter.function.stata'
               }
               {include: '#comments-triple-slash'}
@@ -1765,7 +1748,7 @@
               {include: '#macro-global'}
               {include: '#functions'}
               {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'match': '[\\w&&[^0-9]]\\w{0,31}'
                 'name': 'variable.parameter.function.stata'
               }
               {include: '#comments-triple-slash'}
@@ -1811,7 +1794,7 @@
               {include: '#macro-global'}
               {include: '#functions'}
               {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'match': '[\\w&&[^0-9]]\\w{0,31}'
                 'name': 'variable.parameter.function.stata'
               }
               {include: '#comments'}
@@ -1840,7 +1823,7 @@
               {include: '#macro-global'}
               {include: '#functions'}
               {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'match': '[\\w&&[^0-9]]\\w{0,31}'
                 'name': 'variable.parameter.function.stata'
               }
               {include: '#comments-triple-slash'}
@@ -1865,7 +1848,7 @@
               {include: '#macro-global'}
               {include: '#functions'}
               {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'match': '[\\w&&[^0-9]]\\w{0,31}'
                 'name': 'variable.parameter.function.stata'
               }
               {include: '#comments'}
@@ -1892,7 +1875,7 @@
               {include: '#macro-global'}
               {include: '#functions'}
               {
-                'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+                'match': '[\\w&&[^0-9]]\\w{0,31}'
                 'name': 'variable.parameter.function.stata'
               }
               {include: '#comments-triple-slash'}
@@ -1909,8 +1892,9 @@
         'name': 'keyword.control.anchor.stata'
       }
       {
-        'match': '\\$(?![a-zA-Z\\{])'
+        'match': '\\$(?![[\\w&&[^0-9_]][\\w]{0,31}|_[\\w]{1,31}\\{])'
         'name': 'keyword.control.anchor.stata'
+        'comment': 'matched when not a global'
       }
       {
         'match': '\\\\[1-9][0-9]?'
@@ -1991,8 +1975,7 @@
       {include: '#macro-local'}
       {include: '#macro-global'}
       {
-        # 'comment' 'NOTE: Error if I have \.+ No idea why but it works fine it seems with just \.'
-        # Also extremely weird but if I have the previous 'comment' line uncommented, everything inside the regexm(, "...") goes white.
+        'comment': 'NOTE: Error if I have \.+ No idea why but it works fine it seems with just \.'
         'match': '.'
         'name': 'string.quoted.stata'
       }


z: fix initial thunk diff

z: fix initial thunk diff

--- src/z.coffee
+++ src/z.coffee
@@ -56,7 +56,8 @@
   constructor: ({@renderFn, @props, @child}) -> null
   type: 'Thunk'
   isEqual: (previous) =>
-    not previous.child.__dirtySubject.getValue() and
+    # TODO: Add test for diffing existing DOM to understand why v-nodes come in
+    not previous.child?.__dirtySubject.getValue() and
     previous.child is @child and
     _.isEqual(@props, previous.props)
   render: (previous) =>


Remove unused dependencies

Remove unused dependencies

--- src/scripts/modules/media/media.coffee
+++ src/scripts/modules/media/media.coffee
@@ -1,8 +1,6 @@
 define (require) ->
   Content = require('cs!models/content')
   BaseView = require('cs!helpers/backbone/views/base')
-  HeaderView = require('cs!modules/header/header')
-  FooterView = require('cs!modules/footer/footer')
   MediaEndorsedView = require('cs!./endorsed/endorsed')
   MediaTitleView = require('cs!./title/title')
   MediaTabsView = require('cs!./tabs/tabs')


refactored media.coffee, removed unnecessary code

--- src/scripts/modules/media/media.coffee
+++ src/scripts/modules/media/media.coffee
@@ -73,11 +73,9 @@
       components = linksHelper.getCurrentPathComponents()
       components.version = "@#{components.version}" if components.version
       title = linksHelper.cleanUrl(@model.get('title'))
-      if @model.isBook() and @model.asPage()?
-        title = linksHelper.cleanUrl(@model.get('currentPage').get('title'))
       qs = components.rawquery

-      if title isnt components.title and not @model.isBook() and @model.asPage()?
+      if title isnt components.title and not @model.isBook()
         router.navigate("contents/#{components.uuid}#{components.version}/#{title}#{qs}", {replace: true})

     trackAnalytics: () ->


add source mapping for coffee files

add source mapping for coffee files

--- grunt/coffee.coffee
+++ grunt/coffee.coffee
@@ -6,3 +6,4 @@
     src: ['*.coffee']
     dest: '.tmp/javascripts'
     ext: '.js'
+    options: sourceMap: true


Fix issue with looping while resolving module paths.

--- src/resolver.coffee
+++ src/resolver.coffee
@@ -48,7 +48,7 @@
   resolveModule = (name, cb) ->
     idx = 0
     iterate = ->
-      if idx == modulePaths
+      if idx == modulePaths.length
         throw new Error "Unable to resolve module #{name}"
       path = join modulePaths[idx], name
       exists path, (exist) ->


watdentation

watdentation

I'm not even sure how this happened


Make sure that Models in SharedCollection have an id

--- src/sharedcollection.coffee
+++ src/sharedcollection.coffee
@@ -58,8 +58,6 @@
     if not model.collection
       model.collection = this

-    if not model.id
-      model.set id: SharedCollection.generateGUID()

     @add model, options
     return model
@@ -151,6 +149,11 @@

   _sendModelAdd: (model) ->

+    # Models in shared collection must have unique id. Create one if user did
+    # not give one.
+    if not model.id
+      model.set id: SharedCollection.generateGUID()
+
     # Just ignore readds
     if @_syncDoc.snapshot[this.collectionId][model.id]
       return


fix livereload to be able to livereload again after first full reload

--- src/steroids/connect.coffee
+++ src/steroids/connect.coffee
@@ -156,6 +156,7 @@
           project.package
             onSuccess: =>
               steroidsCli.debug "connect", "doFullReload succ"
+              canBeLiveReload = true
               @prompt.refresh()
               resolve()


allowed localisation of quad-names, e.g. ⎕IO

--- client/editor.coffee
+++ client/editor.coffee
@@ -51,13 +51,10 @@
         return
       'Shift-Esc': -> opts.close?()
       'Ctrl-Up': ->
-        c = cm.getCursor()
-        s = cm.getLine c.line
+        c = cm.getCursor(); s = cm.getLine c.line
         r = '[A-Z_a-zÀ-ÖØ-Ýß-öø-üþ∆⍙Ⓐ-Ⓩ0-9]*' # regex fragment to match identifiers
-        name = ((///⎕?#{r}$///.exec(s[...c.ch])?[0] ? '') +
-                (///^#{r}///.exec(s[c.ch..])?[0] ? ''))
-                  .replace /^\d+/, ''
-        if name and name[0] != '⎕'
+        name = ((///⎕?#{r}$///.exec(s[...c.ch])?[0] or '') + (///^#{r}///.exec(s[c.ch..])?[0] or '')).replace /^\d+/, ''
+        if name
           l = cm.getLine 0; [head, tail...] = l.split ';'
           i = tail.indexOf name; if i < 0 then tail.push name else tail.splice i, 1
           cm.replaceRange [head].concat(tail.sort()).join(';'), {line: 0, ch: 0}, {line: 0, ch: l.length}, 'Dyalog'


updated default prefix mappings

updated default prefix mappings

--- client/keymap.coffee
+++ client/keymap.coffee
@@ -127,9 +127,9 @@
     ☠ ☠ z x c v b n m , . / ☠ ☠       ☠ ☠ Z X C V B N M < > ? ☠ ☠

     ☠ ⋄ ¨ ¯ < ≤ = ≥ > ≠ ∨ ∧ × ÷ ☠ ☠   ☠ ¤ ⌶ ⍫ ⍒ ⍋ ⌽ ⍉ ⊖ ⍟ ⍱ ⍲ ! ⌹ ☠ ☠
-    ☠ ? ⍵ ∊ ⍴ ~ ↑ ↓ ⍳ ○ * ← → ⊢       ☠ ⍰ ⍵ ⍷ ⌾ ⍨ ↑ ↓ ⍸ ⍥ ⍣ ⍞ ⍬ ⊣
-    ☠ ⍺ ⌈ ⌊ _ ∇ ∆ ∘ ' ⎕ ⍎ ⍕ ☠ ☠       ☠ ⍺ ⌈ ⌊ _ ⍢ ∆ ⍤ ⌸ ⌷ ≡ ≢ ☠ ☠
-    ☠ ☠ ⊂ ⊃ ∩ ∪ ⊥ ⊤ | ⍝ ⍀ ⌿ ☠ ☠       ☠ ☠ ⊂ ⊃ ∩ ∪ ⍭ ⍡ ∥ ⍪ ⍙ ⍠ ☠ ☠
+    ☠ ? ⍵ ∊ ⍴ ~ ↑ ↓ ⍳ ○ * ← → ⊢       ☠ ? ⍵ ⍷ ⍴ ⍨ ↑ ↓ ⍸ ⍥ ⍣ ⍞ ⍬ ⊣
+    ☠ ⍺ ⌈ ⌊ _ ∇ ∆ ∘ ' ⎕ ⍎ ⍕ ☠ ☠       ☠ ⍺ ⌈ ⌊ _ ∇ ∆ ⍤ ⌸ ⌷ ≡ ≢ ☠ ☠
+    ☠ ☠ ⊂ ⊃ ∩ ∪ ⊥ ⊤ | ⍝ ⍀ ⌿ ☠ ☠       ☠ ☠ ⊂ ⊃ ∩ ∪ ⊥ ⊤ | ⍪ ⍙ ⍠ ☠ ☠
   '''
   UK: '''
     ☠ ` 1 2 3 4 5 6 7 8 9 0 - = ☠ ☠   ☠ ¬ ! " £ $ % ^ & * ( ) _ + ☠ ☠
@@ -138,9 +138,9 @@
     ☠ \\z x c v b n m , . / ☠ ☠       ☠ | Z X C V B N M < > ? ☠ ☠

     ☠ ⋄ ¨ ¯ < ≤ = ≥ > ≠ ∨ ∧ × ÷ ☠ ☠   ☠ ¤ ⌶ ⍫ ⍒ ⍋ ⌽ ⍉ ⊖ ⍟ ⍱ ⍲ ! ⌹ ☠ ☠
-    ☠ ? ⍵ ∊ ⍴ ~ ↑ ↓ ⍳ ○ * ← → ☠       ☠ ⍰ ⍵ ⍷ ⌾ ⍨ ↑ ↓ ⍸ ⍥ ⍣ ⍞ ⍬ ☠
-    ☠ ⍺ ⌈ ⌊ _ ∇ ∆ ∘ ' ⎕ ⍎ ⍕ ⊢ ☠       ☠ ⍺ ⌈ ⌊ _ ⍢ ∆ ⍤ ⌸ ⌷ ≡ ≢ ⊣ ☠
-    ☠ ⊢ ⊂ ⊃ ∩ ∪ ⊥ ⊤ | ⍝ ⍀ ⌿ ☠ ☠       ☠ ⊣ ⊂ ⊃ ∩ ∪ ⍭ ⍡ ∥ ⍪ ⍙ ⍠ ☠ ☠
+    ☠ ? ⍵ ∊ ⍴ ~ ↑ ↓ ⍳ ○ * ← → ☠       ☠ ? ⍵ ⍷ ⍴ ⍨ ↑ ↓ ⍸ ⍥ ⍣ ⍞ ⍬ ☠
+    ☠ ⍺ ⌈ ⌊ _ ∇ ∆ ∘ ' ⎕ ⍎ ⍕ ⊢ ☠       ☠ ⍺ ⌈ ⌊ _ ∇ ∆ ⍤ ⌸ ⌷ ≡ ≢ ⊣ ☠
+    ☠ ⊢ ⊂ ⊃ ∩ ∪ ⊥ ⊤ | ⍝ ⍀ ⌿ ☠ ☠       ☠ ⊣ ⊂ ⊃ ∩ ∪ ⊥ ⊤ | ⍪ ⍙ ⍠ ☠ ☠
   '''
   DK: '''
     ☠ $ 1 2 3 4 5 6 7 8 9 0 + ´ ☠ ☠   ☠ § ! " # € % & / ( ) = ? ` ☠ ☠
@@ -149,9 +149,9 @@
     ☠ < z x c v b n m , . - ☠ ☠       ☠ > Z X C V B N M ; : _ ☠ ☠

     ☠ ⋄ ¨ ¯ < ≤ = ≥ > ≠ ∨ ∧ × ÷ ☠ ☠   ☠ ¤ ⌶ ⍫ ⍒ ⍋ ⌽ ⍉ ⊖ ⍟ ⍱ ⍲ ! ⌹ ☠ ☠
-    ☠ ? ⍵ ∊ ⍴ ~ ↑ ↓ ⍳ ○ * ← → ☠       ☠ ⍰ ⍵ ⍷ ⌾ ⍨ ↑ ↓ ⍸ ⍥ ⍣ ⍞ ⍬ ☠
-    ☠ ⍺ ⌈ ⌊ _ ∇ ∆ ∘ ' ⎕ ⍎ ⍕ ⊢ ☠       ☠ ⍺ ⌈ ⌊ _ ⍢ ∆ ⍤ ⌸ ⌷ ≡ ≢ ⊣ ☠
-    ☠ ⊢ ⊂ ⊃ ∩ ∪ ⊥ ⊤ | ⍝ ⍀ ⌿ ☠ ☠       ☠ ⊣ ⊂ ⊃ ∩ ∪ ⍭ ⍡ ∥ ⍪ ⍙ ⍠ ☠ ☠
+    ☠ ? ⍵ ∊ ⍴ ~ ↑ ↓ ⍳ ○ * ← → ☠       ☠ ? ⍵ ⍷ ⍴ ⍨ ↑ ↓ ⍸ ⍥ ⍣ ⍞ ⍬ ☠
+    ☠ ⍺ ⌈ ⌊ _ ∇ ∆ ∘ ' ⎕ ⍎ ⍕ ⊢ ☠       ☠ ⍺ ⌈ ⌊ _ ∇ ∆ ⍤ ⌸ ⌷ ≡ ≢ ⊣ ☠
+    ☠ ⊢ ⊂ ⊃ ∩ ∪ ⊥ ⊤ | ⍝ ⍀ ⌿ ☠ ☠       ☠ ⊣ ⊂ ⊃ ∩ ∪ ⊥ ⊤ | ⍪ ⍙ ⍠ ☠ ☠
   '''
 @layouts = layouts = {}
 do ->


Fix incorrectly redirecting to the signin page after signing in.

--- client/router.coffee
+++ client/router.coffee
@@ -53,7 +53,10 @@

 mustBeSignedIn = ->
   if not Meteor.user() and not Meteor.loggingIn()
-    Session.set 'go', Router.current().location.get().path
+    redirect = Router.current().location.get().path
+    if redirect is Router.routes['signin'].path()
+      redirect = Router.routes['room_list'].path()
+    Session.set 'go', redirect
     Router.go 'signin'
   else
     @next()


remove console.log

remove console.log

--- client/tweets.coffee
+++ client/tweets.coffee
@@ -3,7 +3,6 @@

 Template.tweets.feed = ->
   data = Twitter.findOne {}
-  console.log 'test', data
   if data
     $(data.content).find("entry").map ->
       o = {}


better steroidsCli.debug

better steroidsCli.debug

--- src/steroids.coffee
+++ src/steroids.coffee
@@ -49,10 +49,12 @@
   detectSteroidsProject: ->
     return fs.existsSync(paths.application.configDir) and (fs.existsSync(paths.application.appDir) or fs.existsSync(paths.application.wwwDir))

-  debug: (options = {}) =>
+  debug: (options = {}, other) =>
     return unless steroidsCli.options.debug

-    message = if options.constructor.name == "String"
+    message = if other?
+      options + ": " + other
+    else if options.constructor.name == "String"
       options
     else
       options.message


add displayName: TabGuard

add displayName: TabGuard

--- src/tabguard.coffee
+++ src/tabguard.coffee
@@ -21,6 +21,8 @@
 focusableElementsSelector = focusableElementsList.join()

 TabGuard = React.createClass
+  displayName: 'TabGuard'
+
   propTypes:
     className: React.PropTypes.string


allow a bigger scale of schemas

allow a bigger scale of schemas

--- src/template.coffee
+++ src/template.coffee
@@ -16,18 +16,55 @@
         "audio abbr address aside bdi bdo blockquote canvas caption cite code "+
         "datalist details fieldset figcaption figure footer header kbd output "+
         "progress rp rt ruby samp summary time"
+    'strict': -> # xhtml1
+        "#{do schema.html}" # FIXME
+    'xhtml1.1': ->
+        "#{do schema.xhtml}" # FIXME
+    'xhtml': ->
+        "#{do schema.xhtml}" # FIXME
+    'frameset': ->
+        "#{do schema.xhtml}" # FIXME
+    'transitional': ->
+        "#{do schema.xhtml}" # FIXME
+    'mobile': ->
+        "#{do schema.xhtml}" # FIXME
+    'html-ce': ->
+        "#{do schema.xhtml}" # FIXME
     'html-obsolete': ->
         "applet acronym bgsound dir frameset noframes isindex listing nextid " +
         "noembed plaintext rb strike xmp big blink center font marquee nobr "  +
         "multicol spacer tt"

-
 # Valid self-closing HTML 5 elements.
 # set true when all tags are self closing
 self_closing =
     'xml'  : -> on
+    'html' : -> "area br col embed hr img input link meta param"
     'html5': -> "#{do self_closing.html} base command keygen source track wbr"
-    'html' : -> "area br col embed hr img input link meta param"
+    'mobile' : ->  "#{do self_closing.xhtml}"
+    'html-ce': ->  "#{do self_closing.xhtml}"
+    'strict'  : -> "#{do self_closing.xhtml}"
+    'xhtml1.1': -> "#{do self_closing.xhtml}"
+    'xhtml'   : -> "#{do self_closing.xhtml}"
+    'frameset': -> "#{do self_closing.xhtml}"
+    'transitional': -> "#{do self_closing.xhtml}"
+
+# schema aliases
+aliases =
+    'default':'xml'
+    '5':'html5'
+    5:'html5'
+    'ce':'html-ce'
+    '1.1':'xhtml1.1'
+    'html11':'xhtml1.1'
+    'basic':'xhtml'
+    'xhtml1':'xhtml'
+    'xhtml-basic':'xhtml'
+    'xhtml-strict':'strict'
+    'xhtml-mobile':'mobile'
+    'xhtml-frameset':'frameset'
+    'xhtml-trasitional':'transitional'
+

 fill_with_tags = (tag, tags) ->
@@ -41,8 +78,9 @@
 class Template extends EventEmitter
     constructor: (opts = {}, template) ->
         [template, opts] = [opts, {}] if typeof opts is 'function'
-        opts.self_closing = self_closing[opts.schema ? 'xml']?()
-        opts.schema = schema[opts.schema ? 'xml']?()
+        s = aliases[opts.schema] or opts.schema or 'xml'
+        opts.self_closing = self_closing[s]?(opts)
+        opts.schema = schema[s]?(opts)
         opts.end ?= on

         class ExtendedBuilder


:memo:

:memo:

--- src/template.coffee
+++ src/template.coffee
@@ -84,6 +84,11 @@
       return match if match.match(/^`.*`$/)
       @resolveReference(match)

+  # Private: Resolves a single reference.
+  #
+  # * `ref` {String} containing a documentation reference.
+  #
+  # Returns the link {String} to the appropriate documentation.
   resolveReference: (ref) ->
     result = Resolver.getInstance().resolve(ref)
     if typeof result is 'string'
@@ -91,6 +96,11 @@
     else
       Template.render('reference', result)

+  # Private: Strips paragraph tags from the text.
+  #
+  # * `content` {String} to remove the tags from.
+  #
+  # Returns the {String} with the tags removed.
   stripParagraphTags: (content) ->
     content.replace(/<\/?p>/g, '')


respond_error: res.status(500).render('500')

--- node-http-server.coffee
+++ node-http-server.coffee
@@ -2,7 +2,7 @@
   console.log '----- Error, 500-ing -----'
   console.log error
   console.log '--------------------------'
-  respond_plain res, 500, "Internal Error.\n"
+  res.status(500).render('500')

 respond_js = (res, code, js) ->


cleanup

cleanup

--- node/fabricate.coffee
+++ node/fabricate.coffee
@@ -490,7 +490,7 @@
       default_fair_id: "armory-show-2013"
       profile_id: "the-armory-show"
       id: "armory-show-2013"
-      name: "Zona MACO 2014"
+      name: "Armory Show Fair Organizer"

     when 'fair_profile'
       cover_image: null
@@ -504,7 +504,7 @@
       bio: "Awesome Fair"
       default_icon_version: "square"
       owner_type: "FairOrganizer"
-      follows_count:307
+      follows_count: 307
       short_description: null
       published: true


more consistent execution order between render_x and render_y

--- static/coffee/guides.coffee
+++ static/coffee/guides.coffee
@@ -66,20 +66,19 @@
   tick_label : (tick) ->
     return tick.toString()

+
   render_x : ->
     can_ctx = @plot_view.x_can_ctx
     data_range = @mapper.data_range
     interval = ticks.auto_interval(
-      data_range.get('start'), data_range.get('end')
-    )
+      data_range.get('start'), data_range.get('end'))
     range = data_range.get('end') - data_range.get('start')
     x_scale = @mapper.get('scalestate')[0]
-    last_tick_end = 10000
+    last_tick_end = 0
     [first_tick, last_tick] = ticks.auto_bounds(
       data_range.get('start'), data_range.get('end'), interval)
     current_tick = first_tick
     x_ticks = []
-    last_tick_end = 0
     can_ctx.clearRect(0, 0,  @plot_view.viewstate.get('width'),
       @plot_view.viewstate.get('height'))
     while current_tick <= last_tick
@@ -106,11 +105,11 @@
       data_range.get('start'), data_range.get('end'))
     range = data_range.get('end') - data_range.get('start')
     y_scale = @mapper.get('scalestate')[0]
+    last_tick_end = 10000
     [first_tick, last_tick] = ticks.auto_bounds(
       data_range.get('start'), data_range.get('end'), interval)
     current_tick = first_tick
     y_ticks = []
-    last_tick_end = 10000
     can_ctx.clearRect(0, 0, @plot_view.viewstate.get('width'),
       @plot_view.viewstate.get('height'))
     while current_tick <= last_tick
@@ -161,7 +160,6 @@
       start_y = y

-    #width = can_ctx.measureText("blahblah").width
     text_height = 20

     legend_height = text_height * @model.get('legends').length
@@ -185,7 +183,6 @@
       console.log("l.name", l.name, l, legend_offset_x, legend_offset_y)
       can_ctx.strokeStyle = l.color
       can_ctx.fillStyle = l.color
-
       can_ctx.fillText(l.name, legend_offset_x, legend_offset_y)
       legend_offset_y += text_height
     can_ctx.stroke()


Add test case for group select query

Add test case for group select query

--- test/collection-spec.coffee
+++ test/collection-spec.coffee
@@ -162,6 +162,16 @@
     cache.getCol('turer').find({'tags.0': {$ne: 'Sykkeltur'}}).count (err, c) ->
       assert.ifError(err)
       req.get(url + '&tag=!Sykkeltur')
+        .expect(200)
+        .end (err, res) ->
+          assert.ifError(err)
+          assert.equal res.body.total, c
+          done()
+
+  it 'should list items connected with given group', (done) ->
+    cache.getCol('turer').find({grupper: '52c672252dc5138712808e01'}).count (err, c) ->
+      assert.ifError(err)
+      req.get(url + '&gruppe=52c672252dc5138712808e01')
         .expect(200)
         .end (err, res) ->
           assert.ifError(err)


Write a failing test for #33

Write a failing test for #33

--- test/lib/verify-test.coffee
+++ test/lib/verify-test.coffee
@@ -27,6 +27,18 @@

         But was actually called:
           - called with `("the wrong WOAH")`.
+      """
+
+  context 'unsatisfied verify - wrong arg count', ->
+    When -> @testDouble("good", "bad")
+    Then -> shouldThrow (=> @verify(@testDouble("good"))), """
+      Unsatisfied verification on test double.
+
+        Wanted:
+          - called with `("good")`.
+
+        But was actually called:
+          - called with `("good", "bad")`.
       """

   context 'with a named double', ->


Disable tests for now

Disable tests for now

--- test/tests/UtilsTest.coffee
+++ test/tests/UtilsTest.coffee
@@ -198,16 +198,16 @@

 	describe "textSize", ->

-		# Todo: for some reason these don't work in phantomjs
+		# Todo: for some reason these don't work reliable in phantomjs

 		text  = "Hello Koen Bok"
 		style = {font:"20px/1em Menlo"}

-		it "should return the right size", ->
-			Utils.textSize(text, style).should.eql({width:168, height:20})
-
-		it "should return the right size with width constraint", ->
-			Utils.textSize(text, style, {width:100}).should.eql({width:100, height:40})
+		# it "should return the right size", ->
+		# 	Utils.textSize(text, style).should.eql({width:168, height:20})
+
+		# it "should return the right size with width constraint", ->
+		# 	Utils.textSize(text, style, {width:100}).should.eql({width:100, height:40})

 		# it "should return the right size with height constraint", ->
 		# 	Utils.textSize(text, style, {height:100}).should.eql(width:168,height:100)


Revert "Fix multiple ^{username} mentions"

This reverts commit 9179f3baceca9a9b1ffc59526acdcd35bfa291f1.

--- assets/javascripts/helpers/url.js.coffee
+++ assets/javascripts/helpers/url.js.coffee
@@ -72,26 +72,25 @@
         _entities[entity] = true

     if TentStatus.config.tent_host_domain
-      _regex = new RegExp("[\\^]([a-z0-9]{2,}(?:\.#{TentStatus.config.tent_host_domain})?)(?=[\\W]|$)")
-      _offset = 0
-      while m = _regex.exec(text.substr(_offset, text.length))
-        if entity = RegExp.$1
-          _offset += text.substr(_offset, text.length).indexOf(entity)
-          original_text = entity
-          _length = original_text.length
-          _indices = TentStatus.Helpers.substringIndices(text, original_text)
-          if entity.match(new RegExp(TentStatus.config.tent_host_domain))
-            entity = "https://#{entity}"
-          else
-            entity = "https://#{entity}.#{TentStatus.config.tent_host_domain}"
+      m = text.match(
+        new RegExp("[\\^]([a-z0-9]+(?:\.#{TentStatus.config.tent_host_domain})?)(?=[\\W]|\\$)")
+      )
+      if entity = m?[1]
+        original_text = entity
+        _length = original_text.length
+        _indices = TentStatus.Helpers.substringIndices(text, original_text)
+        if entity.match(new RegExp(TentStatus.config.tent_host_domain))
+          entity = "https://#{entity}"
+        else
+          entity = "https://#{entity}.#{TentStatus.config.tent_host_domain}"

-          _mentions.push {
-            entity: entity
-            text: original_text
-            url: entity
-            indices: _indices
-          }
-          _entities[entity] = true
+        _mentions.push {
+          entity: entity
+          text: original_text
+          url: entity
+          indices: _indices
+        }
+        _entities[entity] = true

     _mentions


Remove nonserializable_attribute_names()

--- bokehjs/src/coffee/core/has_props.coffee
+++ bokehjs/src/coffee/core/has_props.coffee
@@ -294,36 +294,24 @@
   # need to add.
   serializable_in_document: () -> true

-  # returns a list of those names which should not be included
-  # in the Document and should not go to the server. Subtypes
-  # should override this. The result will be cached on the class,
-  # so this only gets called one time on one instance.
-  nonserializable_attribute_names: () -> []
-
-  _get_nonserializable_dict: () ->
-    if not @constructor._nonserializable_names_cache?
-      names = {}
-      for name in @nonserializable_attribute_names()
-        names[name] = true
-      for name, {internal} of @props
-        if internal then names[name] = true
-      @constructor._nonserializable_names_cache = names
-    @constructor._nonserializable_names_cache
-
   attribute_is_serializable: (attr) ->
-    (attr not of @_get_nonserializable_dict()) and (attr of @attributes)
+    if attr == "id"
+      return true
+    prop = @props[attr]
+    if not prop?
+      throw new Error("#{@type}.attribute_is_serializable('#{attr}'): #{attr} wasn't declared")
+    return not prop.internal

   # dict of attributes that should be serialized to the server. We
   # sometimes stick things in attributes that aren't part of the
   # Document's models, subtypes that do that have to remove their
   # extra attributes here.
   serializable_attributes: () ->
-    nonserializable = @_get_nonserializable_dict()
     attrs = {}
-    for k, v of @attributes
-      if k not of nonserializable
-        attrs[k] = v
-    attrs
+    for name, value of @attributes
+      if @attribute_is_serializable(name)
+        attrs[name] = value
+    return attrs

   # JSON serialization requires special measures to deal with cycles,
   # which means objects can't be serialized independently but only


* Clone original attributes before parse on syncChangeAttributes analysis

--- app/assets/javascripts/backbone.rails_store.js.coffee
+++ app/assets/javascripts/backbone.rails_store.js.coffee
@@ -970,7 +970,7 @@
   ###
   syncChangedAttributes: (attr) ->
     changed = {}
-    parsedAttr = @_parseAttributeModifiers(attr)
+    parsedAttr = @_parseAttributeModifiers(_.clone(attr))
     _.each parsedAttr, (value, key) =>
       unless (@belongsTo[key] or @hasMany[key] or @hasAndBelongsToMany[key] or @hasOne[key])
         if _.isArray(@syncAttributes[key]) and _.isArray(value)


activity: define `AddedToChannel` and `RemovedFromChannel` event handler methods in `ActivitySidebar` class

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -28,25 +28,26 @@
     @sections     = {}
     @selectedItem = null

-    notificationController.on 'AddedToChannel', (channel) =>
-
-      revived        = revive channel
-      listController = @getListController revived.typeConstant
-
-      listController.addItem revived
-
-
-    notificationController.on 'RemovedFromChannel', (channel) =>
-
-      revived        = revive channel
-      listController = @getListController revived.typeConstant
-      item           = listController.itemsIndexed[revived.id]
-      listController.removeItem item
-
-
     notificationController
+      .on 'AddedToChannel',         @bound 'addToChannel'
+      .on 'RemovedFromChannel',     @bound 'removeFromChannel'
       .on 'ChannelUpdateHappened',  @bound 'notificationHasArrived'
       .on 'NotificationHasArrived', @bound 'notificationHasArrived'
+
+
+  addToChannel: (channel) ->
+
+    channel        = revive channel
+    listController = @getListController channel.typeConstant
+    listController.addItem channel
+
+
+  removeFromChannel: (channel) ->
+
+    channel        = revive channel
+    listController = @getListController channel.typeConstant
+    item           = listController.itemForId channel.getId()
+    listController.removeItem item

   notificationHasArrived: (update) ->


EnvironmentListItem: Fix Managed VMs stack check

--- client/app/lib/environment/environmentlistitem.coffee
+++ client/app/lib/environment/environmentlistitem.coffee
@@ -174,7 +174,7 @@
     @stackStateToggle = new kd.CustomHTMLView
       cssClass : 'stack-state-toggle'

-    if isKoding() or title is 'Managed VMs'
+    if isKoding() or /^Managed\ VMs/.test title
       @infoIcon = new kd.CustomHTMLView
     else
       @createInfoIcon()


Reverting linkUri and text fixed

Reverting linkUri and text fixed

--- style.coffee
+++ style.coffee
@@ -77,6 +77,9 @@
 		@textWatchingNodes = []
 		@visible = true
 		@isTextSet = false
+		@baseText = ''
+		@isLinkUriSet = false
+		@baseLinkUri = ''
 		@classes = null
 		@parentSet = false
 		@lastItemParent = null
@@ -119,6 +122,7 @@
 			if !@parentSet
 				@lastItemParent = @item.parent
 			@item.parent = null
+		itemDocumentNode = @item.document.node
 		@item.document.node = null

 		tmpNode = @node
@@ -130,6 +134,20 @@

 		for child in @children
 			child.revert()
+
+		# shared items
+		if itemDocumentNode._documentStyle is @
+			# revert linkUri
+			if @isLinkUriSet
+				@item.linkUri = @baseLinkUri
+				@isLinkUriSet = false
+				@baseLinkUri = ''
+
+			# revert text
+			if @isTextSet
+				@getTextObject().text = @baseText
+				@isTextSet = false
+				@baseText = ''

 		# revert attr values
 		{setAttrs, attrValues} = @
@@ -158,19 +176,24 @@
 					classes.append name
 		return

+	getTextObject: ->
+		if @item.$? and 'text' of @item.$
+			@item.$
+		else if 'text' of @item
+			@item
+		else if @item.label? and 'text' of @item.label
+			@item.label
+
 	updateText: ->
-		if @item.$? and 'text' of @item.$
-			obj = @item.$
-		else if 'text' of @item
-			obj = @item
-		else if @item.label? and 'text' of @item.label
-			obj = @item.label
-
+		obj = @getTextObject()
 		node = @node
 		if node.children.length is 1 and node.children[0].name is 'a'
 			node = node.children[0]
 			href = node.attrs.get('href')
 			if typeof href is 'string'
+				unless @isLinkUriSet
+					@isLinkUriSet = true
+					@baseLinkUri = @item.linkUri
 				@item.linkUri = href

 		if obj
@@ -178,6 +201,7 @@

 			if text.length > 0 or @isTextSet
 				@isTextSet = true
+				@baseText = obj.text
 				obj.text = text
 		return


Nginx: create dev, sandbox, latest and prod user proxy endpoints

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -289,6 +289,10 @@
       #{createLocations(KONFIG)}

       #{createUserMachineLocation("userproxy")}
+      #{createUserMachineLocation("prodproxy")}
+      #{createUserMachineLocation("sandboxproxy")}
+      #{createUserMachineLocation("latestproxy")}
+      #{createUserMachineLocation("devproxy")}
     # close server
     }


Reset CORS directive

Reset CORS directive

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -135,6 +135,8 @@
                 add_header Access-Control-Allow-Origin $http_origin always;
               }
         """
+      else
+        cors = ''

       locations += fn {name, locationConf: location, auth, cors}


Fix a tiny error

Fix a tiny error

--- app/components/vnc-viewer.coffee
+++ app/components/vnc-viewer.coffee
@@ -20,7 +20,7 @@

   didInsertElement: ->
     canvasEl = @element.getElementsByClassName("canvas")[0]
-    isPlatformIos = ENUMS.PLATFORM.IOS is @get "file.platform"
+    isPlatformIos = ENUMS.PLATFORM.IOS is @get "file.project.platform"
     @set "rfb", new RFB
       'target': canvasEl
       'encrypt': ENV.deviceFarmSsl


fix: minor fix

fix: minor fix

--- app/components/vnc-viewer.coffee
+++ app/components/vnc-viewer.coffee
@@ -52,8 +52,8 @@
     if @get 'file.isReady'
       @send("connect")

-  didUpdate() ->
-    @setupRFB()
+  didUpdate: ->
+    @setupRFB()

   didInsertElement: ->
     @setupRFB()


Further change participant number to reflect unique users

--- app/controllers/home_page.coffee
+++ app/controllers/home_page.coffee
@@ -4,7 +4,7 @@
 FILMING = true

 FROM_BEFORE_BBC =
-  user_count: 4801
+  user_count: -5316 # this is arbitrary, but i promise the resulting number is close
   classification_count: 10027682

 class HomePage extends Page


Update example.pre.coffee

Update example.pre.coffee

--- demo/input/example.pre.coffee
+++ demo/input/example.pre.coffee
@@ -63,7 +63,7 @@
 	return {input, output, first, second}

 macros ->
-	macro require -> arguments[0].split(",").map(token) -> string :: "var ${token} = require(${token})"
+	macro require -> arguments[0].split(",").map(token) -> `var ${token} = require(${token})\n`
 	tag graph, formula = (strings, values) -> strings.join("")
 	tag jsx {*} = (strings, values) -> strings.map(token) -> values[token] || token
 	tag i8n {{*}} = (strings, values, tokens) -> translations[config.language][strings.join("")].replaceAll(tokens, values)


Assign module.paths for node_module loading

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -60,9 +60,15 @@
   # Set the filename.
   root.filename = process.argv[1] =
       if options.filename then fs.realpathSync(options.filename) else '.'
-
+
   # Clear the module cache.
   root.moduleCache = {} if root.moduleCache
+
+  # Assign paths for node_modules loading
+  if process.binding('natives').module
+    {Module} = require 'module'
+    root.paths = Module._nodeModulePaths path.dirname options.filename
+
   # Compile.
   if path.extname(root.filename) isnt '.coffee' or require.extensions
     root._compile compile(code, options), root.filename


Adding top position to loadingInidicator to prevent blurring

--- Framework/Views/Controls/LoadingIndicatorView_Framework.coffee
+++ Framework/Views/Controls/LoadingIndicatorView_Framework.coffee
@@ -7,10 +7,12 @@

     if Ti.Platform.osname == "iphone" || Ti.Platform.osname == "ipad"
       @loadingView = Ti.UI.createView({
+        top: 0
         height: "100%"
         width: "100%"
       })
       @activityIndicator = Ti.UI.createActivityIndicator({
+        top: 200
         message: "Loading..."
         width: Ti.UI.SIZE
         height: Ti.UI.SIZE


Add Bryan to credits

Add Bryan to credits

--- app/scripts/d3-directives.coffee
+++ app/scripts/d3-directives.coffee
@@ -40,8 +40,8 @@
           return @_click

       # parse numerical data from strings
-      # TODO: handle dollar signs, commas, etc.
       parseValue: (val)->
+        val = (val ? "").replace /[$,]/, ""
         parseFloat(val)

       ###
@@ -88,7 +88,10 @@
           .attr("d", @path)
           .attr("data-region", (d)->d.id)
           .attr("class", (d)=>
-            "region " + quantize(@value(d))
+            q = quantize(@value(d))
+            console.log d
+            console.log q
+            "region " + q)
           ).on "mouseover", (d, i)=>@_mouseover(d,i)
           .on "mouseout", (d,i)=>@_mouseout(d,i)
           .on "click", (d,i)=>@_click(d,i)


Add key to rotate

Add key to rotate

--- app/scripts/states/repair.coffee
+++ app/scripts/states/repair.coffee
@@ -29,6 +29,8 @@
     @counter = 15
     @game.text.setText('' + @counter)
     @cleanGarbage()
+
+    @game.input.keyboard.addCallbacks(this, null, null, @rotate)

   cleanGarbage: ()->
     for x in [0..39]
@@ -64,7 +66,6 @@
     @marker.destroy()
     for c in @cantbuilds
       c.destroy()
-

   updateMarker: ()->
     if @marker?
@@ -112,7 +113,11 @@
         @game.cantbuild.play()

     if @game.input.activePointer.rightButton.isDown
-      @marker.rotation += Math.PI / 2
+      @rotate()
+
+  rotate: ()->
+    console.log()
+    @marker.rotation += Math.PI / 2

   onBuild: (args)->
     blockList = args[0]


Issue #670. 'THIS' tokens should trigger an implicit call.

--- rewriter.coffee
+++ rewriter.coffee
@@ -329,7 +329,7 @@
 EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat EXPRESSION_END

 # Tokens that, if followed by an `IMPLICIT_CALL`, indicate a function invocation.
-IMPLICIT_FUNC    = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@']
+IMPLICIT_FUNC    = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS']

 # If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
 IMPLICIT_CALL    = [


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/approachable.coffee
+++ test/approachable.coffee
@@ -77,7 +77,7 @@

   equals obj.I.health, 97, "health cooldown should exist and equal 97"

-  obj.addCooldown 'turbo',
+  obj.cooldown 'turbo',
     target: 5
     approachBy: 3
     value: 100


In order to check if polling transport properly restarts connections, we need to send more data in tests (>128K).

--- tests/html/src/tests.coffee
+++ tests/html/src/tests.coffee
@@ -86,11 +86,15 @@

 factor_echo_large_message = (protocol) ->
+    # Should be larger than 128k - the limit for a single request in
+    # some streaming transports.
     messages = [
         Array(4096).join('x'),
         Array(4096*2).join('x'),
         Array(4096*4).join('x'),
         Array(4096*8).join('x'),
+        Array(Math.pow(2,18)).join('x'),
+        Array(Math.pow(2,18)).join('x'),
     ]
     return echo_factory_factory(protocol, messages)


fixed tests for newer mocha version

fixed tests for newer mocha version

--- test/autodoc_test.coffee
+++ test/autodoc_test.coffee
@@ -117,10 +117,10 @@
         data.description.should.match /^\s*
This is a description.<\/p>\s*$/

       it 'gets all namespaces', ->
-        listNamespaces(data).should.include 'Foo'
-        listNamespaces(data).should.include 'Foo.Bar'
-        listNamespaces(data).should.not.include 'privateFunction'
-        listNamespaces(data).should.include '[private]'
+        listNamespaces(data).should.containEql 'Foo'
+        listNamespaces(data).should.containEql 'Foo.Bar'
+        listNamespaces(data).should.not.containEql 'privateFunction'
+        listNamespaces(data).should.containEql '[private]'

       it 'groups functions by namespace', ->
         listMembersForNamespace(data, 'Foo').should.eql ['getName']
@@ -139,7 +139,7 @@
       data = parseExampleFile('redundant.js')

       it 'can infer namespaces from object expressions', ->
-        listNamespaces(data).should.include 'R.objects'
+        listNamespaces(data).should.containEql 'R.objects'

       it 'also respects the @memberOf tag for explicitly defining namespaces', ->
         listMembersForNamespace(data, 'R.strings').should.eql ['split', 'toUpperCase']
@@ -160,14 +160,14 @@
         parseExamples[12].should.have.property('expected', "NaN")

       it 'includes private methods with examples', ->
-        listPrivateFunctions(data).should.include 'privateWithExample'
+        listPrivateFunctions(data).should.containEql 'privateWithExample'

       it 'includes private constructors with members', ->
-        listPrivateFunctions(data).should.include 'PrivateWithMembers'
+        listPrivateFunctions(data).should.containEql 'PrivateWithMembers'

       it 'includes members of private methods', ->
         listMethodsOfPrivateFunction(data, 'PrivateWithMembers')
-          .should.include 'PrivateWithMembers.prototype.foo'
+          .should.containEql 'PrivateWithMembers.prototype.foo'

       describe 'multiline expressions', ->
         insertExamples = listExamplesForMember(data, 'insert')
@@ -218,7 +218,7 @@
     data = parseFile('autodoc.js')

     it 'can read @typedefs', ->
-      getTypeDef(data, 'TypeInfo').should.include {
+      getTypeDef(data, 'TypeInfo').should.have.properties {
         name: 'TypeInfo',
         identifier: 'type-TypeInfo',
         description: '
A custom type defined by a library.
\n',


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/clamp_bounds.coffee
+++ test/clamp_bounds.coffee
@@ -2,7 +2,7 @@

 test "should clamp the x-axis", ->
   player = GameObject()
-  player.include(Clam)
+  player.include(ClampBounds)

   player.I.x = - 400
   player.update()


Add debugging to exceptions controller

Add debugging to exceptions controller

--- controllers/exceptions.coffee
+++ controllers/exceptions.coffee
@@ -35,12 +35,11 @@
         link = "http://#{req.headers.host}/admin/exceptions/#{ex.id}"
         body = "Oh noes, there was an error in ⌘ + Queue!

-          #{link}
+          #{link}

           #{ex.message}
-

+
           #{ex.trace}
-

           "

         mg.sendText config.mailgunFrom,
@@ -71,12 +70,19 @@
 # ----- RESTful routes -----

 # TODO: Fancy admin panel screen
-#exports.index = (req, res, next) ->
+exports.index = (req, res, next) ->
+  models.Exception.findAll()
+    .then (exceptions) ->
+      res.json exceptions
+

 exports.show = (req, res, next) ->
   id = req.params.id

   models.Exception.findById id
     .then (ex) ->
-      res.render 'error', ex
+      if ex
+        res.render 'error', ex
+      else
+        res.status(404).send 'Exception not found'


these components were pointless

these components were pointless

--- app/builtins.coffee
+++ app/builtins.coffee
@@ -65,7 +65,7 @@
         return fn_help_index ctx, fns

     # TODO shouldn't be pre
-    return PreComponent value: "Documentation for #{cmd} not found."
+    return React.DOM.pre null, "Documentation for #{cmd} not found."

   component_cmd 'help', 'Shows this help', (ctx, cmd) ->
     if arguments.length > 0
@@ -129,23 +129,15 @@
   component_fn 'md', 'Renders Markdown', (ctx, string, opts) ->
     Markdown.MarkdownComponent value: string, opts: opts

-  TextComponent = React.createClass
-    displayName: 'TextComponent'
-    render: -> React.DOM.p {}, @props.value
-
-  PreComponent = React.createClass
-    displayName: 'PreComponent'
-    render: -> React.DOM.pre {}, @props.value
-
   HtmlComponent = React.createClass
     displayName: 'HtmlComponent'
     render: -> React.DOM.div className: 'user-html', dangerouslySetInnerHTML: __html: @props.value

   component_fn 'text', 'Prints text', (ctx, string) ->
-    TextComponent value: string
+    React.DOM.p {}, string

   component_fn 'pre', 'Prints preformatted text', (ctx, string) ->
-    PreComponent value: string
+    React.DOM.pre null, string

   component_fn 'html', 'Adds some HTML', (ctx, string) ->
     HtmlComponent value: string
@@ -174,17 +166,13 @@
       _.extend ctx.current_options, options
     ctx.value ctx.current_options

-  LinkComponent = React.createClass
-    displayName: 'LinkComponent'
-    render: -> React.DOM.a {href: @props.href}, @props.value
-
   component_cmd 'permalink', 'Create a link to the code in the input cell above', (ctx, code) ->
     uri = URI location.href
     uri.hash null
     code ?= ctx.previously_run()
     uri.query '?' + encodeURIComponent btoa code
     uri = uri.toString()
-    LinkComponent href: uri, value: uri
+    React.DOM.a {href: uri}, uri

   PromiseStatusComponent = React.createClass
     displayName: 'PromiseStatusComponent'


Only emit remove events once tags have been removed from the dom

--- modules/tag-input/main/index.coffee
+++ modules/tag-input/main/index.coffee
@@ -182,18 +182,22 @@
   # removes a tag by name
   remove: (name) ->
     if name?
-      returnValue = @tagContainer
+      tagsToRemove = @tagContainer
         .selectAll('.hx-tag')
         .filter((d) -> d.text() is name)
-        .forEach (d) => @emit 'remove', { value: d.text(), type: 'api' }
-        .remove()
-        .length
-    else
-      tags = @tagContainer
+
+      returnValue = tagsToRemove.size()
+      tagsToRemove.remove()
+      tagsToRemove.forEach (d) => @emit 'remove', { value: d.text(), type: 'api' }
+
+    else
+      tagsToRemove = @tagContainer
         .selectAll('.hx-tag')
-        .forEach (d) => @emit 'remove', { value: d.text(), type: 'api' }
-      returnValue = tags.text()
-      tags.remove()
+
+      returnValue = tagsToRemove.text()
+      tagsToRemove.remove()
+      tagsToRemove.forEach (d) => @emit 'remove', { value: d.text(), type: 'api' }
+
     if @options.draggable then @_.dragContainer.setup()
     returnValue


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -33,6 +33,6 @@
 test "should move player back and forth when keys are changed", ->
   ;

-test "should move player acco"
+test "should move player according to their spe"

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -100,6 +100,6 @@
   window.keydown.left = true
   window.keydown.right = true

-
+  p

 module()


[module] ignore numbers in `printf`

[module] ignore numbers in `printf`

--- modules/printf.coffee
+++ modules/printf.coffee
@@ -10,6 +10,8 @@
  * @return {string} String with replaced placeholders
 ###
 module.exports = (input, placeholders...) ->
+  if typeof input == 'number' then return
+
   isObject = typeof input == 'object'
   input = if isObject then JSON.stringify(input) else input
   [placeholder, ...] = placeholders


refactoring

refactoring

--- test/project_test.coffee
+++ test/project_test.coffee
@@ -7,6 +7,71 @@
   Project = null
   Chrome = null

+  prj1 = [
+    {
+      url: "http://redmine.com"
+      urlIndex: 2
+      id: 0
+      text: "prj1_0"
+      show: SHOW.DEFAULT
+    }, {
+      url: "http://redmine.com"
+      urlIndex: 2
+      id: 1
+      text: "prj1_1"
+      show: SHOW.DEFAULT
+    }, {
+      url: "http://redmine.com"
+      urlIndex: 2
+      id: 2
+      text: "prj1_2"
+      show: SHOW.DEFAULT
+    }
+  ]
+
+  prj2 = [
+    {
+      url: "http://redmine.com2"
+      urlIndex: 3
+      id: 0
+      text: "prj2_0"
+      show: SHOW.DEFAULT
+    }, {
+      url: "http://redmine.com2"
+      urlIndex: 3
+      id: 1
+      text: "prj2_1"
+      show: SHOW.DEFAULT
+    }, {
+      url: "http://redmine.com2"
+      urlIndex: 3
+      id: 2
+      text: "prj2_2"
+      show: SHOW.DEFAULT
+    }
+  ]
+
+  prj3 = [
+    {
+      url: "http://redmine.com3"
+      urlIndex: 4
+      id: 0
+      text: "prj3_0"
+      show: SHOW.DEFAULT
+    }, {
+      url: "http://redmine.com3"
+      urlIndex: 4
+      id: 1
+      text: "prj3_1"
+      show: SHOW.DEFAULT
+    }, {
+      url: "http://redmine.com3"
+      urlIndex: 4
+      id: 2
+      text: "prj3_2"
+      show: SHOW.DEFAULT
+    }
+  ]

   beforeEach () ->
     angular.mock.module('timeTracker')
@@ -92,227 +157,127 @@

     it 'add 1 project', () ->
       expect(Project.get()).to.be.empty
-      prj =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 0
-        text: ""
-        show: SHOW.DEFAULT
-      Project.add(prj)
-      added = Project.get()
-      expect(added[prj.url].index).to.be.equal(prj.urlIndex)
-      expect(added[prj.url][prj.id].text).to.be.equal(prj.text)
-      expect(added[prj.url][prj.id].show).to.be.equal(prj.show)
+      Project.add(prj1[0])
+      added = Project.get()
+      expect(added[prj1[0].url].index).to.be.equal(prj1[0].urlIndex)
+      expect(added[prj1[0].url][prj1[0].id].text).to.be.equal(prj1[0].text)
+      expect(added[prj1[0].url][prj1[0].id].show).to.be.equal(prj1[0].show)

     it 'update a project', () ->
       expect(Project.get()).to.be.empty

       # create test data
-      prj1 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 0
-        text: "first"
-        show: SHOW.DEFAULT
-      prj2 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
+      updated =
+        url: "http://redmine.com"
         urlIndex: 2   # same url and urlIndex
         id: 0         # same id
-        text: "second"
+        text: "prj_updated"
         show: SHOW.SHOW

       # execute
-      Project.add(prj1)
-      Project.add(prj2)
-
-      # assert
-      added = Project.get()
-      expect(added[prj1.url].index).to.be.equal(prj1.urlIndex)
-      expect(added[prj1.url][prj1.id].text).to.not.be.equal(prj1.text)
-      expect(added[prj1.url][prj1.id].show).to.not.be.equal(prj1.show)
-      expect(added[prj2.url].index).to.be.equal(prj2.urlIndex)
-      expect(added[prj2.url][prj2.id].text).to.be.equal(prj2.text)
-      expect(added[prj2.url][prj2.id].show).to.be.equal(prj2.show)
+      Project.add(prj1[0])
+      Project.add(updated)
+
+      # assert
+      added = Project.get()
+      expect(added[prj1[0].url].index).to.be.equal(prj1[0].urlIndex)
+      expect(added[prj1[0].url][prj1[0].id].text).to.not.be.equal(prj1[0].text)
+      expect(added[prj1[0].url][prj1[0].id].show).to.not.be.equal(prj1[0].show)
+      expect(added[updated.url].index).to.be.equal(updated.urlIndex)
+      expect(added[updated.url][updated.id].text).to.be.equal(updated.text)
+      expect(added[updated.url][updated.id].show).to.be.equal(updated.show)

     it 'add 2 projects on same redmine server', () ->
       expect(Project.get()).to.be.empty

-      # create test data
-      prj1 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 0
-        text: "first"
-        show: SHOW.DEFAULT
-      prj2 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 1
-        text: "second"
-        show: SHOW.DEFAULT
-
-      # execute
-      Project.add(prj1)
-      Project.add(prj2)
-
-      # assert
-      added = Project.get()
-      expect(added[prj1.url].index).to.be.equal(prj1.urlIndex)
-      expect(added[prj1.url][prj1.id].text).to.be.equal(prj1.text)
-      expect(added[prj1.url][prj1.id].show).to.be.equal(prj1.show)
-      expect(added[prj2.url].index).to.be.equal(prj2.urlIndex)
-      expect(added[prj2.url][prj2.id].text).to.be.equal(prj2.text)
-      expect(added[prj2.url][prj2.id].show).to.be.equal(prj2.show)
+      # execute
+      Project.add(prj1[0])
+      Project.add(prj1[1])
+
+      # assert
+      added = Project.get()
+      expect(added[prj1[0].url].index).to.be.equal(prj1[0].urlIndex)
+      expect(added[prj1[0].url][prj1[0].id].text).to.be.equal(prj1[0].text)
+      expect(added[prj1[0].url][prj1[0].id].show).to.be.equal(prj1[0].show)
+      expect(added[prj1[1].url].index).to.be.equal(prj1[1].urlIndex)
+      expect(added[prj1[1].url][prj1[1].id].text).to.be.equal(prj1[1].text)
+      expect(added[prj1[1].url][prj1[1].id].show).to.be.equal(prj1[1].show)

     it 'add 2 projects on different redmine server', () ->
       expect(Project.get()).to.be.empty

-      # create test data
-      prj1 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 0
-        text: "first"
-        show: SHOW.DEFAULT
-      prj2 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker2"
-        urlIndex: 3 # url is different from prj1.
-        id: 0
-        text: "second"
-        show: SHOW.DEFAULT
-
-      # execute
-      Project.add(prj1)
-      Project.add(prj2)
-
-      # assert
-      added = Project.get()
-      expect(added[prj1.url].index).to.be.equal(prj1.urlIndex)
-      expect(added[prj1.url][prj1.id].text).to.be.equal(prj1.text)
-      expect(added[prj1.url][prj1.id].show).to.be.equal(prj1.show)
-      expect(added[prj2.url].index).to.be.equal(prj2.urlIndex)
-      expect(added[prj2.url][prj2.id].text).to.be.equal(prj2.text)
-      expect(added[prj2.url][prj2.id].show).to.be.equal(prj2.show)
+      # execute
+      Project.add(prj1[0])
+      Project.add(prj2[0])
+
+      # assert
+      added = Project.get()
+      expect(added[prj1[0].url].index).to.be.equal(prj1[0].urlIndex)
+      expect(added[prj1[0].url][prj1[0].id].text).to.be.equal(prj1[0].text)
+      expect(added[prj1[0].url][prj1[0].id].show).to.be.equal(prj1[0].show)
+      expect(added[prj2[0].url].index).to.be.equal(prj2[0].urlIndex)
+      expect(added[prj2[0].url][prj2[0].id].text).to.be.equal(prj2[0].text)
+      expect(added[prj2[0].url][prj2[0].id].show).to.be.equal(prj2[0].show)

     it 'add 3 projects on same redmine server', () ->
       expect(Project.get()).to.be.empty

-      # create test data
-      prj1 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 0
-        text: "first"
-        show: SHOW.DEFAULT
-      prj2 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 1
-        text: "second"
-        show: SHOW.DEFAULT
-      prj3 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 2
-        text: "third"
-        show: SHOW.DEFAULT
-
-      # execute
-      Project.add(prj1)
-      Project.add(prj2)
-      Project.add(prj3)
-
-      # assert
-      added = Project.get()
-      expect(added[prj1.url].index).to.be.equal(prj1.urlIndex)
-      expect(added[prj1.url][prj1.id].text).to.be.equal(prj1.text)
-      expect(added[prj1.url][prj1.id].show).to.be.equal(prj1.show)
-      expect(added[prj2.url].index).to.be.equal(prj2.urlIndex)
-      expect(added[prj2.url][prj2.id].text).to.be.equal(prj2.text)
-      expect(added[prj2.url][prj2.id].show).to.be.equal(prj2.show)
-      expect(added[prj3.url].index).to.be.equal(prj3.urlIndex)
-      expect(added[prj3.url][prj3.id].text).to.be.equal(prj3.text)
-      expect(added[prj3.url][prj3.id].show).to.be.equal(prj3.show)
+      # execute
+      Project.add(prj1[0])
+      Project.add(prj1[1])
+      Project.add(prj1[2])
+
+      # assert
+      added = Project.get()
+      expect(added[prj1[0].url].index).to.be.equal(prj1[0].urlIndex)
+      expect(added[prj1[0].url][prj1[0].id].text).to.be.equal(prj1[0].text)
+      expect(added[prj1[0].url][prj1[0].id].show).to.be.equal(prj1[0].show)
+      expect(added[prj1[1].url].index).to.be.equal(prj1[1].urlIndex)
+      expect(added[prj1[1].url][prj1[1].id].text).to.be.equal(prj1[1].text)
+      expect(added[prj1[1].url][prj1[1].id].show).to.be.equal(prj1[1].show)
+      expect(added[prj1[2].url].index).to.be.equal(prj1[2].urlIndex)
+      expect(added[prj1[2].url][prj1[2].id].text).to.be.equal(prj1[2].text)
+      expect(added[prj1[2].url][prj1[2].id].show).to.be.equal(prj1[2].show)

     it 'add 3 projects on same/different redmine server', () ->
       expect(Project.get()).to.be.empty

-      # create test data
-      prj1 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 0
-        text: "first"
-        show: SHOW.DEFAULT
-      prj2 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker1"
-        urlIndex: 3
-        id: 1
-        text: "second"
-        show: SHOW.DEFAULT
-      prj3 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 1
-        text: "third"
-        show: SHOW.DEFAULT
-
-      # execute
-      Project.add(prj1)
-      Project.add(prj2)
-      Project.add(prj3)
-
-      # assert
-      added = Project.get()
-      expect(added[prj1.url].index).to.be.equal(prj1.urlIndex)
-      expect(added[prj1.url][prj1.id].text).to.be.equal(prj1.text)
-      expect(added[prj1.url][prj1.id].show).to.be.equal(prj1.show)
-      expect(added[prj2.url].index).to.be.equal(prj2.urlIndex)
-      expect(added[prj2.url][prj2.id].text).to.be.equal(prj2.text)
-      expect(added[prj2.url][prj2.id].show).to.be.equal(prj2.show)
-      expect(added[prj3.url].index).to.be.equal(prj3.urlIndex)
-      expect(added[prj3.url][prj3.id].text).to.be.equal(prj3.text)
-      expect(added[prj3.url][prj3.id].show).to.be.equal(prj3.show)
+      # execute
+      Project.add(prj1[0])
+      Project.add(prj2[0])
+      Project.add(prj1[1])
+
+      # assert
+      added = Project.get()
+      expect(added[prj1[0].url].index).to.be.equal(prj1[0].urlIndex)
+      expect(added[prj1[0].url][prj1[0].id].text).to.be.equal(prj1[0].text)
+      expect(added[prj1[0].url][prj1[0].id].show).to.be.equal(prj1[0].show)
+      expect(added[prj2[0].url].index).to.be.equal(prj2[0].urlIndex)
+      expect(added[prj2[0].url][prj2[0].id].text).to.be.equal(prj2[0].text)
+      expect(added[prj2[0].url][prj2[0].id].show).to.be.equal(prj2[0].show)
+      expect(added[prj1[1].url].index).to.be.equal(prj1[1].urlIndex)
+      expect(added[prj1[1].url][prj1[1].id].text).to.be.equal(prj1[1].text)
+      expect(added[prj1[1].url][prj1[1].id].show).to.be.equal(prj1[1].show)

     it 'add 3 projects on different redmine server', () ->
       expect(Project.get()).to.be.empty

-      # create test data
-      prj1 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 0
-        text: "first"
-        show: SHOW.DEFAULT
-      prj2 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker1"
-        urlIndex: 3
-        id: 0
-        text: "second"
-        show: SHOW.DEFAULT
-      prj3 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker2"
-        urlIndex: 4
-        id: 0
-        text: "third"
-        show: SHOW.DEFAULT
-
-      # execute
-      Project.add(prj1)
-      Project.add(prj2)
-      Project.add(prj3)
-
-      # assert
-      added = Project.get()
-      expect(added[prj1.url].index).to.be.equal(prj1.urlIndex)
-      expect(added[prj1.url][prj1.id].text).to.be.equal(prj1.text)
-      expect(added[prj1.url][prj1.id].show).to.be.equal(prj1.show)
-      expect(added[prj2.url].index).to.be.equal(prj2.urlIndex)
-      expect(added[prj2.url][prj2.id].text).to.be.equal(prj2.text)
-      expect(added[prj2.url][prj2.id].show).to.be.equal(prj2.show)
-      expect(added[prj3.url].index).to.be.equal(prj3.urlIndex)
-      expect(added[prj3.url][prj3.id].text).to.be.equal(prj3.text)
-      expect(added[prj3.url][prj3.id].show).to.be.equal(prj3.show)
+      # execute
+      Project.add(prj1[0])
+      Project.add(prj2[0])
+      Project.add(prj3[0])
+
+      # assert
+      added = Project.get()
+      expect(added[prj1[0].url].index).to.be.equal(prj1[0].urlIndex)
+      expect(added[prj1[0].url][prj1[0].id].text).to.be.equal(prj1[0].text)
+      expect(added[prj1[0].url][prj1[0].id].show).to.be.equal(prj1[0].show)
+      expect(added[prj2[0].url].index).to.be.equal(prj2[0].urlIndex)
+      expect(added[prj2[0].url][prj2[0].id].text).to.be.equal(prj2[0].text)
+      expect(added[prj2[0].url][prj2[0].id].show).to.be.equal(prj2[0].show)
+      expect(added[prj3[0].url].index).to.be.equal(prj3[0].urlIndex)
+      expect(added[prj3[0].url][prj3[0].id].text).to.be.equal(prj3[0].text)
+      expect(added[prj3[0].url][prj3[0].id].show).to.be.equal(prj3[0].show)

   ###
@@ -322,18 +287,10 @@

     it 'should be empty, when remove 1 project from 1 project', () ->
       expect(Project.get()).to.be.empty
-
-      # create test data
-      prj1 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 0
-        text: "first"
-        show: SHOW.DEFAULT
-
-      # execute
-      Project.add(prj1)
-      Project.remove(prj1.url, prj1.id)
+
+      # execute
+      Project.add(prj1[0])
+      Project.remove(prj1[0].url, prj1[0].id)

       # assert
       added = Project.get()
@@ -343,78 +300,38 @@
     it 'should leaves 2 projects, when remove 1 project from same url 3 projects', () ->
       expect(Project.get()).to.be.empty

-      # create test data
-      prj1 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 0
-        text: "first"
-        show: SHOW.DEFAULT
-      prj2 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 1
-        text: "second"
-        show: SHOW.DEFAULT
-      prj3 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 2
-        text: "third"
-        show: SHOW.DEFAULT
-
-      # execute
-      Project.add(prj1)
-      Project.add(prj2)  # be removed
-      Project.add(prj3)
-      Project.remove(prj2.url, prj2.id)
-
-      # assert
-      added = Project.get()
-      expect(added[prj1.url].index).to.be.equal(prj1.urlIndex)
-      expect(added[prj1.url][prj1.id].text).to.be.equal(prj1.text)
-      expect(added[prj1.url][prj1.id].show).to.be.equal(prj1.show)
-      expect(added[prj2.url][prj2.id]).to.be.empty  # removed
-      expect(added[prj3.url].index).to.be.equal(prj3.urlIndex)
-      expect(added[prj3.url][prj3.id].text).to.be.equal(prj3.text)
-      expect(added[prj3.url][prj3.id].show).to.be.equal(prj3.show)
+      # execute
+      Project.add(prj1[0])
+      Project.add(prj1[1])  # be removed
+      Project.add(prj1[2])
+      Project.remove(prj1[1].url, prj1[1].id)
+
+      # assert
+      added = Project.get()
+      expect(added[prj1[0].url].index).to.be.equal(prj1[0].urlIndex)
+      expect(added[prj1[0].url][prj1[0].id].text).to.be.equal(prj1[0].text)
+      expect(added[prj1[0].url][prj1[0].id].show).to.be.equal(prj1[0].show)
+      expect(added[prj1[1].url][prj1[1].id]).to.be.empty  # removed
+      expect(added[prj1[2].url].index).to.be.equal(prj1[2].urlIndex)
+      expect(added[prj1[2].url][prj1[2].id].text).to.be.equal(prj1[2].text)
+      expect(added[prj1[2].url][prj1[2].id].show).to.be.equal(prj1[2].show)

     it 'should leaves 2 projects, when remove 1 project from different url 3 projects', () ->
       expect(Project.get()).to.be.empty

-      # create test data
-      prj1 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker"
-        urlIndex: 2
-        id: 0
-        text: "first"
-        show: SHOW.DEFAULT
-      prj2 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker1"
-        urlIndex: 3
-        id: 0
-        text: "second"
-        show: SHOW.DEFAULT
-      prj3 =
-        url: "https://github.com/ujiro99/RedmineTimeTracker2"
-        urlIndex: 3
-        id: 0
-        text: "third"
-        show: SHOW.DEFAULT
-
-      # execute
-      Project.add(prj1)
-      Project.add(prj2)  # be removed
-      Project.add(prj3)
-      Project.remove(prj2.url, prj2.id)
-
-      # assert
-      added = Project.get()
-      expect(added[prj1.url].index).to.be.equal(prj1.urlIndex)
-      expect(added[prj1.url][prj1.id].text).to.be.equal(prj1.text)
-      expect(added[prj1.url][prj1.id].show).to.be.equal(prj1.show)
-      expect(added[prj2.url]).to.be.empty  # removed
-      expect(added[prj3.url].index).to.be.equal(prj3.urlIndex - 1) # update index
-      expect(added[prj3.url][prj3.id].text).to.be.equal(prj3.text)
-      expect(added[prj3.url][prj3.id].show).to.be.equal(prj3.show)
+      # execute
+      Project.add(prj1[0])
+      Project.add(prj2[0])  # be removed
+      Project.add(prj3[0])
+      Project.remove(prj2[0].url, prj2[0].id)
+
+      # assert
+      added = Project.get()
+      expect(added[prj1[0].url].index).to.be.equal(prj1[0].urlIndex)
+      expect(added[prj1[0].url][prj1[0].id].text).to.be.equal(prj1[0].text)
+      expect(added[prj1[0].url][prj1[0].id].show).to.be.equal(prj1[0].show)
+      expect(added[prj2[0].url]).to.be.empty  # removed
+      expect(added[prj3[0].url].index).to.be.equal(prj3[0].urlIndex - 1) # update index
+      expect(added[prj3[0].url][prj3[0].id].text).to.be.equal(prj3[0].text)
+      expect(added[prj3[0].url][prj3[0].id].show).to.be.equal(prj3[0].show)


Links recognizing fixed

Links recognizing fixed

'a' tags with no 'href' attribute now are not recognized as a links.

--- style.coffee
+++ style.coffee
@@ -403,7 +403,7 @@
 		return

 	isLink: ->
-		@node.name is 'a' and @node.attrs.get('href')?[0] isnt '#'
+		@node.name is 'a' and @node.attrs.has('href') and @node.attrs.get('href')?[0] isnt '#'

 	getLinkUri: ->
 		uri = @node.attrs.get('href') + ''


payment: `PaymentMethodView` click handler emits "PaymentMethodChosen" event

--- client/Main/payment/paymentmethodview.coffee
+++ client/Main/payment/paymentmethodview.coffee
@@ -1,24 +1,20 @@
 class PaymentMethodView extends JView
-
-  constructor: (options, data) ->
-    options.cssClass = "payment-method"
-
-    super
+  constructor: (options = {}, data) ->
+    options.cssClass = KD.utils.curry "payment-method", options.cssClass
+    super options, data

     @loader = new KDLoaderView
       size        : { width: 14 }
       showLoader  : !data?

-    @paymentMethodInfo = new KDCustomHTMLView
-      cssClass  : 'billing-link'
-
+    @paymentMethodInfo = new KDCustomHTMLView cssClass  : 'billing-link'
     @paymentMethodInfo.hide()
-
     @setPaymentInfo data

+  click: ->
+    @emit "PaymentMethodChosen", @getData()

   getCardInfoPartial: (paymentMethod) ->
-
     return "Enter billing information"  unless paymentMethod

     { description, cardFirstName, cardLastName
@@ -45,18 +41,14 @@
     <pre>#{cardMonth}/#{cardYear}</pre>
     """

-
   setPaymentInfo: (paymentMethod) ->
-
     @loader.hide()
     @setData paymentMethod  if paymentMethod
     @paymentMethodInfo.updatePartial @getCardInfoPartial paymentMethod?.billing
     @paymentMethodInfo.show()
-

   pistachio: ->
     """
     {{> @loader }}
     {{> @paymentMethodInfo }}
     """
-


Machine: interface for JMachine in client-side, initial commit

--- client/Main/providers/computeprovider.coffee
+++ client/Main/providers/computeprovider.coffee
@@ -34,9 +34,15 @@
       KD.singletons.mainController.emit "renderStacks"

+class Machine extends KDObject

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

+    unless data?.bongo_?.constructorName is 'JMachine'
+      throw new Error 'Data should be a JMachine instance'

+    super options, data
+    { @name, @publicAddress } = @jMachine = @getData()


resurrection.teamwork: prevent pane remove procedure if host is not online

--- client/Teamwork/Views/teamworktabview.coffee
+++ client/Teamwork/Views/teamworktabview.coffee
@@ -204,6 +204,10 @@

   registerPaneRemoveListener_: (pane) ->
     pane.on "KDObjectWillBeDestroyed", =>
+      switch @workspace.hostStatus
+        when "unknown", "offline"
+          return
+
       paneIndexKey = pane.getOptions().indexKey

       @keysRef.once "value", (snapshot) =>


TabToOpen: tweaks

TabToOpen: tweaks

--- pages/vomnibar.coffee
+++ pages/vomnibar.coffee
@@ -161,7 +161,7 @@
         # If the input is empty, then reinstate the keyword (the "w").
         @input.value = @customSearchMode
         @customSearchMode = null
-        @updateCompletions()
+        @update true
       else if inputIsEmpty and @tabToOpen
         @tabToOpen = false
         @update true


removed href link from template

removed href link from template

--- client/landing/app/MainApp/avatararea.coffee
+++ client/landing/app/MainApp/avatararea.coffee
@@ -409,7 +409,7 @@
     """
     {{> @avatar}}

-      {{#(subject) or '(No title)'}}

+      {{#(subject) or '(No title)'}}

       {{@teaser #(body)}}

         <time>{{> @participants}} {{$.timeago #(meta.createdAt)}}</time>


Fixed control bar leaderboard button for #429

--- app/views/play/level_view.coffee
+++ app/views/play/level_view.coffee
@@ -164,7 +164,7 @@
     @initSurface()
     @initGoalManager()
     @initScriptManager()
-    @insertSubviews ladderGame: @otherSession?
+    @insertSubviews ladderGame: (@level.get('type') is "ladder")
     @initVolume()
     @session.on 'change:multiplayer', @onMultiplayerChanged, @
     @originalSessionState = _.cloneDeep(@session.get('state'))


reduceFile progress

reduceFile progress

--- app/assets/javascripts/uploadcare/utils/image-processor.coffee
+++ app/assets/javascripts/uploadcare/utils/image-processor.coffee
@@ -28,17 +28,20 @@
             exif = view.buffer
     op.always ->
       # start = new Date()
+      df.notify(.1)
       img = new Image()
       img.onload = ->
         # console.log('load: ' + (new Date() - start))
+        df.notify(.3)
         op = ns.reduceImage(img, settings)
         op.progress (progress) ->
-          console.log(progress)
+          df.notify(.3 + progress * .5)
         op.fail(df.reject)
         op.done (canvas) ->
           # start = new Date()
           utils.canvasToBlob canvas, 'image/jpeg', settings.quality or 0.95,
             (blob) ->
+              df.notify(.9)
               # console.log('to blob: ' + (new Date() - start))
               if exif
                 op = ns.replaceJpegChunk(blob, 0xe1, [exif])


More meaningful label for time tracking

More meaningful label for time tracking

--- init.coffee
+++ init.coffee
@@ -1,3 +1,4 @@
 # KD Framework Init Script

-KD.registerSingleton "windowController", new KDWindowController+KD.registerSingleton "windowController", new KDWindowController
+console.timeEnd "Framework loaded"


fix unit-test for al-repeat

fix unit-test for al-repeat

--- test/test_drepeat.coffee
+++ test/test_drepeat.coffee
@@ -56,7 +56,7 @@

                 scope.list.splice 0, 0, { text: 'f' }
                 scope.$scan ->
-                    $test.equal result(), results[2]
+                    $test.equal result(), results[2], result()

                     scope.list.splice 2, 0, { text: 'g' }, { text: 'h' }
                     scope.$scan ->


IMPROVED- a more reasonable draw-area for canvas shadows

--- source/Art/Engine/Core/Drawing/DrawAreaCollector.coffee
+++ source/Art/Engine/Core/Drawing/DrawAreaCollector.coffee
@@ -12,6 +12,7 @@
     offsetX ?= 0
     offsetY ?= 0
     blur ?= 0
+    blur /= 2
     expandLeft    = max 0, blur - offsetX - 1
     expandTop     = max 0, blur - offsetY - 1
     expandRight   = max 0, blur + offsetX + 1


Adds limit

Adds limit

--- apps/fair/client/articles.coffee
+++ apps/fair/client/articles.coffee
@@ -12,4 +12,5 @@
         published: true
         fair_id: fair.get('_id')
         sort: '-published_at'
+        limit: 5
     view


Fix setup

Fix setup

--- test/type-checker.coffee
+++ test/type-checker.coffee
@@ -1,4 +1,6 @@
+{initializeGlobalTypes} = require '../lib/types'
 reporter = require '../lib/reporter'
+
 parse = (coffee) ->
   reporter.clean()
   parse coffee
@@ -21,7 +23,10 @@
 suite 'TypeChecker', ->
   setup ->
     global._root_.vars = []
+    global._root_.types = []
     global._root_._this = []
+
+    initializeGlobalTypes(global._root_)
     reporter.clean()

   suite 'Assignment', ->
@@ -662,35 +667,42 @@
       s :: Int = a.text
       """

-    test 'constructor', ->
+    suite 'constructor', ->
+      test 'constructor annotation', ->
+        class A
+          constructor :: Int -> ()
+        a :: A = new A 3
+
+      test 'constructor', ->
+        class A
+          constructor :: Int -> ()
+          constructor: (n) ->
+        a :: A = new A 3
+
+      test 'constructor', ->
+        shouldBeTypeError """
+        class A
+          constructor :: Int -> ()
+        new A ''
+        """
+
+    test 'new', ->
+      shouldBeError """
+      struct S
+        foo :: String
       class A
-        constructor :: Int -> ()
-      a :: A = new A 3
-
-    # test 'constructor', ->
-    #   shouldBeTypeError """
-    #   class A
-    #     constructor :: Int -> ()
-    #   a :: A = new A ''
-    #   """
-
-    # test 'new', ->
-    #   shouldBeTypeError """
-    #   struct S
-    #     foo: String
-    #   class A
-    #     bar: String
-    #   a :: S = new A
-    #   """
-
-    # test 'new', ->
-    #   shouldBeTypeError """
-    #   struct S {
-    #     foo: String
-    #   }
-    #   class A
-    #   a :: S = new A
-    #   """
+        bar: String
+
+      a :: S = new A
+      """
+
+    test 'new', ->
+      shouldBeTypeError """
+      struct S
+        foo :: String
+      class A
+      a :: S = new A
+      """

     # test 'new', ->
     #   class X
@@ -706,24 +718,23 @@
     #       @num = num
     #   x :: X = new X 3, ""

-    # test 'throw access proto this in class', ->
-    #   shouldBeTypeError """
-    #   class X
-    #     constructor :: Number -> ()
-    #     constructor: (num, fuga) ->
-    #       @num = num
-    #   x :: X = new X ""
-    #   """
-
-    # test 'new', ->
-    #   shouldBeTypeError """
-    #   class X
-    #     f: (n :: Number) :: Number ->
-    #       n * n
-    #   x :: X = new X
-    #   n :: String = x.f 3
-    #   """
-
+    test 'throw access proto this in class', ->
+      shouldBeTypeError """
+      class X
+        constructor :: Number -> ()
+        constructor: (num, fuga) ->
+          @num = num
+      x :: X = new X ""
+      """
+
+    test 'new', ->
+      shouldBeTypeError """
+      class X
+        f :: Number -> Number
+        f: (n) -> n * n
+      x :: X = new X
+      n :: String = x.f 3
+      """

   suite 'Generics', ->
     # test 'generics', ->


Fix unit test.

Fix unit test.

--- test/unit/loading.coffee
+++ test/unit/loading.coffee
@@ -64,3 +64,4 @@
         $httpBackend.flush()

         catalog.loadRemote('/strings/nl.json')
+        $httpBackend.verifyNoOutstandingRequest()


fix emoji fallback cdn

fix emoji fallback cdn

--- vue/src/shared/helpers/emojis.coffee
+++ vue/src/shared/helpers/emojis.coffee
@@ -47,23 +47,12 @@

 export srcForEmoji = (char, set) ->
   char = char.match(rx)[0]
-  switch set
-    when 'openmoji-svg'
-      prefix = 'https://raw.githubusercontent.com/hfg-gmuend/openmoji/master/color/svg/'
-      suffix = '.svg'
-      prefix + emojiUnicode(char).toUpperCase().replace(' ', '-') + suffix
-    when 'openmoji-png'
-      prefix = 'https://raw.githubusercontent.com/hfg-gmuend/openmoji/master/color/72x72/'
-      suffix = '.png'
-      prefix + emojiUnicode(char).toUpperCase().replace(' ', '-') + suffix
-    when 'twemoji-svg'
-      prefix = 'https://twemoji.maxcdn.com/v/latest/svg/'
-      suffix = '.svg'
-      prefix + emojiUnicode(char).toLowerCase().replace(' ', '-') + suffix
-    else
-      prefix = 'https://twemoji.maxcdn.com/v/latest/72x72/'
-      suffix = '.png'
-      prefix + emojiUnicode(char).toLowerCase().replace(' ', '-') + suffix
+  # prefix = 'https://raw.githubusercontent.com/hfg-gmuend/openmoji/master/color/svg/'
+  # suffix = '.svg'
+  # prefix + emojiUnicode(char).toUpperCase().replace(' ', '-') + suffix
+  prefix = '/img/emojis/'
+  suffix = '.png'
+  prefix + emojiUnicode(char).toLowerCase().replace(' ', '-') + suffix

 export imgForEmoji = (char) ->
   '<img class="emoji" src="' + srcForEmoji(char) + '">'