Expressions up to unary

Expressions up to unary

--- grammars/d.cson
+++ grammars/d.cson
@@ -242,45 +242,13 @@
       ]
     },

-    #
-    # Storage Classes
-    #
-
-    #
-    # Traits
-    #
-    'traits-expression': {
-      'patterns': [
-        'begin': '__traits\\s*\\(',
-        'beginCaptures': { '0': { 'name': 'keyword.other.traits.begin.d' } },
-        'end': '\\)',
-        'endCaptures': { '0': { 'name': 'keyword.other.traits.end.d' } },
-        'patterns': [
-          { 'include': '#traits-keyword' },
-          { 'include': '#traits-argument' }
-        ]
-      ]
-    },
-    'traits-keyword': {
-      'patterns': [
-        'match': 'isAbstractClass|isArithmetic|isAssociativeArray|isFinalClass|isPOD|isNested|isFloating|isIntegral|isScalar|isStaticArray|isUnsigned|isVirtualFunction|isVirtualMethod|isAbstractFunction|isFinalFunction|isStaticFunction|isOverrideFunction|isRef|isOut|isLazy|hasMember|identifier|getAliasThis|getAttributes|getMember|getOverloads|getProtection|getVirtualFunctions|getVirtualMethods|getUnitTests|parent|classInstanceSize|getVirtualIndex|allMembers|derivedMembers|isSame|compiles',
-        'name': 'keyword.other.traits.d'
-      ]
-    },
-    'traits-argument': {
-      'patterns': [
-        { 'include': '#expression' }
-      ]
-    },
-    #
-    # Special Keywords
-    #
-    'special-keyword': {
-      'patterns': [
-        'match': '__FILE__|__MODULE__|__LINE__|__FUNCTION__|__PRETTY_FUNCTION__',
-        'name': 'keyword.other.special.d'
-      ]
-    }
+
+
+
+
+
+
+

@@ -400,7 +368,536 @@
     ###########################################################################
     # Expression
     ###########################################################################
-
+    'expression': {
+      'patterns': [
+
+      ]
+    },
+    'comma-expression': {
+      'patterns': [
+
+      ]
+    },
+    'assign-expression': {
+      'patterns': [
+
+      ]
+    },
+    'conditional-expression': {
+      'patterns': [
+
+      ]
+    },
+    'or-or-expression': {
+      'patterns': [
+
+      ]
+    },
+    'and-and-expression': {
+      'patterns': [
+
+      ]
+    },
+    'or-expression': {
+      'patterns': [
+
+      ]
+    },
+    'xor-expression': {
+      'patterns': [
+
+      ]
+    },
+    'and-expression': {
+      'patterns': [
+
+      ]
+    },
+    'cmp-expression': {
+      'patterns': [
+
+      ]
+    },
+    'equal-expression': {
+      'patterns': [
+
+      ]
+    },
+    'identity-expression': {
+      'patterns': [
+
+      ]
+    },
+    'rel-expression': {
+      'patterns': [
+
+      ]
+    },
+    'in-expression': {
+      'patterns': [
+
+      ]
+    },
+    'shift-expression': {
+      'patterns': [
+
+      ]
+    },
+    'add-expression': {
+      'patterns': [
+
+      ]
+    },
+    'cat-expression': {
+      'patterns': [
+
+      ]
+    },
+    'mul-expression': {
+      'patterns': [
+
+      ]
+    },
+    'unary-expression': {
+      'patterns': [
+        {
+          'match': '&|(\\+\\+)|(--)|\\*|-|\\+|!',
+          'name': 'keyword.operator.unary.d'
+        },
+        { 'include': '#complement-expression' },
+        {
+          'begin': '\\(',
+          'beginCaptures': 'support.type.parens-expression.begin.d',
+          'end': '\\)',
+          'endCaptures': 'support.type.parens-expression.end.d',
+          'patterns': [
+              { 'include': '#type' }
+          ]
+        },
+        { 'include': '#identifier' },
+        { 'include': '#template-instance' },
+        { 'include': '#delete-expression' },
+        { 'include': '#cast-expression' },
+        { 'include': '#pow-expression' }
+      ]
+    },
+    'complement-expression': {
+      'patterns': [
+        {
+          'match': '~',
+          'name': 'keyword.operator.complement.d'
+        },
+        { 'include': '#unary-expression' }
+      ]
+    },
+    'new-expression': {
+      'patterns': [
+        {
+          'match': 'new\\s+',
+          'name': 'keyword.other.new.d'
+        },
+        { 'include': '#allocator-arguments' },
+        { 'include': '#type' },
+        { 'include': '#new-expression-with-args' }
+      ]
+    },
+    'new-expression-with-args': {
+      'patterns': [
+        {
+          'match': 'new\\s+',
+          'name': 'keyword.other.new.d'
+        }
+        { 'include': '#allocator-arguments' },
+        { 'include': '#type' },
+        {
+          'begin': '\\[',
+          'beginCaptures': 'support.type.expression.bracket.begin.d',
+          'end': '\\]',
+          'endCaptures': 'support.type.expression.bracket.end.d',
+          'patterns': [
+            { 'include': '#assign-expression' },
+          ]
+        }
+        {
+          'begin': '\\(',
+          'beginCaptures': 'support.type.parens-expression.begin.d',
+          'end': '\\)',
+          'endCaptures': 'support.type.parens-expression.end.d',
+          'patterns': [
+              { 'include': '#argument-list' }
+          ]
+        },
+        { 'include': '#new-anon-class-expression' }
+      ]
+    },
+    'allocator-arguments': {
+      'patterns': [
+        'begin': '\\(',
+        'beginCaptures': 'support.type.parens-expression.begin.d',
+        'end': '\\)',
+        'endCaptures': 'support.type.parens-expression.end.d',
+        'patterns': [
+            { 'include': '#argument-list' }
+        ]
+      ]
+    },
+    'argument-list': {
+      'patterns': [
+        { 'include': '#assign-expression' },
+        { 'include': '#comma' }
+      ]
+    },
+    'new-anon-class-expression': {
+      'patterns': [
+        {
+          'match': 'new\\s+',
+          'name': 'keyword.other.new.d'
+        },
+        { 'include': '#allocator-arguments' },
+        {
+          'match': 'class\\s+',
+          'name': 'entity.name.class.d'
+        },
+        { 'include': '#class-arguments' },
+        { 'include': '#super-class' },
+        { 'include': '#interfaces' },
+        { 'include': '#aggregate-body' }
+      ]
+    },
+    'class-arguments': {
+      'patterns': [
+        'begin': '\\(',
+        'beginCaptures': 'support.type.parens-expression.begin.d',
+        'end': '\\)',
+        'endCaptures': 'support.type.parens-expression.end.d',
+        'patterns': [
+            { 'include': '#argument-list' }
+        ]
+      ]
+    },
+    'delete-expression': {
+      'patterns': [
+        {
+          'match': 'delete\\s+',
+          'name': 'keyword.other.delete.d'
+        },
+        { 'include': '#unary-expression' }
+      ]
+    },
+    'cast-expression': {
+      'patterns': [
+        {
+          'begin': 'cast\\s*\\(',
+          'beginCaptures': 'support.type.cast.begin.d',
+          'end': '\\)',
+          'endCaptures': 'support.type.cast.end.d',
+          'patterns': [
+              { 'include': '#type' },
+              { 'include': '#type-ctors' }
+          ]
+        },
+        { 'include': '#unary-expression' }
+      ]
+    },
+    'pow-expression': {
+      'patterns': [
+        { 'include': '#postfix-expression' },
+        {
+          'match': '\\^\\^',
+          'name': 'keyword.operator.pow.d'
+        },
+        { 'include': '#unary-expression' }
+      ]
+    },
+    'postfix-expression': {
+      'patterns': [
+        { 'include': '#primary-expression' },
+        { 'include': '#postfix-expression' },
+        { 'include': '#identifier' },
+        { 'include': '#template-instance' },
+        { 'include': '#new-expression' },
+        {
+          'match': '(\\+\\+)|(--)',
+          'name': 'keyword.operator.postfix-op.d'
+        },
+        {
+          'begin': '\\(',
+          'beginCaptures': 'support.type.parens-expression.begin.d',
+          'end': '\\)',
+          'endCaptures': 'support.type.parens-expression.end.d',
+          'patterns': [
+              { 'include': '#argument-list' }
+          ]
+        },
+        { 'include': '#type-ctors' },
+        { 'include': '#basic-type' },
+        { 'include': '#index-expression' },
+        { 'include': '#slice-expression' }
+      ]
+    },
+    'index-expression': {
+      'patterns': [
+        { 'include': '#postfix-expression' },
+        {
+          'begin': '\\[',
+          'beginCaptures': 'support.type.index.bracket.begin.d',
+          'end': '\\]',
+          'endCaptures': 'support.type.index.bracket.end.d',
+          'patterns': [
+              { 'include': '#argument-list' },
+          ]
+        }
+      ]
+    },
+    'slice-expression': {
+      'patterns': [
+        { 'include': '#postfix-expression' },
+        {
+          'begin': '\\[',
+          'beginCaptures': 'support.type.slice.bracket.begin.d',
+          'end': '\\]',
+          'endCaptures': 'support.type.slice.bracket.end.d',
+          'patterns': [
+              { 'include': '#assign-expression' },
+              {
+                'match': '\\.\\.',
+                'name': 'keyword.operator.range.d'
+              }
+          ]
+        }
+      ]
+    },
+    'primary-expression': {
+      'patterns': [
+        {
+          'match': 'this|super|null|true|false|$',
+          'name': 'constant.language.primary-expression.d'
+        },
+        { 'include': '#identifier' },
+        { 'include': '#template-instance' },
+        { 'include': '#integer-literal' },
+        { 'include': '#float-literal' },
+        { 'include': '#character-literal' },
+        { 'include': '#string-literals' },
+        { 'include': '#array-literal' },
+        { 'include': '#assoc-array-literal' },
+        { 'include': '#function-literal' },
+        { 'include': '#assert-expression' },
+        { 'include': '#mixin-expression' },
+        { 'include': '#import-expression' },
+        { 'include': '#new-expression-with-args' },
+        { 'include': '#basic-type-x' },
+        { 'include': '#typeof' },
+        { 'include': '#typeid-expression' },
+        { 'include': '#is-expression' },
+        {
+          'begin': '\\(',
+          'beginCaptures': 'support.type.parens-expression.begin.d',
+          'end': '\\)',
+          'endCaptures': 'support.type.parens-expression.end.d',
+          'patterns': [
+              { 'include': '#expression' }
+          ]
+        },
+        { 'include': '#traits-expression' },
+        { 'include': '#special-keyword' }
+      ]
+    },
+    'string-literals': {
+      'patterns': [
+        { 'include': '#string-literal' }
+      ]
+    },
+    'array-literal': {
+      'patterns': [
+        'begin': '\\[',
+        'beginCaptures': 'support.type.array.bracket.begin.d',
+        'end': '\\]',
+        'endCaptures': 'support.type.array.bracket.end.d',
+        'patterns': [
+            { 'include': '#argument-list' }
+        ]
+      ]
+    },
+    'assoc-array-literal': {
+      'patterns': [
+        'begin': '\\[',
+        'beginCaptures': 'support.type.assoc-array.bracket.begin.d',
+        'end': '\\]',
+        'endCaptures': 'support.type.assoc-array.bracket.end.d',
+        'patterns': [
+            { 'include': '#key-value-pairs' }
+        ]
+      ]
+    },
+    'key-value-pairs': {
+      'patterns': [
+        { 'include': '#key-value-pair' },
+        { 'include': '#comma' }
+      ]
+    },
+    'key-value-pair': {
+      'patterns': [
+        { 'include': '#key-expression' },
+        { 'include': '#colon' },
+        { 'include': '#value-expression' }
+      ]
+    },
+    'key-expression': {
+      'patterns': [
+        { 'include': '#assign-expression' }
+      ]
+    },
+    'value-expression': {
+      'patterns': [
+        { 'include': '#assign-expression' }
+      ]
+    },
+    'function-literal': {
+      'patterns': [
+        {
+          'match': '(function|delegate)\\s+',
+          'name': 'entity.name.function.d'
+        },
+        { 'include': '#type' },
+        { 'include': '#parameter-attributes' },
+        { 'include': '#function-body' },
+        { 'include': '#lambda' }
+      ]
+    },
+    'parameter-attributes': {
+      'patterns': [
+        { 'include': '#parameters' },
+        { 'include': '#function-attributes' }
+      ]
+    },
+    'lambda': {
+      'patterns': [
+        {
+          'match': '=>',
+          'name': 'keyword.operator.lambda.d'
+        },
+        { 'include': '#assign-expression' },
+        { 'include': '#identifier' },
+        {
+          'match': '(function|delegate)\\s+',
+          'name': 'entity.name.function.d'
+        },
+        { 'include': '#parameter-attributes' }
+      ]
+    },
+    'assert-expression': {
+      'patterns': [
+        'begin': 'assert\\s+\\(',
+        'beginCaptures': 'keyword.other.assert.begin.d',
+        'end': '\\)',
+        'endCaptures': 'keyword.other.assert.end.d',
+        'patterns': [
+          { 'include': '#assign-expression' },
+          { 'include': '#comma' }
+        ]
+      ]
+    },
+    'mixin-expression': {
+      'patterns': [
+        'begin': 'mixin\\s+\\(',
+        'beginCaptures': 'keyword.other.mixin.begin.d',
+        'end': '\\)',
+        'endCaptures': 'keyword.other.mixin.end.d',
+        'patterns': [
+          { 'include': '#assign-expression' }
+        ]
+      ]
+    },
+    'import-expression': {
+      'patterns': [
+        'begin': 'import\\s+\\(',
+        'beginCaptures': 'keyword.other.import.begin.d',
+        'end': '\\)',
+        'endCaptures': 'keyword.other.import.end.d',
+        'patterns': [
+          { 'include': '#assign-expression' }
+        ]
+      ]
+    },
+    'typeid-expression': {
+      'patterns': [
+        'begin': 'typeid\\s+\\(',
+        'beginCaptures': 'keyword.other.typeid.begin.d',
+        'end': '\\)',
+        'endCaptures': 'keyword.other.typeid.end.d',
+        'patterns': [
+          { 'include': '#type' },
+          { 'include': '#expression' }
+        ]
+      ]
+    },
+    'is-expression': {
+      'patterns': [
+        'begin': 'is\\s+\\(',
+        'beginCaptures': 'keyword.control.is.begin.d',
+        'end': '\\)',
+        'endCaptures': 'keyword.control.is.end.d',
+        'patterns': [
+          { 'include': '#type' },
+          {
+            'match': '==',
+            'name': 'keyword.operator.equivalency.d'
+          },
+          { 'include': '#colon' },
+          { 'include': '#type-specialization' },
+          { 'include': '#comma' },
+          { 'include': '#template-parameter-list' },
+          { 'include': '#identifier' }
+        ]
+      ]
+    },
+    'type-specialization': {
+      'patterns': [
+        {
+          'match': 'struct|union|class|interface|enum|function|delegate|super|const|immutable|inout|shared|return|__parameters',
+          'name': 'keyword.other.storage.type-specialization.d'
+        },
+        { 'include': '#type' }
+      ]
+    },
+    'traits-expression': {
+      'patterns': [
+        'begin': '__traits\\s+\\(',
+        'beginCaptures': 'keyword.other.traits.begin.d',
+        'end': '\\)',
+        'endCaptures': 'keyword.other.traits.end.d',
+        'patterns': [
+          { 'include': '#traits-keyword' },
+          { 'include': '#comma' },
+          { 'include': '#traits-argument' }
+        ]
+      ]
+    },
+    'traits-keyword': {
+      'patterns': [
+        'match': 'isAbstractClass|isArithmetic|isAssociativeArray|isFinalClass|isPOD|isNested|isFloating|isIntegral|isScalar|isStaticArray|isUnsigned|isVirtualFunction|isVirtualMethod|isAbstractFunction|isFinalFunction|isStaticFunction|isOverrideFunction|isRef|isOut|isLazy|hasMember|identifier|getAliasThis|getAttributes|getMember|getOverloads|getProtection|getVirtualFunctions|getVirtualMethods|getUnitTests|parent|classInstanceSize|getVirtualIndex|allMembers|derivedMembers|isSame|compiles',
+        'name': 'support.constant.traits-keyword.d'
+      ]
+    },
+    'traits-arguments': {
+      'patterns': [
+        { 'include': '#traits-argument' },
+        { 'include': '#comma' }
+      ]
+    },
+    'traits-argument': {
+      'patterns': [
+        { 'include': '#assign-expression' },
+        { 'include': '#type' }
+      ]
+    },
+    'special-keyword': {
+      'patterns': [
+        'match': '__FILE__|__MODULE__|__LINE__|__FUNCTION__|__PRETTY_FUNCTION__',
+        'name': 'constant.language.special-keyword.d'
+      ]
+    },
     ###########################################################################
     # Statement
     ###########################################################################


removed DHT marker FFC4 from list of SOF markers

--- lib/image/jpeg.coffee
+++ lib/image/jpeg.coffee
@@ -8,7 +8,7 @@
         if data.readUInt16() isnt 0xFFD8
             throw "SOI not found in JPEG"

-        markers = [0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC4, 0xFFC5, 0xFFC6, 0xFFC7,
+        markers = [0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC5, 0xFFC6, 0xFFC7,
                    0xFFC8, 0xFFC9, 0xFFCA, 0xFFCB, 0xFFCC, 0xFFCD, 0xFFCE, 0xFFCF]

         while data.pos < len


update event name for handson table

update event name for handson table

--- bokehjs/src/coffee/widget/handson_table.coffee
+++ bokehjs/src/coffee/widget/handson_table.coffee
@@ -14,15 +14,15 @@
       @listenTo(@model, 'change', () => @renderFn())
       source = @mget("source")
       @listenTo(source, 'change:data', () => @renderFn())
-      @listenTo(source, 'change:selected', () => @changeSelection())
+      @listenTo(source, 'change:selection', () => @changeSelection())

     changeSelection: () ->
       @ht.deselectCell()

       # NOTE: only linear selection allowed by ht
-      selected = @mget("source").get("selected")
-      i = _.min(selected)
-      j = _.max(selected)
+      selection = @mget("source").get("selection")
+      i = _.min(selection)
+      j = _.max(selection)
       n = @ht.countCols()
       @ht.selectCell(i, 0, j, n-1, true)


forgot to use defaults in vboxmodelform

forgot to use defaults in vboxmodelform

--- bokehjs/src/coffee/widget/vboxmodelform.coffee
+++ bokehjs/src/coffee/widget/vboxmodelform.coffee
@@ -69,7 +69,7 @@
       defaults =
         _children : []
         _field_defs : {}
-      return {'children' : []}
+      return defaults
   class VBoxModelFormes extends Backbone.Collection
     model : VBoxModelForm
   vboxmodelformes = new VBoxModelFormes()


Atom: add git-diff minimap plugin

Atom: add git-diff minimap plugin

--- opt/config.cson
+++ opt/config.cson
@@ -23,7 +23,9 @@
     pythonExecutablePath: "/usr/local/bin/python"
     rubyExecutablePath: "/usr/bin/ruby"
   linter: {}
-  minimap: {}
+  minimap:
+    plugins:
+      "git-diff": true
   "linter-jshint": {}
   "linter-htmlhint": {}
   "linter-shellcheck":


:bug: Disable AutoComplete on Commented Line/Block Fixes steelbrain/atom-hack#19

--- lib/autocomplete.coffee
+++ lib/autocomplete.coffee
@@ -2,6 +2,7 @@
   Instance = null
   Provider =
     selector: '.source.php,.source.cpp'
+    blacklist: '.comment'
     requestHandler: (options) ->
       console.log options
       [{


fix #14

fix #14

--- lib/block-cursor.coffee
+++ lib/block-cursor.coffee
@@ -92,8 +92,8 @@
     @stylesheet.sheet.insertRule """
       #{@selectorForScope scopeName} {
         #{primaryColor.property}: #{primaryColor.toRGBAString()};
-        #{if pulseDuration? then "transition-duration: #{pulseDuration}" else ''}
-        #{if cursorThickness? then "border-width: #{cursorThickness}" else ''}
+        #{if pulseDuration? then "transition-duration: #{pulseDuration};" else ''}
+        #{if cursorThickness? then "border-width: #{cursorThickness};" else ''}
       }
     """, @cssRulesIndexes[scopeName]
     @stylesheet.sheet.insertRule """


Modified openFileContainingVar to work on all platforms.

The Clojure openFileContainingVar script used to fail on Windows,
because it did not handle the path in a multiplatform safe way and it
was relying on the unzip external utility that does not exist on
Windows.

I changed the code to generate valid paths no matter what platform we're
running on by relying on Java's path manipulation constructs instead of
string concatenation.

I also modified the unziping of the Jar to be performed all in Clojure
instead of relying on an external tool.

These changes were made to fix the "proto-repl:open-file-containing-var"
command on Windows.

Issues Related:
- https://github.com/jasongilman/proto-repl/issues/172
- https://github.com/jasongilman/proto-repl/issues/217

--- lib/proto-repl.coffee
+++ lib/proto-repl.coffee
@@ -689,33 +689,57 @@
       if editor = atom.workspace.getActiveTextEditor()
         if selected = @getClojureVarUnderCursor(editor)
           text = "(do (require 'clojure.repl)
-                      (require 'clojure.java.shell)
-                      (require 'clojure.java.io)
-                      (let [var-sym '#{selected}
-                            the-var (or (some->> (or (get (ns-aliases *ns*) var-sym) (find-ns var-sym))
-                                                 clojure.repl/dir-fn
-                                                 first
-                                                 name
-                                                 (str (name var-sym) \"/\")
-                                                 symbol)
-                                        var-sym)
-                            {:keys [file line]} (meta (eval `(var ~the-var)))
-                            file-path (.getPath (.getResource (clojure.lang.RT/baseLoader) file))]
-                        (if-let [[_
-                                  jar-path
-                                  partial-jar-path
-                                  within-file-path] (re-find #\"file:(.+/\\.m2/repository/(.+\\.jar))!/(.+)\" file-path)]
-                          (let [decompressed-path (str (System/getProperty \"user.home\")
-                                                       \"/.lein/tmp-atom-jars/\"
-                                                       partial-jar-path)
-                                decompressed-file-path (str decompressed-path \"/\" within-file-path)
-                                decompressed-path-dir (clojure.java.io/file decompressed-path)]
-                            (when-not (.exists decompressed-path-dir)
-                              (println \"decompressing\" jar-path \"to\" decompressed-path)
-                              (.mkdirs decompressed-path-dir)
-                              (clojure.java.shell/sh \"unzip\" jar-path \"-d\" decompressed-path))
-                            [decompressed-file-path line])
-                          [file-path line])))"
+              (require 'clojure.java.shell)
+              (require 'clojure.java.io)
+              (import [java.io File])
+              (import [java.util.jar JarFile])
+              (let [var-sym '#{selected}
+                    the-var (or (some->> (or (get (ns-aliases *ns*) var-sym) (find-ns var-sym))
+                                         clojure.repl/dir-fn
+                                         first
+                                         name
+                                         (str (name var-sym) \"/\")
+                                         symbol)
+                                var-sym)
+                    {:keys [file line]} (meta (eval `(var ~the-var)))
+                    file-path (loop [paths (remove empty? (clojure.string/split (.getPath (.toURI (java.io.File. file)))
+                                                                                #\"/\"))]
+                                (when-not (empty? paths)
+                                  (let [path (clojure.string/join \"/\" paths)
+                                        res (.getResource (clojure.lang.RT/baseLoader) path)]
+                                    (if-not (nil? res)
+                                            (let [uri (.normalize (.toURI (.getResource (clojure.lang.RT/baseLoader) path)))]
+                                              (if (.isOpaque uri)
+                                                (let [url (.toURL uri)
+                                                      conn (.openConnection url)
+                                                      file (java.io.File. (.. conn getJarFileURL toURI))]
+                                                  (str (.getAbsolutePath file) \"!\" (second (clojure.string/split (.getPath url) #\"!\"))))
+                                                (.getAbsolutePath (java.io.File. uri))))
+                                            (recur (rest paths))))))]
+                (if-let [[_
+                          jar-path
+                          partial-jar-path
+                          within-file-path] (re-find #\"(.+\\.m2.repository.(.+\\.jar))!/(.+)\" file-path)]
+                  (let [decompressed-path (.getAbsolutePath
+                                           (File. (.getAbsolutePath (File.
+                                                                     (System/getProperty \"user.home\")
+                                                                     \"/.lein/tmp-atom-jars/\"))
+                                                  partial-jar-path))
+                        decompressed-file-path (.getAbsolutePath (File. decompressed-path within-file-path))
+                        decompressed-path-dir (clojure.java.io/file decompressed-path)]
+                    (when-not (.exists decompressed-path-dir)
+                      (println \"decompressing\" jar-path \"to\" decompressed-path)
+                      (.mkdirs decompressed-path-dir)
+                      (let [jar-file (JarFile. jar-path)]
+                        (run! (fn [jar-entry]
+                                (let [file (File. decompressed-path (.getName jar-entry))]
+                                  (if (.isDirectory jar-entry)
+                                    (.mkdir file)
+                                    (with-open [is (.getInputStream jar-file jar-entry)]
+                                               (clojure.java.io/copy is file)))))
+                              (seq (.toArray (.stream jar-file))))))
+                    [decompressed-file-path line])
+                  [file-path line])))"
           @executeCodeInNs text,
             displayInRepl: false
             resultHandler: (result)=>


ComponentLibrary: Resolve paths when loading

--- lib/componentlib.coffee
+++ lib/componentlib.coffee
@@ -80,7 +80,7 @@
         bluebird.map(paths, expandDirectory).then (expanded) =>
             files = [].concat.apply([], expanded) # flatten
             for p in files.filter isComponentFile
-                @loadFile p, options
+                @loadFile path.resolve(p), options
         .asCallback(callback)

         return null # avoid returning promise


Left in a console.log.

Left in a console.log.

--- lib/stacktrace.coffee
+++ lib/stacktrace.coffee
@@ -13,7 +13,6 @@

   @parse: (text) ->
     frames = (Frame.parse(line) for line in text.split(/\r?\n/))
-    console.log frames
     new Stacktrace(frames)

 # Internal: A single stack frame within a {Stacktrace}.


some todos

some todos

--- lib/ui/console.coffee
+++ lib/ui/console.coffee
@@ -1,3 +1,5 @@
+# TODO: modules, history, modes
+
 comm = require '../connection/comm'

 module.exports =


:fire: remove haml task

:fire: remove haml task

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -222,7 +222,7 @@
 gulp.task 'watch:reload', ->
   gulp.watch watchForReload, ['reload']

-gulp.task 'watch:templete', ['watch:haml', 'watch:jade']
+gulp.task 'watch:templete', ['watch:jade']

 gulp.task 'watch', ['watch:main', 'watch:templete']


Refactor gulpfile related to i18n and config file loading.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -19,66 +19,72 @@
 cache = require("gulp-cached")
 jadeInheritance = require('gulp-jade-inheritance')

-paths = {
-    app: "app"
-    dist: "dist"
-    html: "app/*.html"
-    jade: ["app/index.jade", "app/partials/**/*.jade"]
-    scssStyles: "app/styles/**/*.scss"
-    distStylesPath: "dist/styles"
-    distStyles: ["dist/styles/vendor.css",
-                 "dist/styles/app.css"]
-    sassStylesMain: "app/styles/main.scss"
-    css:  "app/styles/vendor/*.css"
-    images: "app/images/**/*"
-    svg: "app/svg/**/*"
-    locales: "app/locales/**/*.json"
-    coffee: ["app/coffee/app.coffee",
-             "app/config/*.coffee",
-             "app/coffee/*.coffee",
-             "app/coffee/modules/controllerMixins.coffee",
-             "app/coffee/modules/*.coffee",
-             "app/coffee/modules/common/*.coffee",
-             "app/coffee/modules/backlog/*.coffee",
-             "app/coffee/modules/taskboard/*.coffee",
-             "app/coffee/modules/kanban/*.coffee",
-             "app/coffee/modules/issues/*.coffee",
-             "app/coffee/modules/userstories/*.coffee",
-             "app/coffee/modules/tasks/*.coffee",
-             "app/coffee/modules/wiki/*.coffee",
-             "app/coffee/modules/admin/*.coffee",
-             "app/coffee/modules/projects/*.coffee",
-             "app/coffee/modules/locales/*.coffee",
-             "app/coffee/modules/base/*.coffee",
-             "app/coffee/modules/resources/*.coffee",
-             "app/coffee/modules/user-settings/*.coffee"
-             "app/plugins/**/*.coffee"]
-    vendorJsLibs: [
-        "app/vendor/jquery/dist/jquery.js",
-        "app/vendor/lodash/dist/lodash.js",
-        "app/vendor/emoticons/lib/emoticons.js",
-        "app/vendor/underscore.string/lib/underscore.string.js",
-        "app/vendor/angular/angular.js",
-        "app/vendor/angular-route/angular-route.js",
-        "app/vendor/angular-sanitize/angular-sanitize.js",
-        "app/vendor/angular-animate/angular-animate.js",
-        "app/vendor/i18next/i18next.js",
-        "app/vendor/moment/min/moment-with-langs.js",
-        "app/vendor/checksley/checksley.js",
-        "app/vendor/pikaday/pikaday.js",
-        "app/vendor/jquery-flot/jquery.flot.js",
-        "app/vendor/jquery-flot/jquery.flot.pie.js",
-        "app/vendor/jquery-flot/jquery.flot.time.js",
-        "app/vendor/jquery-flot/jquery.flot.time.js",
-        "app/vendor/flot-axislabels/jquery.flot.axislabels.js",
-        "app/vendor/jquery-textcomplete/jquery.textcomplete.js",
-        "app/vendor/markitup/markitup/jquery.markitup.js",
-        "app/vendor/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js",
-        "app/js/jquery.ui.git.js",
-        "app/js/sha1.js",
-        "app/plugins/**/*.js"
-    ]
-}
+paths = {}
+paths.app = "app/"
+paths.dist = "dist/"
+paths.tmp = "tmp/"
+
+paths.jade = [
+    paths.app + "index.jade",
+    paths.app + "partials/**/*.jade"
+]
+
+paths.images = paths.app + "images/**/*"
+paths.svg = paths.app + "svg/**/*"
+paths.css = paths.app + "app/styles/vendor/*.css"
+paths.locales = paths.app + "locales/**/*.json"
+paths.sass = [
+    paths.app + "styles/**/*.scss"
+    "!#{paths.app}/styles/bourbon/**/*.scss"
+]
+
+paths.coffee = [
+    paths.app + "coffee/app.coffee",
+    paths.app + "coffee/*.coffee",
+    paths.app + "coffee/modules/controllerMixins.coffee",
+    paths.app + "coffee/modules/*.coffee",
+    paths.app + "coffee/modules/common/*.coffee",
+    paths.app + "coffee/modules/backlog/*.coffee",
+    paths.app + "coffee/modules/taskboard/*.coffee",
+    paths.app + "coffee/modules/kanban/*.coffee",
+    paths.app + "coffee/modules/issues/*.coffee",
+    paths.app + "coffee/modules/userstories/*.coffee",
+    paths.app + "coffee/modules/tasks/*.coffee",
+    paths.app + "coffee/modules/wiki/*.coffee",
+    paths.app + "coffee/modules/admin/*.coffee",
+    paths.app + "coffee/modules/projects/*.coffee",
+    paths.app + "coffee/modules/locales/*.coffee",
+    paths.app + "coffee/modules/base/*.coffee",
+    paths.app + "coffee/modules/resources/*.coffee",
+    paths.app + "coffee/modules/user-settings/*.coffee"
+    paths.app + "plugins/**/*.coffee"
+]
+
+paths.js = [
+    paths.app + "vendor/jquery/dist/jquery.js",
+    paths.app + "vendor/lodash/dist/lodash.js",
+    paths.app + "vendor/emoticons/lib/emoticons.js",
+    paths.app + "vendor/underscore.string/lib/underscore.string.js",
+    paths.app + "vendor/angular/angular.js",
+    paths.app + "vendor/angular-route/angular-route.js",
+    paths.app + "vendor/angular-sanitize/angular-sanitize.js",
+    paths.app + "vendor/angular-animate/angular-animate.js",
+    paths.app + "vendor/i18next/i18next.js",
+    paths.app + "vendor/moment/min/moment-with-langs.js",
+    paths.app + "vendor/checksley/checksley.js",
+    paths.app + "vendor/pikaday/pikaday.js",
+    paths.app + "vendor/jquery-flot/jquery.flot.js",
+    paths.app + "vendor/jquery-flot/jquery.flot.pie.js",
+    paths.app + "vendor/jquery-flot/jquery.flot.time.js",
+    paths.app + "vendor/jquery-flot/jquery.flot.time.js",
+    paths.app + "vendor/flot-axislabels/jquery.flot.axislabels.js",
+    paths.app + "vendor/jquery-textcomplete/jquery.textcomplete.js",
+    paths.app + "vendor/markitup/markitup/jquery.markitup.js",
+    paths.app + "vendor/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js",
+    paths.app + "js/jquery.ui.git.js",
+    paths.app + "js/sha1.js",
+    paths.app + "plugins/**/*.js"
+]

 ############################################################################
 # Layout/CSS Related tasks
@@ -89,125 +95,153 @@
         .pipe(plumber())
         .pipe(cache("jade"))
         .pipe(jade({pretty: false}))
-        .pipe(gulp.dest("#{paths.dist}/partials"))
+        .pipe(gulp.dest(paths.dist + "partials/"))

 gulp.task "jade-watch", ->
     gulp.src(paths.jade)
         .pipe(plumber())
         .pipe(cache("jade"))
-        .pipe(jadeInheritance({basedir: './app'}))
+        .pipe(jadeInheritance({basedir: "./app"}))
         .pipe(jade({pretty: true}))
-        .pipe(gulp.dest("#{paths.dist}"))
-
-gulp.task "template", ->
-    gulp.src("#{paths.app}/index.jade")
+        .pipe(gulp.dest(paths.dist))
+
+gulp.task "templates", ->
+    gulp.src(paths.app + "index.jade")
         .pipe(plumber())
         .pipe(jade({pretty: true, locals:{v:(new Date()).getTime()}}))
-        .pipe(gulp.dest("#{paths.dist}"))
+        .pipe(gulp.dest(paths.dist))

 gulp.task "sass-lint", ->
-    gulp.src([paths.scssStyles, '!app/styles/bourbon/**/*.scss'])
+    gulp.src(paths.sass)
         .pipe(cache("sasslint"))
         .pipe(scsslint({config: "scsslint.yml"}))

 gulp.task "sass-watch", ["sass-lint"], ->
-    gulp.src(paths.sassStylesMain)
+    gulp.src(paths.app + "styles/main.scss")
         .pipe(plumber())
         .pipe(sass())
         .pipe(rename("app.css"))
-        .pipe(gulp.dest(paths.distStylesPath))
+        .pipe(gulp.dest(paths.tmp))

 gulp.task "sass-deploy", ->
-    gulp.src(paths.sassStylesMain)
+    gulp.src(paths.app + "styles/main.scss")
         .pipe(plumber())
         .pipe(sass())
         .pipe(rename("app.css"))
-        .pipe(gulp.dest(paths.distStylesPath))
+        .pipe(gulp.dest(paths.tmp))

 gulp.task "css-vendor", ->
     gulp.src(paths.css)
         .pipe(concat("vendor.css"))
-        .pipe(gulp.dest(paths.distStylesPath))
+        .pipe(gulp.dest(paths.tmp))

 gulp.task "css-lint-app", ["sass-watch"],  ->
-    gulp.src(paths.distStylesPath + "/app.css")
+    gulp.src(paths.tmp + "app.css")
         .pipe(csslint("csslintrc.json"))
         .pipe(csslint.reporter())

 gulp.task "styles-watch", ["sass-watch", "css-vendor", "css-lint-app"], ->
-    gulp.src(paths.distStyles)
+    _paths = [
+        paths.tmp + "vendor.css",
+        paths.tmp + "app.css"
+    ]
+
+    gulp.src(_paths)
         .pipe(concat("main.css"))
-        .pipe(gulp.dest(paths.distStylesPath))
+        .pipe(gulp.dest(paths.dist + "styles/"))

 gulp.task "styles-deploy", ["sass-deploy", "css-vendor"], ->
-    gulp.src(paths.distStyles)
+    _paths = [
+        paths.tmp + "vendor.css",
+        paths.tmp + "app.css"
+    ]
+
+    gulp.src(_paths)
         .pipe(concat("main.css"))
         .pipe(minifyCSS())
-        .pipe(gulp.dest(paths.distStylesPath))
+        .pipe(gulp.dest(paths.dist + "styles/"))

 ##############################################################################
 # JS Related tasks
 ##############################################################################
+
+gulp.task "conf", ->
+    gulp.src("conf/main.json")
+        .pipe(wrap("angular.module('taigaBase').value('localconf', <%= contents %>);"))
+        .pipe(concat("conf.js"))
+        .pipe(gulp.dest(paths.tmp));

 gulp.task "locales", ->
     gulp.src("app/locales/en/app.json")
-        .pipe(wrap("angular.module('taigaLocales').constant('localesEnglish', <%= contents %>);"))
-        .pipe(rename("localeEnglish.coffee"))
-        .pipe(gulp.dest("app/coffee/modules/locales"))
-
-    # gulp.src("app/locales/es/app.json")
-    #     .pipe(wrap("angular.module('locales.es', []).constant('locales.es', <%= contents %>);"))
-    #     .pipe(rename("locale.es.coffee"))
-    #     .pipe(gulp.dest("app/coffee/"))
-
-gulp.task "coffee-watch", ["locales"], ->
+        .pipe(wrap("angular.module('taigaBase').value('localesEn', <%= contents %>);"))
+        .pipe(rename("locales.en.js"))
+        .pipe(gulp.dest(paths.tmp))
+
+gulp.task "coffee", ->
     gulp.src(paths.coffee)
         .pipe(plumber())
         .pipe(coffee())
         .pipe(concat("app.js"))
-        .pipe(gulp.dest("dist/js/"))
-
-gulp.task "coffee-deploy", ["locales"], ->
-    gulp.src(paths.coffee)
-        .pipe(plumber())
-        .pipe(coffee())
-        .pipe(concat("app.js"))
-        .pipe(uglify({mangle:false, preserveComments: false}))
-        .pipe(gulp.dest("dist/js/"))
+        .pipe(gulp.dest(paths.tmp))

 gulp.task "jslibs-watch", ->
-    gulp.src(paths.vendorJsLibs)
+    gulp.src(paths.js)
         .pipe(plumber())
         .pipe(concat("libs.js"))
         .pipe(gulp.dest("dist/js/"))

 gulp.task "jslibs-deploy", ->
-    gulp.src(paths.vendorJsLibs)
+    gulp.src(paths.js)
         .pipe(plumber())
         .pipe(concat("libs.js"))
         .pipe(uglify({mangle:false, preserveComments: false}))
         .pipe(gulp.dest("dist/js/"))

+gulp.task "app-watch", ["coffee", "conf", "locales"], ->
+    _paths = [
+        paths.tmp + "app.js",
+        paths.tmp + "conf.js",
+        paths.tmp + "locales.en.js"
+    ]
+
+    gulp.src(_paths)
+        .pipe(concat("app.js"))
+        .pipe(gulp.dest(paths.dist + "js/"));
+
+gulp.task "app-deploy", ["coffee", "conf", "locales"], ->
+    _paths = [
+        paths.tmp + "app.js",
+        paths.tmp + "conf.js",
+        paths.tmp + "locales.en.js"
+    ]
+
+    gulp.src(_paths)
+        .pipe(concat("app.js"))
+        .pipe(uglify({mangle:false, preserveComments: false}))
+        .pipe(gulp.dest(paths.dist + "js/"));
+
 ##############################################################################
 # Common tasks
 ##############################################################################

 # SVG
-gulp.task "svg",  ->
+gulp.task "copy-svg",  ->
     gulp.src("#{paths.app}/svg/**/*")
         .pipe(gulp.dest("#{paths.dist}/svg/"))

-# Copy Files
-gulp.task "copy",  ->
+gulp.task "copy-fonts",  ->
     gulp.src("#{paths.app}/fonts/*")
         .pipe(gulp.dest("#{paths.dist}/fonts/"))

+gulp.task "copy-images",  ->
     gulp.src("#{paths.app}/images/**/*")
         .pipe(gulp.dest("#{paths.dist}/images/"))

+gulp.task "copy-plugin-templates",  ->
     gulp.src("#{paths.app}/plugins/**/templates/*")
         .pipe(gulp.dest("#{paths.dist}/plugins/"))

+gulp.task "copy", ["copy-fonts", "copy-images", "copy-plugin-templates", "copy-svg"]

 gulp.task "express", ->
     express = require("express")
@@ -230,32 +264,33 @@
 # Rerun the task when a file changes
 gulp.task "watch", ->
     gulp.watch(paths.jade, ["jade-watch"])
-    gulp.watch("#{paths.app}/index.jade", ["template"])
-    gulp.watch(paths.scssStyles, ["styles-watch"])
-    gulp.watch(paths.svg, ["svg"])
-    gulp.watch(paths.coffee, ["coffee-watch"])
-    gulp.watch(paths.vendorJsLibs, ["jslibs-watch"])
-    gulp.watch(paths.locales, ["coffee-watch"])
+    gulp.watch(paths.app + "index.jade", ["templates"])
+    gulp.watch(paths.sass, ["styles-watch"])
+    gulp.watch(paths.svg, ["copy-svg"])
+    gulp.watch(paths.coffee, ["app-watch"])
+    gulp.watch(paths.js, ["jslibs-watch"])
+    gulp.watch(paths.locales, ["app-watch"])
+    gulp.watch(paths.images, ["copy-images"])
+    gulp.watch(paths.fonts, ["copy-fonts"])
+

 gulp.task "deploy", [
+    "templates",
+    "copy",
     "jade-deploy",
-    "template",
-    "copy",
-    "svg",
-    "coffee-deploy",
+    "app-deploy",
     "jslibs-deploy",
     "styles-deploy"
 ]

 # The default task (called when you run gulp from cli)
 gulp.task "default", [
+    "copy",
+    "templates",
+    "styles-watch",
+    "app-watch",
+    "jslibs-watch",
     "jade-deploy",
-    "template",
-    "styles-watch",
-    "svg",
-    "copy",
-    "coffee-watch",
-    "jslibs-watch",
     "express",
     "watch"
 ]


change error occured as error occurred

change error occured as error occurred

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -372,5 +372,5 @@

 process.on 'uncaughtException', (err)->

-  log 'red', "An error has occured: #{err.name}"
+  log 'red', "An error has occurred: #{err.name}"
   console.error err


Autoprefixer設定変更

Autoprefixer設定変更

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -13,7 +13,7 @@
 PORT = '3501'
 AUTOPREFIXER_BROWSERS = [
     'last 2 versions'
-    'IE >= 8'
+    'IE >= 9'
     'Firefox ESR'
     'Android >= 4.1'
 ]


fix timer command

fix timer command

--- commands/timer.coffee
+++ commands/timer.coffee
@@ -69,8 +69,7 @@
     if time
         setTimeout (-> respond "#{text}"), time
         respond "Ok."
-
-<<<<<<< HEAD
+
 count = (num, dir, down, respond) ->
     num = +num

@@ -123,14 +122,11 @@
             "I don't have time for that."
         ]

-=======
->>>>>>> d63fddf4c5a6a64eb0907afeb287e3cb458b6c6a
 brain.addPattern 'timer',
     match: /set (timer|alarm|reminder) (?:to )?(.*)/i
     fn: (user, m, cb) -> setTimer m[2], user, cb

 brain.addPattern 'reminder',
-<<<<<<< HEAD
     match: /remind (\w+) (?:to|of|about) (.+) in (.+)/i
     fn: (user, m, cb) -> setReminder m[3], m[2], (if m[1] == 'me' then user else m[1]), cb

@@ -143,12 +139,4 @@
 brain.addPattern 'countdown',
     match: /count(down)? ?(from|to)? (\d+)/i
     fn: (user, m, cb) -> count m[3], m[2], m[1], cb
-
-=======
-    match: [/remind (\w+) (?:to|of|about) (.+) in (.+)/i]
-    fn: (user, m, cb) -> setReminder m[3], m[2], (if m[1] == 'me' then user else m[1]), cb
-
-brain.addPattern 'sayTimer',
-    match: [/say (.+) in (.+)/i]
-    fn: (user, m, cb) -> setSayTimer m[2], m[1], user, cb
->>>>>>> d63fddf4c5a6a64eb0907afeb287e3cb458b6c6a
+


Remove homepage nad installation link from Executables required properties

--- src/beautifiers/executable.coffee
+++ src/beautifiers/executable.coffee
@@ -23,9 +23,7 @@
   constructor: (options) ->
     # Validation
     if !options.cmd?
-      throw new Error("Command is required for an Executable.")
-    if !options.homepage? or !options.homepage?
-      throw new Error("Either a homepage or installation page is required for an Executable.")
+      throw new Error("The command (i.e. cmd property) is required for an Executable.")
     @name = options.name
     @cmd = options.cmd
     @key = @cmd.split('-').join('_')


Fixed being unable to disable the spinner

--- progress.coffee
+++ progress.coffee
@@ -86,7 +86,7 @@

 # Default options
 IronRouterProgress.configure
-	element : -> """

"""
+	element : """

"""
 	spinner : true
 	tick    : true

@@ -95,6 +95,7 @@
 	reset : ->
 		@element.removeClass 'loading done'
 		@element.css 'width', '0%'
+		@element.toggleClass 'spinner', @currentOptions.spinner

 		# Hack to reset the CSS transition
 		@element[0].offsetWidth = @element[0].offsetWidth


Dispatcher: faking timeout by changing it to half

--- smsgw/static/src/dispatcher.coffee
+++ smsgw/static/src/dispatcher.coffee
@@ -17,7 +17,7 @@
                     action: action
                     success: yes
                     data: data
-            , 1000
+            , 500
         .error (err) ->
             action = action.split '_'
             action = "#{action[0]}_ERROR"


Switch servers to use the heroku beta

Switch servers to use the heroku beta

--- source/scripts/utils/config.coffee
+++ source/scripts/utils/config.coffee
@@ -7,7 +7,7 @@
 }

 # Set active server
-active = servers.local
+active = servers.heroku

 module.exports =
   server: active


test core.commentchar configs

test core.commentchar configs

--- spec/models/git-commit-spec.coffee
+++ spec/models/git-commit-spec.coffee
@@ -24,6 +24,7 @@
   getItems: -> [
     getURI: -> commitFilePath
   ]
+commentchar_config = ''

 setupMocks = ->
   spyOn(currentPane, 'activate')
@@ -41,8 +42,10 @@

   spyOn(git, 'cmd').andCallFake ->
     args = git.cmd.mostRecentCall.args[0]
-    if args[0] is 'config'
+    if args[0] is 'config' and args[2] is 'commit.template'
       Promise.resolve ''
+    else if args[0] is 'config' and args[2] is 'core.commentchar'
+      Promise.resolve commentchar_config
     else if args[0] is 'status'
       Promise.resolve status
     else if args[0] is 'commit'
@@ -57,19 +60,17 @@

 describe "GitCommit", ->
   describe "a regular commit", ->
-    commit = null
-    beforeEach ->
+    it "gets the current pane", ->
       setupMocks()
-      commit = GitCommit repo
-
-    it "saves the current pane", ->
+      GitCommit repo
       expect(atom.workspace.getActivePane).toHaveBeenCalled()

     describe "::start", ->
       beforeEach ->
         atom.config.set "git-plus.openInPane", false
+        setupMocks()
         waitsForPromise ->
-          commit.start()
+          GitCommit(repo).start()

       it "gets the commentchar from configs", ->
         expect(git.cmd).toHaveBeenCalledWith ['config', '--get', 'core.commentchar']
@@ -83,10 +84,9 @@
       it "gets the commit template from git configs", ->
         expect(git.cmd).toHaveBeenCalledWith ['config', '--get', 'commit.template']

-      it "writes to a file and the commentchar is default '#'", ->
+      it "writes to a file", ->
         argsTo_fsWriteFile = fs.writeFileSync.mostRecentCall.args
         expect(argsTo_fsWriteFile[0]).toEqual commitFilePath
-        expect(argsTo_fsWriteFile[1].trim().charAt(0)).toBe '#'

       it "shows the file", ->
         expect(atom.workspace.open).toHaveBeenCalled()
@@ -105,3 +105,18 @@
         textEditor.destroy()
         expect(currentPane.activate).toHaveBeenCalled()
         expect(fs.unlinkSync).toHaveBeenCalledWith commitFilePath
+
+    describe "when core.commentchar config is not set", ->
+      it "uses '#' in commit file", ->
+        setupMocks()
+        GitCommit(repo).start().then ->
+          argsTo_fsWriteFile = fs.writeFileSync.mostRecentCall.args
+          expect(argsTo_fsWriteFile[1].trim().charAt(0)).toBe '#'
+
+    describe "when core.commentchar config is set to '$'", ->
+      it "uses '$' as the commentchar", ->
+        commentchar_config = '$'
+        setupMocks()
+        GitCommit(repo).start().then ->
+          argsTo_fsWriteFile = fs.writeFileSync.mostRecentCall.args
+          expect(argsTo_fsWriteFile[1].trim().charAt(0)).toBe commentchar_config


add notifications:toggle-log tests

add notifications:toggle-log tests

--- spec/notifications-log-spec.coffee
+++ spec/notifications-log-spec.coffee
@@ -5,15 +5,14 @@
 {generateFakeFetchResponses, generateException} = require './helper'

 describe "Notifications Log", ->
-  [workspaceElement, activationPromise] = []
+  workspaceElement = null

   beforeEach ->
     workspaceElement = atom.views.getView(atom.workspace)
     atom.notifications.clear()
-    activationPromise = atom.packages.activatePackage('notifications')

     waitsForPromise ->
-      activationPromise
+      atom.packages.activatePackage('notifications')

     waitsForPromise ->
       atom.workspace.open(NotificationsLog::getURI())
@@ -34,9 +33,8 @@
       atom.notifications.addNotification(error)
       atom.notifications.addNotification(warning)

-      activationPromise = atom.packages.activatePackage('notifications')
       waitsForPromise ->
-        activationPromise
+        atom.packages.activatePackage('notifications')

       waitsForPromise ->
         atom.workspace.open(NotificationsLog::getURI())
@@ -50,6 +48,7 @@

   describe "when notifications are added to atom.notifications", ->
     notificationsLogContainer = null
+
     beforeEach ->
       enableInitNotification = atom.notifications.addSuccess('A message to trigger initialization', dismissable: true)
       enableInitNotification.dismiss()
@@ -62,29 +61,27 @@
       generateFakeFetchResponses()

     it "adds an .notifications-log-item element to the container with a class corresponding to the type", ->
-      expect(notificationsLogContainer.childNodes.length).toBe 1
-
       atom.notifications.addSuccess('A message')
       notification = notificationsLogContainer.querySelector('.notifications-log-item.success')
-      expect(notificationsLogContainer.childNodes.length).toBe 2
+      expect(notificationsLogContainer.childNodes).toHaveLength 2
       expect(notification.querySelector('.message').textContent.trim()).toBe 'A message'
       expect(notification.querySelector('.btn-toolbar')).toBeEmpty()

       atom.notifications.addInfo('A message')
-      expect(notificationsLogContainer.childNodes.length).toBe 3
+      expect(notificationsLogContainer.childNodes).toHaveLength 3
       expect(notificationsLogContainer.querySelector('.notifications-log-item.info')).toBeDefined()

       atom.notifications.addWarning('A message')
-      expect(notificationsLogContainer.childNodes.length).toBe 4
+      expect(notificationsLogContainer.childNodes).toHaveLength 4
       expect(notificationsLogContainer.querySelector('.notifications-log-item.warning')).toBeDefined()

       atom.notifications.addError('A message')
-      expect(notificationsLogContainer.childNodes.length).toBe 5
+      expect(notificationsLogContainer.childNodes).toHaveLength 5
       expect(notificationsLogContainer.querySelector('.notifications-log-item.error')).toBeDefined()

       atom.notifications.addFatalError('A message')
       notification = notificationsLogContainer.querySelector('.notifications-log-item.fatal')
-      expect(notificationsLogContainer.childNodes.length).toBe 6
+      expect(notificationsLogContainer.childNodes).toHaveLength 6
       expect(notification).toBeDefined()
       expect(notification.querySelector('.btn-toolbar')).not.toBeEmpty()

@@ -234,3 +231,75 @@
             expect(didDismiss).toBe true
             expect(notification.dismissed).toBe true
             expect(notificationView.element).not.toBeVisible()
+
+  describe "the dock pane", ->
+    notificationsLogPane = null
+
+    beforeEach ->
+      notificationsLogPane = atom.workspace.paneForURI(NotificationsLog::getURI())
+
+    describe "when notifications:toggle-log is dispatched", ->
+      it "toggles the pane URI", ->
+        spyOn(atom.workspace, "toggle")
+
+        atom.commands.dispatch(workspaceElement, "notifications:toggle-log")
+        expect(atom.workspace.toggle).toHaveBeenCalledWith(NotificationsLog::getURI())
+
+      describe "when the pane is destroyed", ->
+
+        beforeEach ->
+          notificationsLogPane.destroyItems()
+
+        it "opens the pane", ->
+          [notificationsLog] = []
+
+          waitsForPromise ->
+            atom.workspace.toggle(NotificationsLog::getURI()).then (paneItem) ->
+              notificationsLog = paneItem
+
+          runs ->
+            expect(notificationsLog).toBeDefined()
+
+        describe "when notifications are displayed", ->
+
+          beforeEach ->
+            atom.notifications.addSuccess("success")
+
+          it "lists all notifications", ->
+            waitsForPromise ->
+              atom.workspace.toggle(NotificationsLog::getURI())
+
+            runs ->
+              notificationsLogContainer = workspaceElement.querySelector('.notifications-log-items')
+              expect(notificationsLogContainer.childNodes).toHaveLength 1
+
+      describe "when the pane is hidden", ->
+
+        beforeEach ->
+          atom.workspace.hide(NotificationsLog::getURI())
+
+        it "opens the pane", ->
+          [notificationsLog] = []
+
+          waitsForPromise ->
+            atom.workspace.toggle(NotificationsLog::getURI()).then (paneItem) ->
+              notificationsLog = paneItem
+
+          runs ->
+            expect(notificationsLog).toBeDefined()
+
+      describe "when the pane is open", ->
+
+        beforeEach ->
+          waitsForPromise ->
+            atom.workspace.open(NotificationsLog::getURI())
+
+        it "closes the pane", ->
+          notificationsLog = null
+
+          waitsForPromise ->
+            atom.workspace.toggle(NotificationsLog::getURI()).then (paneItem) ->
+              notificationsLog = paneItem
+
+          runs ->
+            expect(notificationsLog).not.toBeDefined()


Setup hover message on overlay

Setup hover message on overlay

--- src/components/exercise-card.cjsx
+++ src/components/exercise-card.cjsx
@@ -17,6 +17,7 @@
     hideAnswers: React.PropTypes.bool
     toggleExercise: React.PropTypes.func
     isSelected: React.PropTypes.bool
+    hoverMessage: React.PropTypes.string
     exercise:   React.PropTypes.shape(
       content: React.PropTypes.object
       tags:    React.PropTypes.array
@@ -57,7 +58,9 @@

   renderToggleOverlay: ->

-

+

+        {@props.hoverMessage}
+

   render: ->
@@ -71,7 +74,7 @@
     unless @props.displayAllTags
       tags = _.where tags, is_visible: true
     renderedTags = _.map(_.sortBy(tags, 'name'), @renderTag)
-    classes = classnames( 'card', 'exercise', @props.className, {
+    classes = classnames( 'exercise-card', @props.className, {
       'answers-hidden': @props.hideAnswers,
       'is-selectable' : @props.toggleExercise?
       'is-selected': @props.isSelected


publish app api method rewritten

publish app api method rewritten

--- kites/sharedHosting/api.coffee
+++ kites/sharedHosting/api.coffee
@@ -46,7 +46,7 @@
     # options =
     #    contents   : String # file text content
     #
-    console.log 'attempting to upload file', options
+    # console.log 'attempting to upload file', options
     {usersPath,fileUrl} = config
     {username,path,contents} = options
     filename = hat()
@@ -137,31 +137,38 @@
               else
                 log.error error = "[ERROR] couldn't create default vhost for #{username}: #{err}"
                 callback? error
-
+
   publishApp:(options, callback)->
-
+
     {username, version, appName, userAppPath} = options

     latestPath    = "/opt/Apps/#{username}/#{appName}/latest"
     versionedPath = "/opt/Apps/#{username}/#{appName}/#{version}"
-
-    console.log latestPath, versionedPath, version, appName, userAppPath, username
-
+
+    cb = (err)->
+      if err then console.error err
+      else callback? null
+
     mkdirp versionedPath, (err)->
-      if err then console.error err,"mkdirp erorro"
+      if err then cb err
       else
         fs.readFile userAppPath, (err, appScript)->
-          if err then console.error err,"readfile errorr"
+          if err then cb err
           else
             fs.writeFile "#{versionedPath}/index.js", appScript, 'utf-8', (err)->
-              if err then console.error err,"sondan bir"
-              else
-                # fs.symlink latestPath, versionedPath, 'dir', (err)=>
-                exec "rm #{latestPath} && ln -s #{versionedPath} #{latestPath}", (err)->
-                  if err then console.error err,"son error"
-                  else callback?()
-
-
+              if err then cb err
+              else
+                fs.stat latestPath, (err, statObj)->
+                  if err
+                    exec "ln -s #{versionedPath} #{latestPath}", cb
+                  else
+                    if statObj.isSymbolicLink() or statObj.isFile()
+                      exec "rm #{latestPath} && ln -s #{versionedPath} #{latestPath}", cb
+                    else if statObj.isDirectory() and appName.length?
+                      exec "rm -rf #{latestPath} && ln -s #{versionedPath} #{latestPath}", cb
+                    else
+                      cb new KodingError "Something went wrong"
+
   createSystemUser : (options,callback)->
     #
     # This method will create operation system user with default group in LDAP


index cleanups

index cleanups

--- lib/vows/index.coffee
+++ lib/vows/index.coffee
@@ -16,8 +16,6 @@
     return suite

 vows.checkDone = () ->
-    results = { honored: 0, broken: 0, errored: 0, pending: 0, total: 0 }
-
     for suite in suites
         for batch in suite.batches
             if batch.status != 'end'


use the language code $$l as the end of stream marker

cannot use a custom word in the session dictionary due to language
restrictions on allowed characters, e.g. armenian 'hy' does not allow
latin characters

--- app/coffee/ASpellWorker.coffee
+++ app/coffee/ASpellWorker.coffee
@@ -27,10 +27,11 @@
 				@callback err, []

 		output = ""
+		endMarker = new RegExp("^(#{language}|Error)$", "m")
 		@pipe.stdout.on "data", (chunk) =>
 			output = output + chunk
-			# We receive a single "*" from Aspell as the end of data marker
-			if chunk.toString().match(/^\*$/m)
+			# We receive the language code from Aspell as the end of data marker
+			if chunk.toString().match(endMarker)
 				@callback(null, output.slice())
 				@state = 'ready'
 				output = ""
@@ -52,7 +53,6 @@
 		# receive the end of data marker
 		@state = 'busy'
 		@callback = callback
-		@setEndOfStreamMarker()
 		@setTerseMode()
 		@write(words)
 		@flush()
@@ -67,9 +67,10 @@

 	flush: () ->
 		# get aspell to send an end of data marker "*" when ready
-		@sendCommand("%")		# take the aspell pipe out of terse mode so we can look for a '*'
-		@sendCommand("^ENDOFSTREAMMARKER") # send our marker which will generate a '*'
-		@sendCommand("!")		# go back into terse mode
+		#@sendCommand("%")		# take the aspell pipe out of terse mode so we can look for a '*'
+		#@sendCommand("^ENDOFSTREAMMARKER") # send our marker which will generate a '*'
+		#@sendCommand("!")		# go back into terse mode
+		@sendCommand("$$l")

 	shutdown: (reason) ->
 		logger.log process: @pipe.pid, reason: reason, 'shutting down'
@@ -80,11 +81,6 @@
 		logger.log process: @pipe.pid, reason: reason, 'killing'
 		return if @state == 'killed'
 		@pipe.kill('SIGKILL')
-
-	setEndOfStreamMarker: () ->
-		return if @setup
-		@sendCommand("@ENDOFSTREAMMARKER") # make this string a valid word
-		@setup = true

 	setTerseMode: () ->
 		@sendCommand("!")


cache is per minute now

cache is per minute now


Rewrite hookBackboneAjax based on comments, now overrides Backbone.ajax if it exists

--- src/coffee/messenger.coffee
+++ src/coffee/messenger.coffee
@@ -379,6 +379,7 @@
         action: $.ajax

     # When called, will override Backbone.sync to place globalMessenger in the chain.
+    # If using Backbone >= 0.99, will instead override Backbone.ajax
     hookBackboneAjax: (msgr_opts={}) ->

         # Set backbone ajax defaults. These do not override defaults set manually by the programmer
@@ -390,29 +391,36 @@

             showSuccessWithoutError: false

-        # Override Backbone.sync to call $.globalMessenger().do() instead
-        _old_sync = Backbone.sync
-        Backbone.sync = (method, model, options) =>
-
-            # Create ajax override
-            _ajax = (opts) =>
-                if $('html').hasClass('ie9-and-less')
-                    opts.cache = false
-
-                @do sync_msgr_opts, opts
-
-            # Switch ajax methods
-            _old_ajax = $.ajax
-            $.ajax = _ajax
-
+        # Create ajax override
+        _ajax = (options) =>
+
             # if options were provided to this individual call, use them
             # don't clobber default options, use _.clone
             sync_msgr_opts = if options.messenger? then _.clone msgr_opts else msgr_opts
             _.extend sync_msgr_opts, options.messenger

-            _old_sync.call(Backbone, method, model, options)
-
-            $.ajax = _old_ajax
+            if $('html').hasClass('ie9-and-less')
+                options.cache = false
+
+            @do sync_msgr_opts, options
+
+        # If Backbone.ajax exists (Backbone >= 0.99), override it
+        if Backbone.ajax?
+            # We set the action to Backbone.ajax so any previous overrides in Backbone.ajax are not clobbered
+            msgr_opts.action = Backbone.ajax
+            Backbone.ajax = _ajax
+        # Override Backbone.sync if Backbone < 0.99
+        else
+            Backbone.sync = _.wrap Backbone.sync (_old_sync, args...) ->
+                # Switch ajax methods
+                _old_ajax = $.ajax
+                $.ajax = _ajax
+
+                # Call old Backbone.sync (remember to keep Backbone as context)
+                _old_sync.call(Backbone, args)
+
+                # Restore ajax
+                $.ajax = _old_ajax

     _getMessage: (returnVal, def) ->
         if returnVal == false


Upload multiple, duh

Upload multiple, duh

--- app/pages/lab/subject-set.cjsx
+++ app/pages/lab/subject-set.cjsx
@@ -43,7 +43,7 @@

-        <UploadDropTarget accept="text/csv, text/tab-separated-values, image/*" onSelect={@handleFileSelection}>
+        <UploadDropTarget accept="text/csv, text/tab-separated-values, image/*" multiple onSelect={@handleFileSelection}>
           Drag-and-drop manifests and subject images here.

           Manifests must be .csv or .tsv. The first row should define metadata headers. All other rows should include at least one reference to an image filename in the same directory as the manifest.

           Subject images can be any of: {{ext}{', ' if VALID_SUBJECT_EXTENSIONS[i + 1]?} for ext, i in VALID_SUBJECT_EXTENSIONS}{' '}


Revert "Add config option for stylus images."

This reverts commit fd79f20ff19ffa1673263399d99775acad751a73.

--- src/compilers/stylus.coffee
+++ src/compilers/stylus.coffee
@@ -15,16 +15,13 @@

   compile: (files, callback) ->
     mainFilePath = @getAppPath "src/app/styles/main.styl"
-    imagePath = @getAppPath (@options.stylus?.imagePath or "build/web/images")

     fs.readFile mainFilePath, "utf8", (error, data) =>
       return @logError error if error?
       compiler = stylus(data)
         .set("filename", mainFilePath)
         .set("compress", true)
-        .set("paths", [imagePath])
         .set("firebug", @options.stylus?.firebug)
-        .define("image-url", stylus.url({ limit: 0 }))
         .include(@getAppPath "src")

       compiler.use nib if nib


Reverse an un-necessary change

Reverse an un-necessary change

--- services/web/app/coffee/Features/Authentication/AuthenticationController.coffee
+++ services/web/app/coffee/Features/Authentication/AuthenticationController.coffee
@@ -52,7 +52,7 @@
 						logger.err {user_id: user._id}, "error saving regenerated session after login"
 						return callback(err)
 					UserSessionsManager.trackSession(user, req.sessionID, () ->)
-					callback(err)
+					callback(null)

 	passportLogin: (req, res, next) ->
 		# This function is middleware which wraps the passport.authenticate middleware,


:bug: Fix error raised when expanding the number of cursors

--- lib/minimap-selection-view.coffee
+++ lib/minimap-selection-view.coffee
@@ -28,10 +28,9 @@

     {editor} = @minimapView

-    return if editor.getSelections().length is 1 and editor.getLastSelection().isEmpty()
-
     for selection in editor.getSelections()
-      @decorations.push @minimapView.decorateMarker(selection.marker, type: 'highlight-under', scope: '.minimap .minimap-selection .region')
+      unless selection.isEmpty()
+        @decorations.push @minimapView.decorateMarker(selection.marker, type: 'highlight-under', scope: '.minimap .minimap-selection .region')

   removeDecorations: ->
     return if @decorations.length is 0


Generalized message

Generalized message

--- test/core/test-amd.coffee
+++ test/core/test-amd.coffee
@@ -17,4 +17,4 @@
       runner.start()

 catch error
-  alert("Background AMD specs failed: '#{error}'")+  alert("AMD tests failed: '#{error}'")


geometry-test: added missing tests for new functions; TODO: write tests which check the results

--- test/geometry-test.coffee
+++ test/geometry-test.coffee
@@ -19,16 +19,39 @@
       assert.isFunction geom.toString
       assert.equal geom.toString(), "POINT (1.0000000000000000 1.0000000000000000)"

+    # geos unary predicates
+    "should have a isSimple function": (geom) ->
+      assert.isFunction geom.isSimple
+    "should have a isValid function": (geom) ->
+      assert.isFunction geom.isValid
+    "should have a isEmpty function": (geom) ->
+      assert.isFunction geom.isEmpty
+    "should have a isRectangle function": (geom) ->
+      assert.isFunction geom.isRectangle
+
+    # geos binary predicates
+    "should have a disjoint function": (geom) ->
+      assert.isFunction geom.disjoint
+      assert.ok not geom.disjoint geom
+    "should have a touches function": (geom) ->
+      assert.isFunction geom.touches
+      assert.ok not geom.touches geom
+    "should have a intersects function": (geom) ->
+      assert.isFunction geom.intersects
+    "should have a crosses function": (geom) ->
+      assert.isFunction geom.crosses
+    "should have a within function": (geom) ->
+      assert.isFunction geom.within
+    "should have a contains function": (geom) ->
+      assert.isFunction geom.contains
+    "should have a overlaps function": (geom) ->
+      assert.isFunction geom.overlaps
     "should have a equals function": (geom) ->
       assert.isFunction geom.equals
       assert.ok geom.equals geom
-
-    "should have a touches function": (geom) ->
-      assert.isFunction geom.touches
-      assert.ok not geom.touches geom
-
-    "should have a disjoint function": (geom) ->
-      assert.isFunction geom.disjoint
-      assert.ok not geom.disjoint geom
+    "should have a covers function": (geom) ->
+      assert.isFunction geom.covers
+    "should have a coveredBy function": (geom) ->
+      assert.isFunction geom.coveredBy

 tests.export module


fixed uniqueName issue

fixed uniqueName issue

--- src/FileManager/FileManager.coffee
+++ src/FileManager/FileManager.coffee
@@ -45,7 +45,6 @@
         $q.when()

     isRootFolder: (folder) ->
-      console.error "isRootFolder", folder._id, session.getRootFolderId()
       return folder._id == session.getRootFolderId()

     goToFolder: (folder) ->
@@ -120,11 +119,13 @@
       assert.array   parentDirContent, "parentDirContent", "uniqueName"
       s = name.split('.')
       basename  = s[0]
-      extension = '.' + s[1..].join('.')
+      extension = s[1..].join('.')
       i = 0
       while name in (f.metadata.name for f in parentDirContent)
         i += 1
-        name = basename + " (" + i + ")" + extension
+        name = basename + " (" + i + ")"
+        if extension.length
+          name += '.' + extension
       console.info "new name", name
       return name


Fix mobile slide

Fix mobile slide

--- apps/block/client/index.coffee
+++ apps/block/client/index.coffee
@@ -21,7 +21,7 @@
 module.exports.FullBlockView = class FullBlockView extends Backbone.View
   cookieKey: 'sidebar-hidden'
   events:
-    'tap .block-mobile-arrow' : 'scrollDown'
+    'click .block-mobile-arrow' : 'scrollDown'
     'click .block-arrow' : 'clickSlide'
     'click .js-connect-button' : 'loadConnectView'
     'click .js-toggle-info' : 'toggleSidebar'
@@ -90,7 +90,7 @@
     @slide direction

   scrollDown: ->
-    $el = $('.modalize-body') or $('html,body')
+    $el = $('.modalize-body, html, body')
     $el.animate { scrollTop: $(".block-sidebar").offset().top }, 200

   loadConnectView: (e)->


EnvironmentMachineItem: remove pistachio dependency

--- client/Environments/views/scene/environmentmachineitem.coffee
+++ client/Environments/views/scene/environmentmachineitem.coffee
@@ -1,6 +1,5 @@
 class EnvironmentMachineItem extends EnvironmentItem

-  JView.mixin @prototype
   stateClasses = ""
   for state in Object.keys Machine.State
     stateClasses += "#{state.toLowerCase()} "
@@ -15,19 +14,40 @@

     super options, data

+  viewAppended: ->

-    @terminalIcon = new KDCustomHTMLView
-      tagName     : "span"
-      cssClass    : "terminal"
-      click       : @bound "openTerminal"
+    { label, provider, uid, status } = machine = @getData()
+    { computeController } = KD.singletons

-    @progress = new KDProgressBarView
+    @addSubView new KDCustomHTMLView
+      partial : ""
+
+    @addSubView @title = new KDCustomHTMLView
+      partial : "
#{label or provider or uid}
"
+
+    @addSubView @ipAddress = new KDCustomHTMLView
+      partial  : @getIpLink()
+
+    @addSubView @state = new KDCustomHTMLView
+      tagName  : "span"
+      cssClass : "state"
+
+    @addSubView @statusToggle = new KodingSwitch
+      cssClass     : "tiny"
+      defaultValue : status.state is Machine.State.Running
+      callback     : (state)=>
+        if state
+          computeController.start machine
+        else
+          computeController.stop machine
+
+    @addSubView @progress = new KDProgressBarView
       cssClass : "progress"

-
-    machine = @getData()
-
-    {computeController} = KD.singletons
+    @addSubView @terminalIcon = new KDCustomHTMLView
+      tagName  : "span"
+      cssClass : "terminal"
+      click    : @bound "openTerminal"

     computeController.on "build-#{machine._id}",   @bound 'invalidateMachine'
     computeController.on "destroy-#{machine._id}", @bound 'invalidateMachine'
@@ -151,32 +171,11 @@
               @data.meta.initScript = Encoder.htmlEncode modal.editor.getValue()

-  pistachio:->

-    {label, provider, ipAddress, status:{state} } = @getData()

-    title = label or provider

-    publicUrl = ""
-
-    { NotInitialized, Terminated } = Machine.State
-
-    if state not in [ NotInitialized, Terminated ] and ipAddress?
-
-      publicUrl = """

           #{ipAddress}

       """

-    """
-

-
-
#{title}

-        #{publicUrl}
-        {span.state{#(status.state)}}
-        {{> @progress}}
-        {{> @terminalIcon}}
-        {{> @chevron}}
-

-    """


labels: make it plural

labels: make it plural

--- client/app/lib/providers/environmentsmachinestatemodal.coffee
+++ client/app/lib/providers/environmentsmachinestatemodal.coffee
@@ -309,7 +309,7 @@

         if destroyVMs

-          @showBusy "Deleting your VM..."
+          @showBusy "Deleting your VM(s)..."
           ComputeHelpers.destroyExistingMachines (err)=>
             kd.utils.wait 5000, =>
               @buildExpiredView subscription, "downgrade"


teams: selectively show waitlist page or the creation page

by checking the invitation token

--- client/landing/site.landing/coffee/teams/AppController.coffee
+++ client/landing/site.landing/coffee/teams/AppController.coffee
@@ -1,4 +1,5 @@
-TeamsView = require './AppView'
+TeamsView         = require './AppView'
+TeamsWaitListView = require './teamswaitlistview'

 module.exports = class TeamsAppController extends KDViewController

@@ -6,15 +7,8 @@

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

-    options.view = new TeamsView { cssClass: 'content-page' }
+    if KD.utils.getTeamData().invitation?.teamAccessCode
+    then options.view = new TeamsView { cssClass: 'content-page' }
+    else options.view = new TeamsWaitListView { cssClass: 'content-page teams' }

-    super options, data
-
-
-  handleQuery: (query) ->
-
-    return  if not query or not query.group
-
-    { input } = @getView().form.companyName
-    input.setValue query.group.capitalize()
-    @getView().form.companyName.inputReceivedKeyup()
+    super options, data


added neq, def, and ndef (closes #31)

added neq, def, and ndef (closes #31)

--- src/lib/parser/mist-parser.coffee
+++ src/lib/parser/mist-parser.coffee
@@ -30,6 +30,9 @@
 ###
 ConditionalOperators =
   eq: (a, b)-> a is b
+  neq: (a,b)-> a isnt b
+  def: (a)-> a?
+  ndef: (a)-> not a?
 opreg = /^if(@@@@@@)\s*\(\s*((?:(?:'[^']*')|(?:"[^"]*")|(?:[^,\)]+))(?:,(?:(?:'[^']*')|(?:"[^"]*")|(?:[^,\)]+)))*)\s*\)\s*$/
 opreg = new RegExp opreg.source.replace '@@@@@@',
   Object.keys(ConditionalOperators).join '|'
@@ -42,7 +45,7 @@
   arglist.replace /(?:(?:'([^']*)')|(?:"([^"]*)")|([^,\)]+))/g,
     (m, str, strd, env)->
       str = str || strd
-      if env then return args.push vars[env] || ''
+      if env then return args.push vars[env]
       if str then return args.push str
   return operator.apply null, args
 ConditionalProcessor = (src, vars = {})->


a smarter language selection

a smarter language selection

--- library/exposure/localized.coffee
+++ library/exposure/localized.coffee
@@ -67,7 +67,9 @@
         @constructor.compiledTranslations ?= compiler()
         assert cache = @constructor.compiledTranslations
         assert session = @session, "no session detected"
-        selector = language or @session.language or null
+        negotiator = new require("negotiator") @request
+        neg = negotiator.language(_.keys(cache) or [])
+        selector = language or @session.language or neg
         selector = selector or "en" # hardcoded default
         assert messages = cache[selector] or new Object
         amount = _.keys(messages).length # of messages


use `en` as the underlying, fallback translation table

--- library/exposure/localized.coffee
+++ library/exposure/localized.coffee
@@ -132,7 +132,9 @@
         assert identify = try @constructor.identify()
         logger.debug banner, amount, identify, selector
         assert _.isFunction(callback), "invalid callback"
-        return callback selector, cache[selector] or {}
+        assert selected = cache[selector] or new Object
+        assert fallback = _.clone cache.en or Object()
+        callback selector, _.merge fallback, selected

     # Walk over all of the declared translation message locations
     # and try loading messages off the every declared location. All


Minor changes in the phrasing

Minor changes in the phrasing

--- library/membrane/preflight.coffee
+++ library/membrane/preflight.coffee
@@ -44,7 +44,7 @@
 # basis that does some further environment initialization and set
 # up. These preparations will be nececessary no matter what sort of
 # visual functionality you are going to implement. Currently the
-# preflight provides the Bower support and consumes some libraries.
+# primary purpose of preflight is implementation of Bower support.
 module.exports.Preflight = class Preflight extends Screenplay

     # This is a marker that indicates to some internal subsystems


Social: add check for statistic type and parametrize it

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -60,6 +60,10 @@
       channelApiChannelsResFunc 'fetchChannels', options, callback

   fetchPopularTopics = (options, callback)->
+    # here not to break current frontend
+    options.type ?= "weekly"
+    unless options.type in ["daily", "weekly", "monthly"]
+      return callback {message: "type is not valid "}
     getCurrentGroup (group)->
       options.groupName = group.slug
       channelApiChannelsResFunc 'fetchPopularTopics', options, callback


PrivateMessage: remove cycleChannel call from socialapicontroller

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -234,14 +234,6 @@

         # notify listener
         socialapi.emit "ChannelRegistered-#{channelName}", socialApiChannel
-
-
-  cycleChannel: (channel, callback)->
-    options =
-      groupSlug     : channel.groupName
-      apiChannelType: channel.typeConstant
-      apiChannelName: channel.name
-    KD.remote.api.SocialChannel.cycleChannel options, callback

   generateChannelName = ({name, typeConstant, groupName}) ->
     return "socialapi.#{groupName}-#{typeConstant}-#{name}"


Comment: remove includes of input/edit forms.

--- client/Social/Activity/includes.coffee
+++ client/Social/Activity/includes.coffee
@@ -82,8 +82,6 @@
   "views/comments/inputview.coffee"
   "views/comments/inputwidget.coffee"
   "views/comments/inputeditwidget.coffee"
-  "views/comments/inputform.coffee"
-  "views/comments/editform.coffee"
   "views/comments/count.coffee"
   "views/comments/likeview.coffee"
   "views/comments/listpreviouslink.coffee"


Activity: fixed broken build

Activity: fixed broken build

--- client/Social/Activity/includes.coffee
+++ client/Social/Activity/includes.coffee
@@ -78,8 +78,8 @@

   # Activity content displays commons
   "ContentDisplays/ContentDisplayAuthorAvatar.coffee"
-  "ContentDisplays/ContentDisplayMeta.coffee"
-  "ContentDisplays/ContentDisplayTags.coffee"
+  # "ContentDisplays/ContentDisplayMeta.coffee"
+  # "ContentDisplays/ContentDisplayTags.coffee"
   "ContentDisplays/ContentDisplayComments.coffee"

   # Activity List Items


Fixed navigating the sidebar with keyboard shortcuts when the user has folder without visible feeds.

--- app/assets/javascripts/ng-services/ng-HighlightedSidebarLinkSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-HighlightedSidebarLinkSvc.js.coffee
@@ -65,7 +65,7 @@
       for folder in ordered_folders
         folder_feeds = findSvc.find_folder_feeds folder
         # The "All subscriptions" link for a folder is visible only if there is more than 1 feed in it
-        links.push {id: folder.id, type: FOLDER} if folder_feeds.length > 1
+        links.push {id: folder.id, type: FOLDER} if folder_feeds?.length > 1
         ordered_feeds = $filter('orderBy') folder_feeds, 'title'
         if ordered_feeds && ordered_feeds?.length > 0
           for feed in ordered_feeds


Don't put download link in href, Fixes #505

--- app/assets/javascripts/oxalis/view/action-bar/dataset_actions_view.coffee
+++ app/assets/javascripts/oxalis/view/action-bar/dataset_actions_view.coffee
@@ -20,7 +20,7 @@
         <a href="/annotations/<%= tracingType %>/<%= tracingId %>/finishAndRedirect" class="btn btn-default" id="trace-finish-button">Finish
       <% } %>
       <% if(tracing.restrictions.allowDownload || ! tracing.downloadUrl) { %>
-        <a href="<%= tracing.downloadUrl %>" class="btn btn-default" id="trace-download-button">Download
+        Download
       <% } %>
       Help

@@ -92,7 +92,7 @@

     evt.preventDefault()
     @saveTracing()
-    window.location.href = evt.currentTarget.href
+    window.location.href = @model.tracing.downloadUrl

   saveTracing : (evt) ->


Re-arranging topics.

Re-arranging topics.

--- test/test_cyclomatic_complexity.coffee
+++ test/test_cyclomatic_complexity.coffee
@@ -20,28 +20,32 @@

     'An empty function' :

-        topic : getComplexity "x = () -> 1234"
+        topic : "x = () -> 1234"

-        'has a complexity of one' : (complexity) ->
+        'has a complexity of one' : (source) ->
+            complexity = getComplexity(source)
             assert.equal(complexity, 1)

     'If statement' :

-        topic : getComplexity "x = () -> 2 if $ == true"
+        topic : "x = () -> 2 if $ == true"

-        'has a complexity of two' : (complexity) ->
+        'has a complexity of two' : (source) ->
+            complexity = getComplexity(source)
             assert.equal(complexity, 2)
+

     'If Else statement' :

-        topic : getComplexity 'y = -> if $ then 1 else 3'
+        topic : 'y = -> if $ then 1 else 3'

-        'has a complexity of two' : (complexity) ->
+        'has a complexity of two' : (source) ->
+            complexity = getComplexity(source)
             assert.equal(complexity, 2)

     'If ElseIf statement' :

-        topic : getComplexity """
+        topic : """
             x = ->
               if 1233
                 'abc'
@@ -49,13 +53,13 @@
                 'xyz'
             """

-        'has a complexity of three' : (complexity) ->
+        'has a complexity of three' : (source) ->
+            complexity = getComplexity(source)
             assert.equal(complexity, 3)

     'If If-Else Else statement' :

-        topic : getComplexity(
-            """
+        topic : """
             z = () ->
               if x
                 1
@@ -63,41 +67,44 @@
                 2
               else
                 3
-            """)
+            """

-        'has a complexity of three' : (complexity) ->
+        'has a complexity of three' : (source) ->
+            complexity = getComplexity(source)
             assert.equal(complexity, 3)

     'Nested if statements' :

-        topic : getComplexity(
-            """
+        topic : """
             z = () ->
               if abc?
                 if other?
                   123
-            """)
+            """

-        'has a complexity of three' : (complexity) ->
+        'has a complexity of three' : (source) ->
+            complexity = getComplexity(source)
             assert.equal(complexity, 3)
+

     'A while loop' :

-        topic : getComplexity(
-            """
+        topic : """
             x = () ->
               while 1
                 'asdf'
-            """)
+            """

-        'increments complexity' : (complexity) ->
+        'increments complexity' : (source) ->
+            complexity = getComplexity(source)
             assert.equal(complexity, 2)

     'An until loop' :

-        topic : getComplexity "x = () -> log 'a' until $?"
+        topic : "x = () -> log 'a' until $?"

-        'increments complexity' : (complexity) ->
+        'increments complexity' : (source) ->
+            complexity = getComplexity(source)
             assert.equal(complexity, 2)

 }).export(module)


Fix threshold plot inspection brokenness.

--- client/src/scripts/scoring-view.coffee
+++ client/src/scripts/scoring-view.coffee
@@ -130,7 +130,7 @@
 aucCategoryMap = indexBy aucCategories, (cateogory) -> cateogory.key
 aucVariableMap = indexBy aucVariables, (variable) -> variable.key

-createThresholdPlotInspection = (series) ->
+createThresholdPlotInspection = (series, mark) ->
   [ div, h1, h2, table, grid, tbody, tr, th, td ] = geyser.generate words 'div h1 h2 table.table.table-condensed table.table.table-bordered tbody tr th td'

   formatConfusionMatrix = (domain, cm) ->
@@ -1006,7 +1006,7 @@
         .attr 'cx', (d) -> scaleX d[attrX]
         .attr 'cy', (d) -> scaleY d[attrY]
         .on 'click', (d) ->
-          _.inspect createThresholdPlotInspection series
+          _.inspect createThresholdPlotInspection series, d
         .on 'mouseover', (d) ->
           d3.select(@).style 'stroke', series.color
         .on 'mouseout', (d) ->


removed plain alert from webview test, was crashing them quite often

--- testApp/app/controllers/webview.coffee
+++ testApp/app/controllers/webview.coffee
@@ -12,7 +12,7 @@
           steroids.logger.log "testing replaceToRoot"
           @testReplaceToRoot()
         when "testAlert"
-          alert "I should not freeze the app!"
+          navigator.notification.alert "I should not freeze the app!"

   @testReplaceToRoot: ->
     rootView = new steroids.views.WebView {
@@ -38,7 +38,7 @@
     steroids.layers.push {
       view: webView
     }, {
-      onSuccess: -> alert "on success!"
+      onSuccess: -> navigator.notification.alert "on success!"
     }

@@ -82,11 +82,11 @@

   @testAddVisibilitychangeEvent: () ->
     changed = () ->
-      alert "visibility of #{window.location.href} changed, document.visibilityState: " + document.visibilityState + ", document.hidden: " + document.hidden
+      navigator.notification.alert "visibility of #{window.location.href} changed, document.visibilityState: " + document.visibilityState + ", document.hidden: " + document.hidden

     document.addEventListener "visibilitychange", changed, true

-    alert "added eventlistner for visibilitychange"
+    navigator.notification.alert "added eventlistner for visibilitychange"

   @testPreload: () ->

@@ -96,8 +96,8 @@

     webView.preload {
     }, {
-      onSuccess: -> alert "preload call success"
-      onFailure: (error) -> alert "failed to preload: " + error.errorDescription
+      onSuccess: -> navigator.notification.alert "preload call success"
+      onFailure: (error) -> navigator.notification.alert "failed to preload: " + error.errorDescription
     }

   @testUnload: () ->
@@ -107,8 +107,8 @@

     preloadedView.unload {
     }, {
-      onSuccess: -> alert "unload call success"
-      onFailure: (error)  -> alert "failed to unload: " + error.errorDescription
+      onSuccess: -> navigator.notification.alert "unload call success"
+      onFailure: (error)  -> navigator.notification.alert "failed to unload: " + error.errorDescription
     }

   @testPreloadThisAndOpen: () ->
@@ -129,7 +129,7 @@
   @testPreloadAndOpen: () ->

     andOpen = () ->
-      alert "It's preloaded, now opening it."
+      navigator.notification.alert "It's preloaded, now opening it."

       webView.location = null   # make sure that it has no location

@@ -146,7 +146,7 @@
     webView.preload {
     }, {
       onSuccess: () -> andOpen()
-      onFailure: () -> alert "preload failed"
+      onFailure: () -> navigator.notification.alert "preload failed"
     }

   @pushFromPreloaded: () ->
@@ -186,7 +186,7 @@

   @testShowParamsWhenNone: () ->
-    alert JSON.stringify(steroids.view.params)
+    navigator.notification.alert JSON.stringify(steroids.view.params)

   @testParamsInFileURL: () ->
     fileURLWebView = new steroids.views.WebView "file://#{steroids.app.absolutePath}/views/webview/params.html"
@@ -265,19 +265,19 @@
     steroids.view.updateKeyboard {
       accessoryBarEnabled:true
     }, {
-      onSuccess: -> alert "keyboardc accesssory enabled"
+      onSuccess: -> navigator.notification.alert "keyboardc accesssory enabled"
     }

   @testDisableKeyboardAccessory: () ->
     steroids.view.updateKeyboard {
       accessoryBarEnabled:false
     }, {
-      onSuccess: -> alert "keyboardy accesssory disabled"
+      onSuccess: -> navigator.notification.alert "keyboardy accesssory disabled"
     }

   @testKeyboardAccessoryWithEmptyParams: () ->
     steroids.view.updateKeyboard null, {
-      onSuccess: -> alert "updateKeyboard called with no parameters (no change)"
+      onSuccess: -> navigator.notification.alert "updateKeyboard called with no parameters (no change)"
     }

   @testPreloadViaArray: () ->
@@ -290,21 +290,21 @@
   #event tests
   @testCreatedEvent: ->
     eventHandler = steroids.view.on 'created', (event) ->
-      alert "created event -> eventName: #{event.name} webview.location: #{event.webview.location}"
-
-    alert "event listener added"
+      navigator.notification.alert "created event -> eventName: #{event.name} webview.location: #{event.webview.location}"
+
+    navigator.notification.alert "event listener added"

   @testPreloadedEvent: ->
     eventHandler = steroids.view.on 'preloaded', (event) ->
-      alert "preloaded event -> eventName: #{event.name} webview.location: #{event.webview.location}"
-
-    alert "event listener added"
+      navigator.notification.alert "preloaded event -> eventName: #{event.name} webview.location: #{event.webview.location}"
+
+    navigator.notification.alert "event listener added"

   @testUnloadedEvent: ->
     eventHandler = steroids.view.on 'unloaded', (event) ->
-      alert "unloaded event -> eventName: #{event.name} webview.location: #{event.webview.location}"
-
-    alert "event listener added"
+      navigator.notification.alert "unloaded event -> eventName: #{event.name} webview.location: #{event.webview.location}"
+
+    navigator.notification.alert "event listener added"

   @testOffAllEvents: ->
@@ -312,4 +312,4 @@
     steroids.view.off 'preloaded'
     steroids.view.off 'unloaded'

-    alert "all event listeners removed"
+    navigator.notification.alert "all event listeners removed"


typo, was using hbox code for vboxes

typo, was using hbox code for vboxes

--- bokehjs/src/coffee/main.coffee
+++ bokehjs/src/coffee/main.coffee
@@ -136,7 +136,7 @@
   Bokeh.DataSlider = require("widget/data_slider")
   Bokeh.server_page = require("server/serverrun").load
   Bokeh.HBox = require("widget/hbox")
-  Bokeh.VBox = require("widget/hbox")
+  Bokeh.VBox = require("widget/vbox")
   Bokeh.VBoxModelForm = require("widget/vboxmodelform")
   Bokeh.TextInput = require("widget/textinput")
   # utils


Adds endpoint for /api/v1/me

Adds endpoint for /api/v1/me

--- node/server.coffee
+++ node/server.coffee
@@ -90,5 +90,7 @@
 gravity.get '/api/v1/artwork/.*/flag', (req, res) ->
   res.send [fabricate 'artwork']

+gravity.get '/api/v1/me', (req, res) ->
+  res.send [fabricate 'user']

 gravity.all '*', (req, res) -> res.send 404, "Not Found."


Build: minor whitespace

Build: minor whitespace

--- Builder.coffee
+++ Builder.coffee
@@ -128,7 +128,7 @@
     return a if a.length < l - 5
     return a[0..l/2 - 5] + '...' + a[a.length - l/2..a.length]

-  compileChanged: (options, initial)->
+  compileChanged: (options, initial) ->

     includesChanged = @readIncludesFile()


google request hostedDomain param uses ALLOWED_DOMAINS if specified, otherwise 'any'

--- routes/index.coffee
+++ routes/index.coffee
@@ -73,12 +73,12 @@
 router.get '/index', (req, res) ->
   res.render 'index',
     title: 'Protected S3 bucket'
-    domain: if process.env.ALLOWED_DOMAINS then process.env.ALLOWED_DOMAINS else 'any'
+    domain: process.env.ALLOWED_DOMAINS or 'any'

 router.get '/auth/google',
   passport.authenticate 'google',
     scope: 'openid email'
-    hostedDomain: process.env.ALLOWED_DOMAINS
+    hostedDomain: process.env.ALLOWED_DOMAINS or 'any'

 router.get '/auth/google/return',
   passport.authenticate 'google',


fix c and cpp commands

fix c and cpp commands

--- lib/linter-clang.coffee
+++ lib/linter-clang.coffee
@@ -11,7 +11,7 @@

   # A string, list, tuple or callable that returns a string, list or tuple,
   # containing the command line (with arguments) used to lint.
-  cmd: 'clang++ -fsyntax-only -fno-caret-diagnostics'
+  cmd: '-fsyntax-only -fno-caret-diagnostics'
   isCpp: false

   executablePath: null
@@ -84,11 +84,11 @@
   constructor: (editor) ->
     super(editor)
     if editor.getGrammar().name == 'C++'
-      @cmd += ' -x c++ -std=c++11 -fcxx-exceptions'
+      @cmd = 'clang++ ' + @cmd + ' -x c++ -std=c++11 -fcxx-exceptions'
       @grammar = '+'
       @isCpp = true
     if editor.getGrammar().name == 'C'
-      @cmd += ' -x c'
+      @cmd = 'clang ' + @cmd + ' -x c -std=c11 -fexceptions'
       @grammar = 'c'

     # @cmd += ' ' + ClangFlags.getClangFlags(editor.getPath()).join ' '


Remove extra rownumber from message

Remove extra rownumber from message

--- lib/linter-julia.coffee
+++ lib/linter-julia.coffee
@@ -9,6 +9,7 @@

 doSomeMagic = (data,textEditor) ->
   filePath = textEditor.getPath()
+  inptext = textEditor.getText()
   linteroutput = [ ]

   lines = data.split("\n")
@@ -16,7 +17,7 @@
     try
       splittedline = line.split(":")
       numbers = splittedline[1].split(" ")
-      row_number = parseInt(numbers[0],10)
+      row_number = parseInt(numbers[0],10) - 1
       severity = (numbers[1])[0]
       if severity.match("I")
         type = "Info"
@@ -24,8 +25,8 @@
         type = "Warning"
       else
         type = "Error"
-      text = splittedline[1] + ":" + splittedline[2]
-      range = [[row_number - 1, 0], [row_number - 1,1]]
+      text = numbers[1] + ":" + splittedline[2]
+      range = [[row_number, 0], [row_number,1]]
       fullmsg = {
         type
         text


move the status bar from the left side to the right

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -109,15 +109,15 @@

   # This method is called when we get the status-bar service
   attachBottom: (statusBar) ->
-    @_statusTiles.push statusBar.addLeftTile
+    @_statusTiles.push statusBar.addRightTile
       item: @_bottomTabFile,
-      priority: -1001
-    @_statusTiles.push statusBar.addLeftTile
+      priority: 1001
+    @_statusTiles.push statusBar.addRightTile
       item: @_bottomTabProject,
-      priority: -1000
-    @_statusTiles.push statusBar.addLeftTile
+      priority: 1000
+    @_statusTiles.push statusBar.addRightTile
       item: @_bottomStatus,
-      priority: -999
+      priority: 999

   # this method is called on package deactivate
   destroy: ->


:art: Dispose markers instead of decorations As markers automatically dispose all decorations

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -6,7 +6,7 @@
   constructor: (@linter) ->
     @showBubble = true # Altered by the config observer in linter-plus
     @_messages = []
-    @_decorations = []
+    @_markers = []
     @_statusTiles = []

     @_bottomTabFile = new BottomTab()
@@ -85,7 +85,7 @@

   # this method is called on package deactivate
   destroy: ->
-    @_removeDecorations()
+    @_removeMarkers()
     @_panelWorkspace.destroy()
     @_removeBubble()
     for statusTile in @_statusTiles
@@ -112,7 +112,7 @@

   _renderPanel: ->
     @_panel.innerHTML = ''
-    @_removeDecorations()
+    @_removeMarkers()
     @_removeBubble()
     if not @_messages.length
       return @setPanelVisibility(false)
@@ -120,11 +120,11 @@
     @_messages.forEach (message) =>
       if @_scope is 'file' then return unless message.currentFile
       if message.currentFile and message.range #Add the decorations to the current TextEditor
-        marker = @linter.activeEditor.markBufferRange message.range, {invalidate: 'never'}
-        @_decorations.push @linter.activeEditor.decorateMarker(
+        @_markers.push marker = @linter.activeEditor.markBufferRange message.range, {invalidate: 'never'}
+        @linter.activeEditor.decorateMarker(
           marker, type: 'line-number', class: "linter-highlight #{message.class}"
         )
-        @_decorations.push @linter.activeEditor.decorateMarker(
+        @linter.activeEditor.decorateMarker(
           marker, type: 'highlight', class: "linter-highlight #{message.class}"
         )
       Element = Message.fromMessage(message, @_scope is 'project')
@@ -132,11 +132,11 @@
     @updateBubble()

-  _removeDecorations: ->
-    return unless @_decorations.length
-    @_decorations.forEach (decoration) ->
-      try decoration.destroy()
-    @_decorations = []
+  _removeMarkers: ->
+    return unless @_markers.length
+    for marker in @_markers
+      try marker.destroy()
+    @_markers = []

   # This method is called in render, and classifies the messages according to scope
   _extractMessages: (Gen, counts) ->


PrivateMessage: Add settings button to Private message list item

--- client/Social/Activity/views/privatemessage/privatemessagelistitem.coffee
+++ client/Social/Activity/views/privatemessage/privatemessagelistitem.coffee
@@ -45,6 +45,7 @@
   pistachio: ->
     """

+      {{> @settingsButton}}
       {{> @avatar}}

         {{> @author}}{{> @timeAgoView}}{{> @likeView}}


new angle and scale work much more predictably

--- src/art/engine/core/element.coffee
+++ src/art/engine/core/element.coffee
@@ -226,6 +226,15 @@
         preprocess: (v, oldValue) -> if v instanceof PointLayoutBase then v else new PointLayout v, oldValue
         postSetter: -> @_locationLayoutDisabled = false

+      scale:
+        default: 1
+        preprocess: (s) -> point s
+        postSetter: -> @_locationLayoutDisabled = false
+
+      angle:
+        default: 0
+        postSetter: -> @_locationLayoutDisabled = false
+
       childrenLayout:         default: null,                  validate:   (v) -> v == null || v == "flow" || v == "column" || v == "row"
       childrenGrid:           default: null,                  validate:   (v) -> v == null || isString(v) && v.match /^[ a-zA-Z]+$/
       childrenAlignment:      default: point0,                preprocess: (v) -> point v
@@ -478,15 +487,6 @@
       key:
         setter: (v) -> @setName v
         getter: (o) -> o._name
-
-      scale:
-        preprocess: (s) -> point s
-        getter: (o) -> o._elementToParentMatrix.getS()
-        setter: (s) -> @_setElementToParentMatrixWithoutChangingLocation @getPendingElementToParentMatrix().withScale s; s
-
-      angle:
-        getter: (o) -> o._elementToParentMatrix.getAngle()
-        setter: (a) -> @_setElementToParentMatrixWithoutChangingLocation @getPendingElementToParentMatrix().withAngle a; a

       invisible:
         getter: (o) -> !o._visible
@@ -1121,23 +1121,44 @@

     _setLocationFromLayoutXY: (x, y) ->
       return if @_locationLayoutDisabled
-      e2p = @getPendingElementToParentMatrix()
-      {tx, ty} = e2p
-
-      newTx = tx + x - @getPendingCurrentLocationX()
-      newTy = ty + y - @getPendingCurrentLocationY()
-      # log _setLocationFromLayout:
-      #   pendingCurrentLocation: @pendingCurrentLocation
-      #   x:x
-      #   y:y
-      #   tx:tx
-      #   ty:ty
-      #   newTx: newTx
-      #   newTy: newTy
-
-      if tx != newTx || ty != newTy
-        @_pendingState._elementToParentMatrix = e2p.withLocationXY newTx, newTy
+
+      angle = @getPendingAngle()
+      size  = @getPendingCurrentSize()
+      scale = @getPendingScale()
+      axis  = @getPendingAxis()
+      padding = @getPendingCurrentPadding()
+
+      x += padding.left
+      y += padding.top
+
+      asx = axis.x * size.x
+      asy = axis.y * size.y
+
+      e2p = identityMatrix
+      shouldScale = !scale.eq point1
+      shouldRotate = !floatEq(angle)
+      if shouldScale || shouldRotate
+        e2p = e2p.translate -asx, -asy
+        e2p = e2p.scale scale if shouldScale
+        e2p = e2p.rotate angle if shouldRotate
+        e2p = e2p.translate x, y
+      else
+        e2p = e2p.translate x - asx, y - asy
+
+      if !@_pendingState._elementToParentMatrix.eq e2p
+        @_pendingState._elementToParentMatrix = e2p
         @_elementChanged()
+
+      # e2p = @getPendingElementToParentMatrix()
+      # {tx, ty} = e2p
+
+      # newTx = tx + x - @getPendingCurrentLocationX()
+      # newTy = ty + y - @getPendingCurrentLocationY()
+
+      # if tx != newTx || ty != newTy
+      #   @_pendingState._elementToParentMatrix = e2p.withLocationXY newTx, newTy
+      #   @_elementChanged()
+
       @

     _sizeDirectlyEffectsDrawing: ->


Refactored after comments.

Refactored after comments.

--- client/landing/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Groups.kdapplication/AppController.coffee
@@ -397,7 +397,7 @@

   showGroupSubmissionView:->

-    verifySlug = ()->
+    verifySlug = ->
       slugInput = modal.modalTabs.forms["General Settings"].inputs.Slug
       KD.remote.api.JName.one
         name: slugInput.getValue()
@@ -407,11 +407,13 @@
         else
           slugInput.unsetClass 'slug-taken'

-    makeSlug = ()=>
+    makeSlug = =>
       titleInput = modal.modalTabs.forms["General Settings"].inputs.Title
       slugInput = modal.modalTabs.forms["General Settings"].inputs.Slug
       KD.remote.api.JGroup.suggestUniqueSlug titleInput.getValue(), (err, newSlug)->
-        slugInput.setValue newSlug
+        if err then slugInput.setValue ''
+        else
+          slugInput.setValue newSlug

     getGroupType = ->
       modal.modalTabs.forms["Select group type"].inputs.type.getValue()
@@ -558,8 +560,9 @@
               #   defaultValue         : no

     modal = new KDModalViewWithForms modalOptions
-    modal.modalTabs.forms["General Settings"].on "FormValidationFailed", ->
-      modal.modalTabs.forms["General Settings"].buttons.Save.hideLoader()
+    form = modal.modalTabs.forms["General Settings"]
+    form.on "FormValidationFailed", ->
+      form.buttons.Save.hideLoader()

   handleError =(err, buttons)->
     unless buttons