TEST: added failing Comprehension test exposing a serious bug

--- test/tests/CaffeineScript/Parser/Working/Comprehensions.coffee
+++ test/tests/CaffeineScript/Parser/Working/Comprehensions.coffee
@@ -6,173 +6,175 @@

 module.exports = suite: parseTestSuite

-    iterationTypes:
-      "array b":        "Caf.e(b, [], (v, k, into) => {into.push(v);});"
-      "object b":       "Caf.e(b, {}, (v, k, into) => {into[k] = v;});"
-      "each b":         "Caf.e(b, undefined, (v, k, into) => {v;});"
-      "find b":         "Caf.ee(b, undefined, (v, k, into, brk) => {return v && (brk(), v);});"
+  iterationTypes:
+    "array b":        "Caf.e(b, [], (v, k, into) => {into.push(v);});"
+    "object b":       "Caf.e(b, {}, (v, k, into) => {into[k] = v;});"
+    "each b":         "Caf.e(b, undefined, (v, k, into) => {v;});"
+    "find b":         "Caf.ee(b, undefined, (v, k, into, brk) => {return v && (brk(), v);});"

-    multipleArgs:
-      """
-      object myV, myK from mySource
-        myV
-      """: "Caf.e(mySource, {}, (myV, myK, into) => {into[myK] = myV;});"
+  multipleArgs:
+    """
+    object myV, myK from mySource
+      myV
+    """: "Caf.e(mySource, {}, (myV, myK, into) => {into[myK] = myV;});"
+
+    """
+    direction = array v, i in precidence
+      i
+    """: "let direction; direction = Caf.e(precidence, [], (v, i, into) => {into.push(i);});"
+
+  syntaxVariants:
+    implicitWith:
+      "object b":         "Caf.e(b, {}, (v, k, into) => {into[k] = v;});"
+      "object from b":    "Caf.e(b, {}, (v, k, into) => {into[k] = v;});"
+      "object v from b":  "Caf.e(b, {}, (v, k, into) => {into[k] = v;});"
+
+    with:
+      "object v from mySource\n  v":    "Caf.e(mySource, {}, (v, k, into) => {into[k] = v;});"
+      "object v from mySource with v":  "Caf.e(mySource, {}, (v, k, into) => {into[k] = v;});"
+
+      "object mySource\n true":         "Caf.e(mySource, {}, (v, k, into) => {into[k] = true;});"
+      "object mySource with true":      "Caf.e(mySource, {}, (v, k, into) => {into[k] = true;});"
+
+
+    when:
+      "object a from b when a":        "Caf.e(b, {}, (a, k, into) => {if (a) {into[k] = a;};});"
+      "object a from b when a with a": "Caf.e(b, {}, (a, k, into) => {if (a) {into[k] = a;};});"

       """
-      direction = array v, i in precidence
-        i
-      """: "let direction; direction = Caf.e(precidence, [], (v, i, into) => {into.push(i);});"
+      object a from b when a
+        a
+      """: "Caf.e(b, {}, (a, k, into) => {if (a) {into[k] = a;};});"

-    syntaxVariants:
-      implicitWith:
-        "object b":         "Caf.e(b, {}, (v, k, into) => {into[k] = v;});"
-        "object from b":    "Caf.e(b, {}, (v, k, into) => {into[k] = v;});"
-        "object v from b":  "Caf.e(b, {}, (v, k, into) => {into[k] = v;});"
+  multilineWith:
+    """
+    array v in a
+      foo
+      v
+    """:
+      "Caf.e(a, [],
+      (v, k, into) => {foo;
+      into.push(v);});"

-      with:
-        "object v from mySource\n  v":    "Caf.e(mySource, {}, (v, k, into) => {into[k] = v;});"
-        "object v from mySource with v":  "Caf.e(mySource, {}, (v, k, into) => {into[k] = v;});"
+    """
+    array v in a
+      foo
+      bar
+      v
+    """:
+      "Caf.e(a, [],
+      (v, k, into) => {foo; bar;
+      into.push(v);});"

-        "object mySource\n true":         "Caf.e(mySource, {}, (v, k, into) => {into[k] = true;});"
-        "object mySource with true":      "Caf.e(mySource, {}, (v, k, into) => {into[k] = true;});"
+    """
+    find a
+      b
+      c
+    """: "
+      Caf.ee(a, undefined, (v, k, into, brk) =>
+      {let cafRet; b; return (cafRet = c) && (brk(), cafRet);});"

+  regressions:
+    """
+    object obj
+      # an intended comment
+    """: "Caf.e(obj, {}, (v, k, into) => {into[k] = v;});"

-      when:
-        "object a from b when a":        "Caf.e(b, {}, (a, k, into) => {if (a) {into[k] = a;};});"
-        "object a from b when a with a": "Caf.e(b, {}, (a, k, into) => {if (a) {into[k] = a;};});"
+    """
+    object obj
+    """: "Caf.e(obj, {}, (v, k, into) => {into[k] = v;});"
+
+    "object k from b": "Caf.e(b, {}, (k, _k, into) => {into[_k] = k;});"
+
+  nested:
+    """
+    array a
+      array b
+    """: "Caf.e(a,
+      [],
+      (v, k, into) =>
+      {into.push(Caf.e(b,
+      [],
+      (v, k, into) =>
+      {into.push(v);}));});"
+
+  scope:
+    enclosingScope:
+      "assigning to existing variable":

         """
-        object a from b when a
-          a
-        """: "Caf.e(b, {}, (a, k, into) => {if (a) {into[k] = a;};});"
+        a = 10
+        object a from b with a
+        """: "let a; a = 10; Caf.e(b, {}, (a, k, into) => {into[k] = a;});"

-    multilineWith:
-      """
-      array v in a
-        foo
-        v
-      """:
-        "Caf.e(a, [],
-        (v, k, into) => {foo;
-        into.push(v);});"
+    assigningToLoopVariables:
+      "in when using default with":

-      """
-      array v in a
-        foo
-        bar
-        v
-      """:
-        "Caf.e(a, [],
-        (v, k, into) => {foo; bar;
-        into.push(v);});"
+        """
+        object a from b when a = a.foo
+        """: "Caf.e(b, {}, (a, k, into) => {if (a = a.foo) {into[k] = a;};});"

-      """
-      find a
-        b
-        c
-      """: "
-        Caf.ee(a, undefined, (v, k, into, brk) =>
-        {let cafRet; b; return (cafRet = c) && (brk(), cafRet);});"
+      "in when using explicit with":

-    regressions:
-      """
-      object obj
-        # an intended comment
-      """: "Caf.e(obj, {}, (v, k, into) => {into[k] = v;});"
+        """
+        object a from b when a = a.foo with a + 1
+        """: "Caf.e(b, {}, (a, k, into) => {if (a = a.foo) {into[k] = a + 1;};});"

-      """
-      object obj
-      """: "Caf.e(obj, {}, (v, k, into) => {into[k] = v;});"
+    definingNewVariables:
+      "in with":
+        """
+        object a from b with foo = a
+        """: "Caf.e(b, {}, (a, k, into) => {let foo; into[k] = foo = a;});"

-    nested:
-      """
-      array a
-        array b
-      """: "Caf.e(a,
-        [],
-        (v, k, into) =>
-        {into.push(Caf.e(b,
-        [],
-        (v, k, into) =>
-        {into.push(v);}));});"
+      "in when":
+        """
+        object a from b when foo = a
+        """: "Caf.e(b, {}, (a, k, into) => {let foo; if (foo = a) {into[k] = a;};});"

-    scope:
-      enclosingScope:
-        "assigning to existing variable":
+      "in both":
+        """
+        object a from b when foo = a with foo = foo + 1
+        """: "Caf.e(b, {}, (a, k, into) => {let foo; if (foo = a) {into[k] = foo = foo + 1;};});"

-          """
-          a = 10
-          object a from b with a
-          """: "let a; a = 10; Caf.e(b, {}, (a, k, into) => {into[k] = a;});"
+    usingExternalVariables:
+      "in with":
+        """
+        foo = 1
+        object a from b with foo = a
+        """: "let foo; foo = 1; Caf.e(b, {}, (a, k, into) => {into[k] = foo = a;});"

-      assigningToLoopVariables:
-        "in when using default with":
+      "in when":
+        """
+        foo = 1
+        object a from b when foo = a
+        """: "let foo; foo = 1; Caf.e(b, {}, (a, k, into) => {if (foo = a) {into[k] = a;};});"

-          """
-          object a from b when a = a.foo
-          """: "Caf.e(b, {}, (a, k, into) => {if (a = a.foo) {into[k] = a;};});"
+      "in both":
+        """
+        foo = 1
+        object a from b when foo = a with foo = foo + 1
+        """: "let foo; foo = 1; Caf.e(b, {}, (a, k, into) => {if (foo = a) {into[k] = foo = foo + 1;};});"

-        "in when using explicit with":
+  into:
+    "object a into b": "Caf.e(a, b, (v, k, into) => {into[k] = v;});"

-          """
-          object a from b when a = a.foo with a + 1
-          """: "Caf.e(b, {}, (a, k, into) => {if (a = a.foo) {into[k] = a + 1;};});"
+  find:
+    "find a from b when a > 10": "Caf.ee(b, undefined, (a, k, into, brk) => {return a > 10 && (brk(), a);});"
+    "find a from b with a > 10": "Caf.ee(b, undefined, (a, k, into, brk) => {let cafRet; return (cafRet = a > 10) && (brk(), cafRet);});"
+    "find a from b when a > 10 with 123": "Caf.ee(b, undefined, (a, k, into, brk) => {return a > 10 && (brk(), 123);});"

-      definingNewVariables:
-        "in with":
-          """
-          object a from b with foo = a
-          """: "Caf.e(b, {}, (a, k, into) => {let foo; into[k] = foo = a;});"
+  alternativeKeywords:
+    returning:
+      "each a in b into      out = [1] with pushUnique out, a": "let out; Caf.e(b, out = [1], (a, k, into) => {pushUnique(out, a);});"
+      "each a in b returning out = [1] with pushUnique out, a": "let out; Caf.e(b, out = [1], (a, k, into) => {pushUnique(out, a);});"
+    in:
+      "find a from b when a > 10":  "Caf.ee(b, undefined, (a, k, into, brk) => {return a > 10 && (brk(), a);});"
+      "find a in   b when a > 10":  "Caf.ee(b, undefined, (a, k, into, brk) => {return a > 10 && (brk(), a);});"
+    do:
+      "object a from b with a + 1": "Caf.e(b, {}, (a, k, into) => {into[k] = a + 1;});"
+      "object a from b do   a + 1": "Caf.e(b, {}, (a, k, into) => {into[k] = a + 1;});"

-        "in when":
-          """
-          object a from b when foo = a
-          """: "Caf.e(b, {}, (a, k, into) => {let foo; if (foo = a) {into[k] = a;};});"
-
-        "in both":
-          """
-          object a from b when foo = a with foo = foo + 1
-          """: "Caf.e(b, {}, (a, k, into) => {let foo; if (foo = a) {into[k] = foo = foo + 1;};});"
-
-      usingExternalVariables:
-        "in with":
-          """
-          foo = 1
-          object a from b with foo = a
-          """: "let foo; foo = 1; Caf.e(b, {}, (a, k, into) => {into[k] = foo = a;});"
-
-        "in when":
-          """
-          foo = 1
-          object a from b when foo = a
-          """: "let foo; foo = 1; Caf.e(b, {}, (a, k, into) => {if (foo = a) {into[k] = a;};});"
-
-        "in both":
-          """
-          foo = 1
-          object a from b when foo = a with foo = foo + 1
-          """: "let foo; foo = 1; Caf.e(b, {}, (a, k, into) => {if (foo = a) {into[k] = foo = foo + 1;};});"
-
-    into:
-      "object a into b": "Caf.e(a, b, (v, k, into) => {into[k] = v;});"
-
-    find:
-      "find a from b when a > 10": "Caf.ee(b, undefined, (a, k, into, brk) => {return a > 10 && (brk(), a);});"
-      "find a from b with a > 10": "Caf.ee(b, undefined, (a, k, into, brk) => {let cafRet; return (cafRet = a > 10) && (brk(), cafRet);});"
-      "find a from b when a > 10 with 123": "Caf.ee(b, undefined, (a, k, into, brk) => {return a > 10 && (brk(), 123);});"
-
-    alternativeKeywords:
-      returning:
-        "each a in b into      out = [1] with pushUnique out, a": "let out; Caf.e(b, out = [1], (a, k, into) => {pushUnique(out, a);});"
-        "each a in b returning out = [1] with pushUnique out, a": "let out; Caf.e(b, out = [1], (a, k, into) => {pushUnique(out, a);});"
-      in:
-        "find a from b when a > 10":  "Caf.ee(b, undefined, (a, k, into, brk) => {return a > 10 && (brk(), a);});"
-        "find a in   b when a > 10":  "Caf.ee(b, undefined, (a, k, into, brk) => {return a > 10 && (brk(), a);});"
-      do:
-        "object a from b with a + 1": "Caf.e(b, {}, (a, k, into) => {into[k] = a + 1;});"
-        "object a from b do   a + 1": "Caf.e(b, {}, (a, k, into) => {into[k] = a + 1;});"
-
-    expression: "c = array b": "let c; c = Caf.e(b, [], (v, k, into) => {into.push(v);});"
+  expression: "c = array b": "let c; c = Caf.e(b, [], (v, k, into) => {into.push(v);});"

   ###
   # TODO


Comment typo

Comment typo

--- src/chaplin/views/collection_view.coffee
+++ src/chaplin/views/collection_view.coffee
@@ -191,7 +191,7 @@

     showHideLoadingIndicator: ->
       # Only show the loading indicator if the collection is empty.
-      # Otherwise loading more items in order to appendthem would
+      # Otherwise loading more items in order to append them would
       # show the loading indicator. If you want the indicator to
       # show up in this case, you need to overwrite this method to
       # disable the check.


issue 156: latex editor can hang.  Fix is to stop on first error no matter what.  This is a change in that a file full of errors only shows the first, which is probably good for reducing user suffering, actually.

--- src/smc-webapp/editor.coffee
+++ src/smc-webapp/editor.coffee
@@ -2730,7 +2730,8 @@
                 opts.cb(false, {n:n, x:x, y:y})

     default_tex_command: () =>
-        return "pdflatex -synctex=1 -interact=nonstopmode '#{@filename_tex}'"
+        # errorstopmode recommended by http://tex.stackexchange.com/questions/114805/pdflatex-nonstopmode-with-tikz-stops-compiling
+        return "pdflatex -synctex=1 -interact=errorstopmode '#{@filename_tex}'"

     # runs pdflatex; updates number of pages, latex log, parsed error log
     update_pdf: (opts={}) =>
@@ -2797,8 +2798,9 @@
             command = @default_tex_command()
         sagetex_file = @base_filename + '.sagetex.sage'
         sha_marker = 'sha1sums'
+        # yes x business recommended by http://tex.stackexchange.com/questions/114805/pdflatex-nonstopmode-with-tikz-stops-compiling
         @_exec
-            command : command + "< /dev/null 2</dev/null; echo '#{sha_marker}'; sha1sum '#{sagetex_file}'"
+            command : "yes x | " + command + "; echo '#{sha_marker}'; sha1sum '#{sagetex_file}'"
             bash    : true
             timeout : 20
             err_on_exit : false


make fix william asked for

make fix william asked for

--- src/smc-webapp/projects.cjsx
+++ src/smc-webapp/projects.cjsx
@@ -576,9 +576,10 @@
         v = {}
         @get('project_map').map (project, project_id) =>
             upgrades = @getIn(['project_map', project_id, 'users', account_id, 'upgrades'])?.toJS()
-            for upgrade of upgrades
-                if upgrades[upgrade] > 0
+            for upgrade,val of upgrades
+                if val > 0
                     v[project_id] = upgrades
+                    break
         return v

 init_store =


sagews: implement output hiding - https://github.com/sagemathinc/smc/issues/762

--- src/smc-webapp/sagews.coffee
+++ src/smc-webapp/sagews.coffee
@@ -42,7 +42,7 @@

 class SynchronizedWorksheet extends SynchronizedDocument2
     constructor: (@editor, @opts) ->
-        #window.w = @
+        # window.w = @

         # these two lines are assumed, at least by the history browser
         @codemirror  = @editor.codemirror
@@ -782,10 +782,38 @@
                     cm.replaceRange(uuid, {line:line, ch:1}, {line:line, ch:37})
                 context.uuids[uuid] = true
                 flagstring = x.slice(37, x.length-1)
+
                 if FLAGS.hide_input in flagstring
                     context.hide = line
                 else
                     delete context.hide
+
+                # Record whether or not the output for this cell should be hidden.
+                context.hide_output = FLAGS.hide_output in flagstring
+
+                # Determine the output line, if available, so we can toggle whether or not
+                # the output is hidden.  Note that we are not doing something based on
+                # state change, as that is too hard to reason about, and are just always
+                # setting the line classes properly.  This will have to be re-done someday.
+                n = line + 1
+                output_line = undefined
+                while n < cm.lineCount()
+                    z = cm.getLine(n)?[0]
+                    if z == MARKERS.output
+                        output_line = n
+                        break
+                    if z == MARKERS.input or not z?
+                        break
+                    n += 1
+                if output_line? # found output line -- properly set hide state
+                    if context.hide_output
+                        for t in ['gutter', 'text']
+                            cm.addLineClass(output_line, t, 'hidden')
+                    else
+                        for t in ['gutter', 'text']
+                            cm.removeLineClass(output_line, t, 'hidden')
+
+
                 if marks.length == 1 and (marks[0].type != 'input' or marks[0].uuid != uuid)
                     marks[0].clear()
                     marks = []
@@ -838,7 +866,6 @@
                     uuid = misc.uuid()
                     cm.replaceRange(uuid, {line:line, ch:1}, {line:line, ch:37})
                 context.uuids[uuid] = true
-
                 if marks.length == 1 and (marks[0].type != 'output' or marks[0].uuid != uuid)
                     marks[0].clear()
                     marks = []
@@ -862,9 +889,19 @@
                         output.dblclick () =>
                             # Double click output to toggle input
                             @action(pos:{line:mark.find().from.line-1, ch:0}, toggle_input:true)
+
                 cm.addLineClass(line, 'gutter', 'sagews-output-cm-gutter')
                 cm.addLineClass(line, 'text',   'sagews-output-cm-text')
                 cm.addLineClass(line, 'wrap',   'sagews-output-cm-wrap')
+
+                # To be sure, definitely properly set output state (should already be properly set when rendering input)
+                if context.hide_output
+                    for t in ['gutter', 'text']
+                        cm.addLineClass(line, t, 'hidden')
+                else
+                    for t in ['gutter', 'text']
+                        cm.removeLineClass(line, t, 'hidden')
+
                 @render_output(marks[0], x.slice(38), line)

             else


Rename the images more appropriately.

Rename the images more appropriately.

--- src/supervisor-update.coffee
+++ src/supervisor-update.coffee
@@ -11,15 +11,15 @@
 Promise.promisifyAll(docker.getImage().__proto__)
 Promise.promisifyAll(docker.getContainer().__proto__)

-supervisorTag = 'resin/rpi-supervisor'
-updateImage = config.REGISTRY_ENDPOINT + '/' + supervisorTag
+localImage = 'resin/rpi-supervisor'
+remoteImage = config.REGISTRY_ENDPOINT + '/' + localImage

 supervisorUpdating = Promise.resolve()
 exports.update = ->
 	# Make sure only one attempt to update the full supervisor is running at a time, ignoring any errors from previous update attempts
 	supervisorUpdating = supervisorUpdating.catch(->).then ->
-		console.log('Fetching updated supervisor:', updateImage)
-		docker.createImageAsync(fromImage: updateImage)
+		console.log('Fetching updated supervisor:', remoteImage)
+		docker.createImageAsync(fromImage: remoteImage)
 	.then (stream) ->
 		return new Promise (resolve, reject) ->
 			if stream.headers['content-type'] is 'application/json'
@@ -30,15 +30,15 @@

 			stream.on('end', resolve)
 	.then ->
-		console.log('Tagging updated supervisor:', updateImage)
-		docker.getImage(updateImage).tagAsync(
-			repo: supervisorTag
+		console.log('Tagging updated supervisor:', remoteImage)
+		docker.getImage(remoteImage).tagAsync(
+			repo: localImage
 			force: true
 		)
 	.then ->
-		console.log('Creating updated supervisor container:', supervisorTag)
+		console.log('Creating updated supervisor container:', localImage)
 		docker.createContainerAsync(
-			Image: supervisorTag
+			Image: localImage
 			Cmd: ['/start']
 			Volumes:
 				'/boot/config.json': '/mnt/mmcblk0p1/config.json'
@@ -50,7 +50,7 @@
 			]
 		)
 	.then (container) ->
-		console.log('Starting updated supervisor container:', supervisorTag)
+		console.log('Starting updated supervisor container:', localImage)
 		container.startAsync(
 			Privileged: true
 			Binds: [


removing wdq: removing wdq as a url option from build_url

--- src/utils/build_url.coffee
+++ src/utils/build_url.coffee
@@ -6,7 +6,6 @@
 roots =
   wikidata: 'https://www.wikidata.org/w/api.php'
   commons: 'http://commons.wikimedia.org'
-  wdq: 'http://wdq.wmflabs.org/api'

 module.exports = (domain, queryObj)->
   roots[domain] + '?' + qs.stringify(queryObj)


default page added in getContext

default page added in getContext

--- src/views/bootstrap.coffee
+++ src/views/bootstrap.coffee
@@ -205,7 +205,7 @@
   getContext: (context) ->
     page = @collection.params.page
     context.meta = _.extend({}, @collection.meta.attributes)
-    context.meta.page = page
+    context.meta.page = page or 1
     context.meta.start = @collection.params.page_size * (page - 1) + 1
     context.meta.end = Math.min(
       context.meta.count, @collection.params.page_size * (page))


flip cursor on snow theme

flip cursor on snow theme

--- src/themes/snow/index.coffee
+++ src/themes/snow/index.coffee
@@ -18,8 +18,9 @@
     'multi-cursor':
       template:
        '
+

-
+

         '

@@ -67,7 +68,7 @@
   extendMultiCursor: (module) ->
     @editor.renderer.addStyles(
       '.editor-container .cursor-name':
-        'border-radius': '3px'
+        'border-radius': '4px'
         'font-size': '11px'
         'font-family': 'Arial'
         'margin-left': '-50%'
@@ -75,17 +76,26 @@
       '.editor-container .cursor-triangle':
         'border-left': '4px solid transparent'
         'border-right': '4px solid transparent'
+        'height': '0px'
+        'margin-left': '-3px'
+        'width': '0px'
+      '.editor-container .cursor-triangle.bottom':
         'border-top': '4px solid transparent'
         'display': 'block'
-        'height': '0px'
         'margin-bottom': '-1px'
-        'margin-left': '-3px'
-        'width': '0px'
-      '.editor-container .cursor.top > .cursor-flag': { 'bottom': '100%', 'top': 'auto' }
+      '.editor-container .cursor-triangle.top':
+        'border-bottom': '4px solid transparent'
+        'display': 'none'
+        'margin-top': '-1px'
+      '.editor-container .cursor.top .cursor-triangle.bottom':
+        'display': 'none'
+      '.editor-container .cursor.top .cursor-triangle.top':
+        'display': 'block'
     )
     module.on(module.constructor.events.CURSOR_ADDED, (cursor) ->
-      triangle = cursor.elem.querySelector('.cursor-triangle')
-      triangle.style.borderTopColor = cursor.color
+      bottomTriangle = cursor.elem.querySelector('.cursor-triangle.bottom')
+      topTriangle = cursor.elem.querySelector('.cursor-triangle.top')
+      bottomTriangle.style.borderTopColor = topTriangle.style.borderBottomColor = cursor.color
     )

   extendToolbar: (module) ->


Split commitPreview bindings by platform

--- keymaps/git.cson
+++ keymaps/git.cson
@@ -55,9 +55,13 @@
   'ctrl-enter': 'github:commit'

 '.github-CommitView-commitPreview':
+  'enter': 'native!'
+'.platform-darwin .github-CommitView-commitPreview':
   'cmd-left': 'github:dive'
+'.platform-win32 .github-CommitView-commitPreview':
   'ctrl-left': 'github:dive'
-  'enter': 'native!'
+'.platform-linux .github-CommitView-commitPreview':
+  'ctrl-left': 'github:dive'

 '.github-RecentCommits':
   'left': 'github:dive'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/number_extensions.coffee
+++ test/number_extensions.coffee
@@ -122,7 +122,7 @@
   ]

   4.circularPoints (p, i) ->
-    equals p.x.toFixed(5), points[i].x.toFixed(5), "#{p.x} == #{points[i].x}"
-    equals p.y.toFixed(5), points[i].y.toFixed(5), "#{p.y} == #{points[i].y}"
+    equalEnough p.x.toFixed(5), points[i].x.toFixed(5), "#{p.x} == #{points[i].x}"
+    equalEnough p.y.toFixed(5), points[i].y.toFixed(5), "#{p.y} == #{points[i].y}"

 module undefined


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -37,6 +37,6 @@
     height: 10

   rightEnemy.include(Follow)
-  rightEne
+  rightEnemy.follow(p)

 module()


Fix #63 - use TLS for API calls

Fix #63 - use TLS for API calls

--- src/scripts/services/dapiServices.coffee
+++ src/scripts/services/dapiServices.coffee
@@ -44,7 +44,7 @@
     base = if useLocalServer
       'http://localhost:8080'
     else
-      'http://api.droneshare.com'
+      'https://api.3drobotics.com'
       # Normally api.3drobotics.com is recommended, but if you'd like cookies used for your application's login info
       # you can create a CNAME under your domain that points to api.3drobotics.com and reference the CNAME here.


Give time budget for the large network test

--- spec/Network.coffee
+++ spec/Network.coffee
@@ -295,7 +295,7 @@

   describe 'with a very large network', ->
     it 'should be able to connect without errors', (done) ->
-      @timeout 0
+      @timeout 60000
       g = new noflo.Graph
       g.baseDir = root
       called = 0


Restoring our toggle option

Restoring our toggle option

--- app/assets/javascripts/darkswarm/controllers/authentication_actions_controller.js.coffee
+++ app/assets/javascripts/darkswarm/controllers/authentication_actions_controller.js.coffee
@@ -1,2 +1,5 @@
 Darkswarm.controller "AuthenticationActionsCtrl", ($scope, Navigation, storage, Sidebar) ->
   $scope.Sidebar = Sidebar
+
+  $scope.toggle = (path)->
+    Navigation.navigate(path)


change the way domain & vm binding works

--- client/landing/app/Applications/Environments.kdapplication/views/DomainMapperView.coffee
+++ client/landing/app/Applications/Environments.kdapplication/views/DomainMapperView.coffee
@@ -15,10 +15,12 @@
     @updatePartial ""
     @destroySubViews()

-    KD.remote.api.JVM.fetchVmsWithHostnames (err, vms)=>
+    KD.remote.api.JVM.fetchVmsByContext (err, vms)=>
       if vms

         hostnameAliases = domain.hostnameAlias
+        vmList = []
+        (vmList.push {hostnameAlias:vm} for vm in vms[0])  if vms.length > 0

         @vmListViewController = new VMListViewController
           viewOptions :
@@ -28,7 +30,7 @@
           domain          : domain
           hostnameAliases : if hostnameAliases then (alias for alias in hostnameAliases) else []

-        @vmListViewController.instantiateListItems vms
+        @vmListViewController.instantiateListItems vmList
         @addSubView @vmListViewController.getView()
       else
         @addSubView new KDCustomHTMLView
@@ -40,13 +42,10 @@
     options.cssClass = 'domain-vm-item'
     super options, data

-    listViewData   = @getDelegate().getData()
-    switchStatus   = off
-    for hostnameAlias in @getData().hostnameAlias
-      if hostnameAlias in listViewData.hostnameAliases
-        switchStatus = on
-
-    domainInstance = listViewData.domain
+    {hostnameAlias} = @getData()
+    listViewData    = @getDelegate().getData()
+    switchStatus    = hostnameAlias in listViewData.hostnameAliases
+    domainInstance  = listViewData.domain

     @onOff = new KDOnOffSwitch
       size        : 'small'
@@ -54,14 +53,13 @@
       defaultValue: switchStatus
       callback : (state) =>
         domainInstance.bindVM
-          vmName     : @getData().name
-          state      : state
+          hostnameAlias : hostnameAlias
+          state         : state
         , (err) =>
           unless err
-            {name} = @getData()
             notificationMsg = if state
-            then "Your domain is connected to the #{name} VM."
-            else "Your domain is disconnected from the #{name} VM."
+            then "Your domain is connected to the #{hostnameAlias} VM."
+            else "Your domain is disconnected from the #{hostnameAlias} VM."
             new KDNotificationView
               type     : "mini"
               cssClass : "success"
@@ -81,6 +79,6 @@
   pistachio:->
     """

-    {{ #(name) }}
+    {{ #(hostnameAlias) }}
     {{> @onOff }}
     """


fix some missing parenthesis

fix some missing parenthesis

--- src/ioiojs/scrollbars.coffee
+++ src/ioiojs/scrollbars.coffee
@@ -15,140 +15,148 @@
     @get(0).scrollWidth - @width()

-  $.fn.scrollbars = ->
-    @each (index, item) ->
+  methods =
+    init: (options) ->
+      @each (index, item) ->

-      # get references to actual jQuery objects:
-      #  1] the $container, i.e. the element on which scrollbars() is called and that will scroll.
-      #  2] the $scrollbar, i.e. the long line that will contain the scroller.
-      #  3] the $scroller, i.e. that tiny button that the user will drag around to scroll the $container.
-      $container = $(item)
+        # get references to actual jQuery objects:
+        #  1] the $container, i.e. the element on which scrollbars() is called and that will scroll.
+        #  2] the $scrollbar, i.e. the long line that will contain the scroller.
+        #  3] the $scroller, i.e. that tiny button that the user will drag around to scroll the $container.
+        $container = $(item)

-      # skip this item if scrolling is not necessary.
-      # debug.log(item.scrollWidth, $container.width())
-      return true if item.scrollWidth <= $container.width()
+        # skip this item if scrolling is not necessary.
+        # debug.log(item.scrollWidth, $container.width())
+        return true if item.scrollWidth <= $container.width()

-      # set the class-name of the scroller element.
-      scroller = "scroller"
-      # set the class-name of the scrollbar element.
-      scrollbar = "scrollbar"
+        # set the class-name of the scroller element.
+        scroller = "scroller"
+        # set the class-name of the scrollbar element.
+        scrollbar = "scrollbar"

-      $container.children("#{scrollbar}").remove()
+        $container.children("#{scrollbar}").remove()

-      $scrollbar = $("

").appendTo($container)
-      $scroller = $("

").appendTo($scrollbar)
+        $scrollbar = $("

").appendTo($container)
+        $scroller = $("

").appendTo($scrollbar)

-      $container.addClass("scroll-container")
-      $scrollbar.css("left", $container.scrollLeft())
-      $scrollbar.css("width", $container.width())
+        $container.addClass("scroll-container")
+        $scrollbar.css("left", $container.scrollLeft())
+        $scrollbar.css("width", $container.width())

-      # remove the ugly scrollbars, unless we're on a touchy device to avoid the user loosing the possibility
-      # to scroll with his fingers.
-      $container.css("overflow", "hidden") if false is Modernizr?.touch
+        # remove the ugly scrollbars, unless we're on a touchy device to avoid the user loosing the possibility
+        # to scroll with his fingers.
+        $container.css("overflow", "hidden") if false is Modernizr?.touch

-      # calculate the constraints for dragging the scroller around, to be passed to the jQuery UI draggable.
-      x1 = $container.offset().left
-      y1 = $scroller.offset().top
-      x2 = x1 + $container.width() - $scroller.width()
-      y2 = y1
-      containment = [x1, y1, x2, y2]
+        # calculate the constraints for dragging the scroller around, to be passed to the jQuery UI draggable.
+        x1 = $container.offset().left
+        y1 = $scroller.offset().top
+        x2 = x1 + $container.width() - $scroller.width()
+        y2 = y1
+        containment = [x1, y1, x2, y2]

-      scrollLeft = (scrollerLeft, container, scroller) ->
+        scrollLeft = (scrollerLeft, container, scroller) ->

-        # the maximum left coordinate of the scroller.
-        scrollerMaxLeft = container.width() - scroller.width()
+          # the maximum left coordinate of the scroller.
+          scrollerMaxLeft = container.width() - scroller.width()

-        # the position of the scroller in a scale 0-1.
-        scrollerLeftRatio = (scrollerLeft / scrollerMaxLeft)
+          # the position of the scroller in a scale 0-1.
+          scrollerLeftRatio = (scrollerLeft / scrollerMaxLeft)

-        # the contained width - or the container scrollwidth.
-        # containerScrollWidth =  container.get(0).scrollWidth
+          # the contained width - or the container scrollwidth.
+          # containerScrollWidth =  container.get(0).scrollWidth

-        # the container width.
-        # containerWidth = container.width()
+          # the container width.
+          # containerWidth = container.width()

-        # the maximum scrollLeft value.
-        containerMaxScrollLeft = container.maxScrollLeft()
+          # the maximum scrollLeft value.
+          containerMaxScrollLeft = container.maxScrollLeft()

-        # the scrollleft.
-        (scrollerLeftRatio * containerMaxScrollLeft)
+          # the scrollleft.
+          (scrollerLeftRatio * containerMaxScrollLeft)

-      scrollerLeft = (container, scroller) ->
+        scrollerLeft = (container, scroller) ->

-        # the current scroll-left.
-        scrollLeft = container.scrollLeft()
+          # the current scroll-left.
+          scrollLeft = container.scrollLeft()

-        # the maximum scroll-left.
-        scrollMaxLeft = container.maxScrollLeft()
+          # the maximum scroll-left.
+          scrollMaxLeft = container.maxScrollLeft()

-        # the scroll-left to scroll-max-left ratio.
-        scrollLeftRatio = (scrollLeft / scrollMaxLeft)
+          # the scroll-left to scroll-max-left ratio.
+          scrollLeftRatio = (scrollLeft / scrollMaxLeft)

-        # the maximum left coordinate of the scroller.
-        scrollerMaxLeft = container.width() - scroller.width()
+          # the maximum left coordinate of the scroller.
+          scrollerMaxLeft = container.width() - scroller.width()

-        (scrollerMaxLeft * scrollLeftRatio)
+          (scrollerMaxLeft * scrollLeftRatio)

-      # set-up the draggable behaviour on the $scroller.
-      $scroller.draggable
-        axis : "x"
-        containment : containment
-        # handle the start dragging event, by setting the user is dragging.
-        start : (event, ui) ->
+        # set-up the draggable behaviour on the $scroller.
+        $scroller.draggable
+          axis : "x"
+          containment : containment
+          # handle the start dragging event, by setting the user is dragging.
+          start : (event, ui) ->
+            $target = $(event.target)
+            $target.data( "dragging", true)
+          # handle the stop dragging event, by setting the user is NOT dragging.
+          stop : (event, ui) ->
+            $target = $(event.target)
+            $target.data( "dragging", false)
+
+          # the user is dragging, scroll the $container.
+          drag : (event, ui) ->
+            $scroller = $(event.target)
+            $scrollbar = $scroller.parent()
+            $container = $scrollbar.parent()
+
+            scrollerLeft = (ui.offset.left - $container.offset().left)
+
+            left = scrollLeft(scrollerLeft, $container, $scroller)
+
+            # realign the scrollbar to the visible left border.
+            $scrollbar.css( "left", left ) if "hidden" is $container.css( "overflow" )
+
+            # scroll the container.
+            $container.scrollLeft(left)
+
+        $container.mousewheel (event, delta, deltaX, deltaY) ->
+
           $target = $(event.target)
-          $target.data( "dragging", true)
-        # handle the stop dragging event, by setting the user is NOT dragging.
-        stop : (event, ui) ->
-          $target = $(event.target)
-          $target.data( "dragging", false)
+          $container = if $target.hasClass( "scroll-container" ) then $target else $target.parents( ".scroll-container" )
+          $scrollbar = $container.children( ".#{scrollbar}" )
+          $scroller = $scrollbar.children( ".#{scroller}" )

-        # the user is dragging, scroll the $container.
-        drag : (event, ui) ->
-          $scroller = $(event.target)
-          $scrollbar = $scroller.parent()
-          $container = $scrollbar.parent()
+          left = $container.scrollLeft() + (deltaX * 10)

-          scrollerLeft = (ui.offset.left - $container.offset().left)
-
-          left = scrollLeft(scrollerLeft, $container, $scroller)
-
-          # realign the scrollbar to the visible left border.
-          $scrollbar.css( "left", left ) if "hidden" is $container.css( "overflow" )
+          # check if out of boundaries.
+          left = 0 if (0 > left)
+          left = $container.maxScrollLeft() if $container.maxScrollLeft() < left

           # scroll the container.
           $container.scrollLeft(left)
+          $scrollbar.css( "left", left)

-      $container.mousewheel (event, delta, deltaX, deltaY) ->
-
-        $target = $(event.target)
-        $container = if $target.hasClass( "scroll-container" ) then $target else $target.parents( ".scroll-container" )
-        $scrollbar = $container.children( ".#{scrollbar}" )
-        $scroller = $scrollbar.children( ".#{scroller}" )
-
-        left = $container.scrollLeft() + (deltaX * 10)
-
-        # check if out of boundaries.
-        left = 0 if (0 > left)
-        left = $container.maxScrollLeft() if $container.maxScrollLeft() < left
-
-        # scroll the container.
-        $container.scrollLeft(left)
-        $scrollbar.css( "left", left)
-
-        # prevent default handling of this event especially to avoid Chrome shaking the page
-        #  and move across history.
-        event.preventDefault()
+          # prevent default handling of this event especially to avoid Chrome shaking the page
+          #  and move across history.
+          event.preventDefault()

-      $container.scroll (event) ->
-        $container = $(event.target)
-        $scrollbar = $container.children( ".#{scrollbar}" )
-        $scroller = $scrollbar.children( ".#{scroller}" )
+        $container.scroll (event) ->
+          $container = $(event.target)
+          $scrollbar = $container.children( ".#{scrollbar}" )
+          $scroller = $scrollbar.children( ".#{scroller}" )

-        # if the user is dragging, we don't move the $scroller.
-        return if true is $scroller.data( "dragging" )
+          # if the user is dragging, we don't move the $scroller.
+          return if true is $scroller.data( "dragging" )

-        $scrollbar.css('left', $container.scrollLeft()
-        $scroller.css('left', scrollerLeft($container, $scroller)
+          $scrollbar.css('left', $container.scrollLeft() )
+          $scroller.css('left', scrollerLeft($container, $scroller) )
+
+
+  $.fn.scrollbars = ( method ) ->
+    # Method calling logic
+    return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )) if methods[method]?
+    return methods.init.apply( this, arguments ) if "object" is typeof method or not method?
+    $.error( "Method #{method }does not exist on scrollbars" )

 )(jQuery)


find out if we need to render() water

find out if we need to render() water

--- src/3d/Water.coffee
+++ src/3d/Water.coffee
@@ -31,4 +31,6 @@
     @speed = 1

   tick: (tpf) ->
+    # @water.render()
+    # @water.material.uniforms.time.value += 1.0 / 60.0
     @water.material.uniforms.time.value += tpf * @speed


CLI making the source compiler to build the css inside of the Lovely() callback

--- cli/source.coffee
+++ cli/source.coffee
@@ -81,7 +81,7 @@

     # adding the 'exports' object
     if /[^a-z0-9_\-\.]exports[^a-z0-9\_\-]/i.test(source)
-      source = "var exports = {};\n\n#{source}\n\nreturn exports;"
+      source = "var exports = {};\n\n"+ source;

     # adding the 'global' object
     if /[^a-z0-9_\-\.]global[^a-z0-9\_\-]/i.test(source)
@@ -93,8 +93,9 @@
     source = """
       Lovely("#{module_name}", [#{"'#{name}'" for name in dependencies}], function(undefined) {
         #{source.replace(/(\n)/g, "$1  ")}
+        #{inline_css(directory)}
+        #{if source.indexOf('var exports = {};') > -1 then "return exports;" else ""}
       });
-      #{inline_css(directory)}
       """

   # creating a standard header block
@@ -179,20 +180,21 @@

   """

-  // embedded css-styles
-  (function(document) {
-    var style = document.createElement('style');
-    var rules = document.createTextNode("#{style}");
-
-    style.type = 'text/css';
-    document.getElementsByTagName('head')[0].appendChild(style);
-
-    if (style.styleSheet) {
-      style.styleSheet.cssText = rules.nodeValue;
-    } else {
-      style.appendChild(rules);
-    }
-  })(this.document);
+    // embedded css-styles
+    (function(document) {
+      var style = document.createElement('style');
+      var rules = document.createTextNode("#{style}");
+
+      style.type = 'text/css';
+      document.getElementsByTagName('head')[0].appendChild(style);
+
+      if (style.styleSheet) {
+        style.styleSheet.cssText = rules.nodeValue;
+      } else {
+        style.appendChild(rules);
+      }
+    })(this.document);
+
   """

 exports.compile = compile


Catch component loading errors under Node.js

--- src/lib/loader/NodeJs.coffee
+++ src/lib/loader/NodeJs.coffee
@@ -110,9 +110,17 @@
     return

   if typeof implementation.getComponent is 'function'
-    instance = implementation.getComponent metadata
+    try
+      instance = implementation.getComponent metadata
+    catch e
+      callback e
+      return
   else if typeof implementation is 'function'
-    instance = implementation metadata
+    try
+      instance = implementation metadata
+    catch e
+      callback e
+      return
   else
     callback new Error "Unable to instantiate #{cPath}"
     return


Added TAPi18n.publishComposit function

Added TAPi18n.publishComposit function

--- tap_i18n_db-server.coffee
+++ tap_i18n_db-server.coffee
@@ -69,6 +69,28 @@

   i18n_handler = () ->
     args = Array.prototype.slice.call(arguments)
+    # last subscription argument is always the language tag
+    language_tag = _.last(args)
+    @.language = language_tag
+    # Set handler context in current fiber's
+    Fiber.current.language_tag = language_tag
+    # Call the user handler without the language_tag argument
+    cursors = handler.apply(@, args.slice(0, -1))
+    # Clear handler context
+    delete Fiber.current.language_tag
+
+    if cursors?
+      return cursors
+
+  # set the actual publish method
+  return Meteor.publish(name, i18n_handler, options)
+
+TAPi18n.publishComposite = (name, handler, options) ->
+  if name is null
+    throw new Meteor.Error(500, "TAPi18n.publishComposite doesn't support null publications")
+
+  i18n_handler = () ->
+    args = Array.prototype.slice.call(arguments)

     # last subscription argument is always the language tag
     language_tag = _.last(args)
@@ -84,4 +106,5 @@
       return cursors

   # set the actual publish method
-  return Meteor.publish(name, i18n_handler, options)
+
+  return Meteor.publishComposite(name, i18n_handler, options)


Add install command

Add install command

--- src/cli/argv.coffee
+++ src/cli/argv.coffee
@@ -3,4 +3,9 @@
 judge = require '../judge'

 module.exports = (argv) ->
+	parser.command 'install'
+	.callback (opts) ->
+		judge.install process.cwd(), (err) ->
+			throw err if err
+
 	parser.parse()


Beautify styles

Beautify styles

--- lib/Styles.coffee
+++ lib/Styles.coffee
@@ -20,78 +20,77 @@
     white: 47

 module.exports = Styles = (settings = {}) ->
-    if this not instanceof Styles
+    if @ not instanceof Styles
         return new Styles settings
-    this.settings = settings
-    this.settings.stdout = settings.stdout ? process.stdout
+    @settings = settings
+    @settings.stdout = settings.stdout ? process.stdout
     # Current state
-    this.current =
+    @current =
         weight: 'regular'
     # Export colors
-    this.colors = colors
-    this.bgcolors = bgcolors
-    this
+    @colors = colors
+    @bgcolors = bgcolors
+    @

 # Color
 Styles.prototype.color = (color, text) ->
-    this.print text, {color: color}
-    if not text
-        # Save state if no text
-        this.current.color = color
-    this
+    @print text, {color: color}
+    # Save state if no text
+    @current.color = color unless text
+    @

 for color, code of colors
     do (color) ->
         Styles.prototype[color] = (text) ->
-            this.color color, text
+            @color color, text

 Styles.prototype.nocolor = (text) ->
-    this.color null, text
+    @color null, text

 # bgcolor
 Styles.prototype.bgcolor = (bgcolor) ->
     bgcolor ?= 0
-    this.print '\x1B[' + bgcolor + ';m39'
-    this
+    @print '\x1B[' + bgcolor + ';m39'
+    @

 # Font weight
 Styles.prototype.weight = (weight, text) ->
-    this.print text, {weight: weight}
+    @print text, {weight: weight}
     if not text
         # Save state if no text
-        this.current.weight = weight
-    this
+        @current.weight = weight
+    @

 Styles.prototype.bold = (text) ->
-    this.weight 'bold', text
+    @weight 'bold', text

 Styles.prototype.regular = (text) ->
-    this.weight 'regular', text
+    @weight 'regular', text

 # Print

 Styles.prototype.print = (text, settings) ->
-    this.settings.stdout.write this.raw(text, settings)
-    this
+    @settings.stdout.write @raw(text, settings)
+    @

 Styles.prototype.println = (text) ->
-    this.settings.stdout.write text + '\n'
-    this
+    @settings.stdout.write text + '\n'
+    @

 Styles.prototype.ln = ->
-    this.settings.stdout.write '\n'
-    this
+    @settings.stdout.write '\n'
+    @

 # Others

 Styles.prototype.raw = (text, settings) ->
     raw = '';
     settings ?= {}
-    if settings.color isnt null and ( settings.color or this.current.color )
-        raw += '\x1b[' + this.colors[settings.color or this.current.color] + 'm'
+    if settings.color isnt null and ( settings.color or @current.color )
+        raw += '\x1b[' + @colors[settings.color or @current.color] + 'm'
     else
         raw += '\x1b[39m'
-    switch settings.weight or this.current.weight
+    switch settings.weight or @current.weight
         when 'bold'
             raw += '\x1b[1m'
         when 'regular'
@@ -102,13 +101,13 @@
         # Print text if any
         raw += text
         # Restore state if any
-        if this.current.color and this.current.color isnt settings.color
-            raw += this.raw null, this.current.color
-        if this.current.weight and this.current.weight isnt settings.weight
-            raw += this.raw null, this.current.weight
+        if @current.color and @current.color isnt settings.color
+            raw += @raw null, @current.color
+        if @current.weight and @current.weight isnt settings.weight
+            raw += @raw null, @current.weight
     raw

 Styles.prototype.reset = (text) ->
-    this.print null,
+    @print null,
         color: null
         weight: 'regular'


restore low contrast tooltips

restore low contrast tooltips

--- lib/config.coffee
+++ lib/config.coffee
@@ -88,19 +88,14 @@
       else
         body.setAttribute('isotope-ui-gutter-style', 'false')

-    # applyTooltipContrast = () ->
-    #   if atom.config.get('isotope-ui.lowContrastTooltip')
-    #     body.className = body.setAttribute(
-    #       'isotope-ui-tooltip-lowcontrast', 'true'
-    #     )
-    #   else
-    #     body.className = body.setAttribute(
-    #       'isotope-ui-tooltip-lowcontrast', 'false'
-    #     )
+    applyTooltipContrast = () ->
+      if atom.config.get('isotope-ui.lowContrastTooltip')
+        body.setAttribute('isotope-ui-tooltip-lowcontrast', 'true')
+      else
+        body.setAttribute('isotope-ui-tooltip-lowcontrast', 'false')

     # run when atom is ready
-

     applyFont(atom.config.get('isotope-ui.fontFamily'))
     applyFontWeight(atom.config.get('isotope-ui.fontWeight'))
@@ -110,7 +105,7 @@
     applyBackgroundGradient()
     applyBackgroundImage()
     applyGutterStyle()
-    # applyTooltipContrast()
+    applyTooltipContrast()

     # run when configs change
@@ -144,6 +139,6 @@

     atom.config.onDidChange 'isotope-ui.gutterStyle', ->
       applyGutterStyle()
-    #
-    # atom.config.onDidChange 'isotope-ui.lowContrastTooltip', ->
-    #   applyTooltipContrast()
+
+    atom.config.onDidChange 'isotope-ui.lowContrastTooltip', ->
+      applyTooltipContrast()


:bug: wrapLeftRightMotion can't wrap in visualMode.

Since isAsTarget() is true in visual mode, Degradation.

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -165,7 +165,7 @@
 class MoveRight extends Motion
   @extend()
   canWrapToNextLine: (cursor) ->
-    if @isAsTarget() and not cursor.isAtEndOfLine()
+    if not @isMode('visual') and @isAsTarget() and not cursor.isAtEndOfLine()
       false
     else
       settings.get('wrapLeftRightMotion')


#441 cleanup anyway

#441 cleanup anyway

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -28,8 +28,6 @@
   getFirstCharacterBufferPositionForScreenRow
   getTextInScreenRange
   cursorIsAtEndOfLineAtNonEmptyRow
-  buildWordPatternByCursor
-  getNonWordCharactersForCursor
   getFirstCharacterColumForBufferRow

   debug
@@ -452,7 +450,7 @@
 #  - section boundary is also sentence boundary(ignore)
 class MoveToNextSentence extends Motion
   @extend()
-  sentenceRegex: ///[\.!\?][\)\]"']*\s+///g
+  sentenceRegex: ///(?:[\.!\?][\)\]"']*\s+)|(\n|\r\n)///g

   moveCursor: (cursor) ->
     point = cursor.getBufferPosition()
@@ -463,37 +461,23 @@
   getFirstCharacterPositionForRow: (row) ->
     new Point(row, getFirstCharacterColumForBufferRow(@editor, row))

-  getParagraphBoundaryRow: (startRow, direction, fn) ->
-    wasAtNonBlankRow = not @editor.isBufferRowBlank(startRow)
-    for row in getBufferRows(@editor, {startRow, direction})
-      isAtNonBlankRow = not @editor.isBufferRowBlank(row)
-      if wasAtNonBlankRow isnt isAtNonBlankRow
-        if fn?
-          return row if fn?(isAtNonBlankRow)
-        else
-          return row
-      wasAtNonBlankRow = isAtNonBlankRow
+  isBlankRow: (row) ->
+    @editor.isBufferRowBlank(row)

   getNextStartOfSentence: (fromPoint) ->
-    scanRange = [fromPoint, @getVimEofBufferPosition()]
+    scanRange = new Range(fromPoint, @getVimEofBufferPosition())
     foundPoint = null
-    @editor.scanInBufferRange @sentenceRegex, scanRange, ({range, stop}) ->
-      foundPoint = range.end
-      stop()
-    foundPoint
+    @editor.scanInBufferRange @sentenceRegex, scanRange, ({range, matchText, match, stop}) =>
+      if match[1]?
+        if @isBlankRow(range.start.row) isnt @isBlankRow(range.end.row)
+          foundPoint = @getFirstCharacterPositionForRow(range.end.row)
+      else
+        foundPoint = range.end
+      stop() if foundPoint?
+    foundPoint ? scanRange.end

   getPoint: (fromPoint) ->
-    vimEof = @getVimEofBufferPosition()
-    foundPoint = @getNextStartOfSentence(fromPoint)
-
-    if boundaryRow = @getParagraphBoundaryRow(fromPoint.row, 'next')
-      paragraphBoundaryPoint = @getFirstCharacterPositionForRow(boundaryRow)
-    paragraphBoundaryPoint ?= vimEof
-
-    if foundPoint?
-      Point.min(foundPoint, paragraphBoundaryPoint)
-    else
-      paragraphBoundaryPoint
+    @getNextStartOfSentence(fromPoint)

 class MoveToPreviousSentence extends MoveToNextSentence
   @extend()
@@ -501,28 +485,23 @@
   getPreviousStartOfSentence: (fromPoint) ->
     scanRange = [fromPoint, [0, 0]]
     foundPoint = null
-    @editor.backwardsScanInBufferRange @sentenceRegex, scanRange, ({range, match, stop, matchText}) ->
-      if range.end.isLessThan(fromPoint)
-        foundPoint = range.end
-        stop()
-    foundPoint
+    @editor.backwardsScanInBufferRange @sentenceRegex, scanRange, ({range, match, stop, matchText}) =>
+      if match[1]?
+        {start: {row: startRow}, end: {row: endRow}} = range
+        if not @isBlankRow(endRow) and @isBlankRow(startRow)
+          point = @getFirstCharacterPositionForRow(endRow)
+          if point.isLessThan(fromPoint)
+            foundPoint = point
+          else
+            foundPoint = @getFirstCharacterPositionForRow(startRow)
+      else
+        if range.end.isLessThan(fromPoint)
+          foundPoint = range.end
+      stop() if foundPoint?
+    foundPoint ? [0, 0]

   getPoint: (fromPoint) ->
-    foundPoint = @getPreviousStartOfSentence(fromPoint)
-
-    isBlank = (isAtNonBlankRow) -> not isAtNonBlankRow
-    if boundaryRow = @getParagraphBoundaryRow(fromPoint.row, 'previous', isBlank)
-      point = @getFirstCharacterPositionForRow(boundaryRow + 1)
-      if point.isLessThan(fromPoint)
-        paragraphBoundaryPoint = point
-      else
-        paragraphBoundaryPoint = @getFirstCharacterPositionForRow(boundaryRow)
-    paragraphBoundaryPoint ?= [0, 0]
-
-    if foundPoint?
-      Point.max(foundPoint, paragraphBoundaryPoint)
-    else
-      paragraphBoundaryPoint
+    @getPreviousStartOfSentence(fromPoint)

 # Paragraph
 # -------------------------


New schema

New schema

--- lib/person.coffee
+++ lib/person.coffee
@@ -1,7 +1,9 @@
 Persons = new Meteor.Collection 'Persons', transform: (doc) => new @Person doc

 class Person extends @Document
-  # user: username (null if unregistered)
+  # user: (null if unregistered)
+  #   id
+  #   username
   # slug: unique slug for URL
   # created: creation timestamp
   # foreNames


fix basename bug

fix basename bug

--- lib/server.coffee
+++ lib/server.coffee
@@ -290,7 +290,7 @@
                 preprocess: (source) ->
                   id = config.id
                   version = config.version.replace /\./g, '-'
-                  name = path.basename src, '.styl'
+                  name = path.basename src, est

                   if config.type in ['product', 'plugin']
                     p = ".#{id}.v#{version} #{name}"
@@ -492,14 +492,14 @@
         id = req.params.id
         version = req.params.version
         file = req.params[0]
-        name = path.basename file
         filepath = path.join "#{pkgDir id, version}", file
+        ext = path.extname filepath
+        name = path.basename file, ext

         # make sure file exists
         fs.exists filepath, (exists) ->
           return error 404, "File #{file} does not exists" unless exists

-          ext = path.extname filepath
           switch ext

             # schema files
@@ -552,15 +552,6 @@
                     next()

               loadVariables ->
-                getContainer = (name) ->
-                  if name in config.pages
-                    return '#content .pages'
-                  for c, o of config.collections
-                    return '#content' if name is c
-                    return '#content .pages' if name is o
-                  return ''
-
-                console.log req.query
                 asset = new wrap.Stylus {
                   src: filepath
                   vars: req.query


classname bug

classname bug

--- lib/server.coffee
+++ lib/server.coffee
@@ -290,10 +290,10 @@
                 preprocess: (source) ->
                   id = config.id
                   version = config.version.replace /\./g, '-'
-                  name = path.basename src, est
+                  name = path.basename src, ext

                   if config.type in ['product', 'plugin']
-                    p = ".#{id}.v#{version} #{name}"
+                    p = ".#{id}.v#{version} .#{name}"
                     subs = [
                       ['.exports.collection',  "#{p}.collection"]
                       ['.exports.object',      "#{p}.object"]
@@ -560,7 +560,7 @@
                     id = id
                     version = version.replace /\./g, '-'

-                    p = ".#{id}.v#{version} #{name}"
+                    p = ".#{id}.v#{version} .#{name}"
                     subs = [
                       ['.exports.collection',  "#{p}.collection"]
                       ['.exports.object',      "#{p}.object"]


letting the zmq cool for defined number of seconds, when implementation not working as prevised

--- lib/server.coffee
+++ lib/server.coffee
@@ -7,6 +7,7 @@
 #testing for non-daemon monde
 canprogress = true
 refreshrate = 500
+zmqWorking = 3
 try
 	process.stdout.clearLine()
 catch err
@@ -65,6 +66,7 @@
 					if obj.id is id or obj._id is id
 						found = i
 				return found
+			dirtyqueue = 0
 			async.forever((next)->
 				unless r.ready
 					setImmediate -> next()
@@ -72,7 +74,6 @@
 					jumpon++
 					status_c = {}
 					tokill = []
-					dirtyqueue = false
 					for act in workque
 						switch act._status
 							when "new"
@@ -82,7 +83,7 @@
 							when "tosend"
 								act._status = "onqueue"
 								act._lastchange = ~~((new Date).getTime() / 1000)
-								dirtyqueue = true
+								dirtyqueue = ~~((new Date).getTime() / 1000)
 								s_wk.send if config.jsonpack then jsonpack.pack(bulkbody.rows[i].doc) else JSON.stringify(act)
 							when "completed"
 								act._status = "todrop"
@@ -93,11 +94,11 @@
 							when "droperr"
 								act._status = "tofetch"
 								act._lastchange = ~~((new Date).getTime() / 1000)
-							when "onqueue"
-								if s_wk._zmq.pending  is 0
-									# the que lost all elements .. zmq bug
-									act._status = "tosend"
-									act._lastchange = ~~((new Date).getTime() / 1000)
+							# when "onqueue"
+							# 	if s_wk._zmq.pending  is 0
+							# 		# the que lost all elements .. zmq bug
+							# 		act._status = "tosend"
+							# 		act._lastchange = ~~((new Date).getTime() / 1000)
 							when "errored"
 								if act.exec?
 									act._status = "tosend"
@@ -121,7 +122,7 @@
 						#console.log output.join(" | "), output.length,".--"
 						#process.stdout.clearLine()
 						output.push "zmq : " + s_wk._zmq.pending
-						output.push "mem : " + (if os.freemem() < config.minMem * 1048576 then "Ok" else "notOk")
+						output.push "mem : " + (if os.freemem() < (config.minMem * 1048576) then "Ok" else "notOk")
 						if canprogress
 							process.stdout.cursorTo(60)
 							process.stdout.write output.join(" | ")
@@ -132,7 +133,7 @@

 					# Inconsistency checks for zmq queue

-					if !dirtyqueue and status_c.onqueue? and status_c.onqueue isnt s_wk._zmq.pending
+					if ((~~((new Date).getTime() / 1000)) - dirtyqueue > zmqWorking) and status_c.onqueue? and status_c.onqueue isnt s_wk._zmq.pending
 						console.log "Inconsistent Queue", status_c.onqueue, s_wk._zmq.pending
 						zmqids = []
 						for item,ix in s_wk._outgoing
@@ -155,14 +156,15 @@
 								return false if todelete.indexOf(ix) isnt -1
 								return true
 						# on Que but not on zmq
-						for item in workque when item._status = "onqueue"
-							found = false
-							for zItem in zmqids when zItem[0] is item._id
-								found = true
-							if !found
-								console.log "requeueing",item._id
-								item._status = "tosend"
-								item._lastchange = ~~((new Date).getTime() / 1000)
+						for item in workque
+							if item._status is "onqueue"
+								found = false
+								for zItem in zmqids
+									found = true if zItem[0] is item._id
+								if !found
+									console.log "requeueing",item._id
+									item._status = "tosend"
+									item._lastchange = ~~((new Date).getTime() / 1000)
 					setImmediate -> next()
 			,(err)->
 				#it will never stop


Use mapping instead of switch

Use mapping instead of switch

--- resources/assets/coffee/react/beatmap-discussions/message-icon.coffee
+++ resources/assets/coffee/react/beatmap-discussions/message-icon.coffee
@@ -25,9 +25,10 @@

   render: ->
-    name = switch @props.messageType
-      when 'praise' then 'heart'
-      when 'suggestion' then 'circle-o'
-      when 'problem' then 'exclamation-circle'
+    el Icon, name: @typeMapping[@props.messageType]

-    el Icon, name: name
+
+  typeMapping:
+    praise: 'heart'
+    suggestion: 'circle-o'
+    problem: 'exclamation-circle'


add blocked without text in the timeline

--- app/modules/user-timeline/user-timeline-item/user-timeline-item-type.service.coffee
+++ app/modules/user-timeline/user-timeline-item/user-timeline-item-type.service.coffee
@@ -106,7 +106,10 @@
             key: 'TIMELINE.BLOCKED',
             translate_params: ['username', 'obj_name'],
             description: (timeline) ->
-                return $(timeline.data.values_diff.blocked_note_html[1]).text()
+                if timeline.data.values_diff.blocked_note_html
+                    return $(timeline.data.values_diff.blocked_note_html[1]).text()
+                else
+                    return false
         },
         { # UnBlocked
             check: (timeline, event) ->


Workspace: Fixed session join status in pane launcher.

--- client/landing/app/CommonViews/workspace/views/workspacefloatingpanelauncher.coffee
+++ client/landing/app/CommonViews/workspace/views/workspacefloatingpanelauncher.coffee
@@ -13,7 +13,7 @@
     @workspace           = @panel.getDelegate()
     @container           = new KDView cssClass: "workspace-floating-panes"
     {workspaceRef}       = @workspace
-    @isJoinedASession    = @workspace.getOptions().joinedASession
+    @isJoinedASession    = @workspace.isJoinedASession()
     @lastActivePaneKey   = null
     @keysRef             = workspaceRef.child "floatingPaneKeys"
     @paneStateRef        = workspaceRef.child "floatingPaneState"


Explicitly test that 'end' dedents properly after an automatic capture

--- test/test_preprocessor.coffee
+++ test/test_preprocessor.coffee
@@ -60,3 +60,19 @@

     test.done()

+  "'end' dedents properly after an automatic capture": (test) ->
+    output = preprocess """
+

+      <% @getItems (items) -> %>
+        <% for item in items: %>
+
<%= item.name %>

+        <% end %>
+      <% end %>
+

+    """
+
+    lines = output.split("\n")
+    test.same "", lines.pop()
+    test.ok lines.pop().match(/^print/)
+    test.done()
+


Remove unnecessary append method.

Remove unnecessary append method.

--- src/react-editor-view.coffee
+++ src/react-editor-view.coffee
@@ -20,8 +20,6 @@
   Object.defineProperty @::, 'charWidth', get: -> @editor.getDefaultCharWidth()
   Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0]
   Object.defineProperty @::, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1]
-  append: (view) ->
-    @appendToLinesView(view)

   scrollTop: (scrollTop) ->
     if scrollTop?


testing filter

testing filter

--- test/unit/service.spec.coffee
+++ test/unit/service.spec.coffee
@@ -182,7 +182,37 @@
 		expect(setText('{{ l10n.interpolation.message }}')).toBe 'This message is inserted through interpolation using object property.'
 	it 'should work when .get method is used', ->
 		expect(setText('{{ l10n.get("interpolation.message")}}')).toBe 'This message is inserted through interpolation using object property.'
-
+describe 'l10n filter', ->
+	filter = null
+	service = null
+	beforeEach ->
+		angular.module('test-filter', ['l10n', 'l10n-tools']).config (l10nProvider) ->
+			l10nProvider.add 'en-US',
+				filter:
+					hello: 'Hello, filter'
+					hello2: 'Hello %1 and %2'
+			l10nProvider.add 'uk-UA',
+				filter:
+					hello: 'UAHello, filter'
+					hello2: 'UAHello %1 and %2'
+
+		module 'test-filter'
+		inject ($filter, l10n) ->
+			l10n.setLocale 'en-US'
+			service = l10n
+			filter = $filter
+
+	it 'should work', ->
+		expect(filter('l10n')('filter.hello')).toBe 'Hello, filter'
+	it 'should accept params', ->
+		expect(filter('l10n')('filter.hello2')).toBe 'Hello %1 and %2'
+		expect(filter('l10n')('filter.hello2', 'one')).toBe 'Hello one and %2'
+		expect(filter('l10n')('filter.hello2', 'one', 'two')).toBe 'Hello one and two'
+		expect(filter('l10n')('filter.hello2', 'one', 'two', 'three')).toBe 'Hello one and two'
+	it 'should respect active locale', ->
+		expect(filter('l10n')('filter.hello')).toBe 'Hello, filter'
+		service.setLocale 'uk-UA'
+		expect(filter('l10n')('filter.hello')).toBe 'UAHello, filter'
 describe 'provider to service', ->
 	it 'default locale can be changed using l10nProvider.setLocale', ->
 		angular.module('provider2service', ['l10n']).config (l10nProvider) ->


STL/core close #60 Event module methods optimisation

--- stl/core/src/events.coffee
+++ stl/core/src/events.coffee
@@ -27,25 +27,28 @@
   # @return {Class} this
   #
   on: ->
-    args     = A(arguments)
-    events   = args.shift()
-    callback = args.shift()
-    callback = this[callback] if typeof(callback) is 'string'
+    args     = Array_slice.call(arguments, 2)
+    events   = arguments[0]
+    callback = arguments[1]
+    by_name  = false

-    this._listeners is null && (this._listeners = [])
+    if typeof(callback) is 'string'
+      callback = this[callback]
+      by_name  = true

-    switch typeof(events)
-      when 'string'
-        for event in events.split(',')
-          this._listeners.push
-            e: event     # event name
-            c: callback  # callback function reference
-            a: args      # remaining arguments list
-            n: typeof(arguments[1]) is 'string' # a marker if the callback was specified as a method-name
+    listeners = if @_listeners is null then (@_listeners = []) else @_listeners

-      when 'object'
-        for event of events
-          this.on event, events[event]
+    if typeof(events) is 'string'
+      for event in events.split(',')
+        listeners.push
+          e: event     # event name
+          c: callback  # callback function reference
+          a: args      # remaining arguments list
+          n: by_name   # a marker if the callback was specified as a method-name
+
+    else if typeof(events) is 'object'
+      for event of events
+        @on event, events[event]

     this

@@ -62,32 +65,32 @@
   # @return {Class} this
   #
   no: ->
-    args     = A(arguments)
-    events   = args.shift()
-    callback = args.shift()
+    args     = Array_slice.call(arguments, 2)
+    events   = arguments[0]
+    callback = arguments[1]
     callback = this[callback] if typeof(callback) is 'string'

-    this._listeners is null && (this._listeners = [])
+    listeners = if @_listeners is null then (@_listeners = []) else @_listeners

     switch typeof(events)
       when 'string'
         for event in events.split(',')
           index = 0
-          while index < this._listeners.length
-            this._listeners.splice index--, 1 if this._listeners[index].e is
-              event and (this._listeners[index].c is callback or callback is undefined)
+          while index < listeners.length
+            listeners.splice index--, 1 if listeners[index].e is
+              event and (listeners[index].c is callback or callback is undefined)
             index++

       when 'function'
         index = 0
-        while index < this._listeners.length
-          if this._listeners[index].c is events
-            this._listeners.splice index--, 1
+        while index < listeners.length
+          if listeners[index].c is events
+            listeners.splice index--, 1
           index++

       when 'object'
         for event of events
-          this.no event, events[event]
+          @no event, events[event]

     this

@@ -107,28 +110,28 @@
   # @return {boolean} check result
   #
   ones: ->
-    result   = no
-    args     = A(arguments)
-    events   = args.shift()
-    callback = args.shift()
+    result   = 0
+    args     = Array_slice.call(arguments, 2)
+    events   = arguments[0]
+    callback = arguments[1]
     callback = this[callback] if typeof(callback) is 'string'

-    this._listeners is null && (this._listeners = [])
+    listeners = if @_listeners is null then (@_listeners = []) else @_listeners

     switch typeof(events)
       when 'string'
         for event in events.split(',')
-          for entry in this._listeners
+          for entry in listeners
             result |= entry.e is event and (
               entry.c is callback or callback is undefined)

       when 'function'
-        for entry in this._listeners
+        for entry in listeners
           result |= entry.c is events

       when 'object'
         for event of events
-          result |= this.ones event, events[event]
+          result |= @ones event, events[event]

     result is 1 # converting to boolean after the `|=` operations


Fixed CI assert

Fixed CI assert

--- test/youtube-info-test.coffee
+++ test/youtube-info-test.coffee
@@ -12,5 +12,5 @@
         require("../src/youtube-info")(@robot)

     it "registers a hear listener", ->
-        expect(@robot.hear).to.have.been.calledWith(/(https?:\/\/www\.youtube\.com\/watch\?.+?)(?:\s|$)/i)
+        expect(@robot.hear).to.have.been.calledWith(/(https?:\/\/(www|gaming)\.youtube\.com\/watch\?.+?)(?:\s|$)/i)
         expect(@robot.hear).to.have.been.calledWith(/(https?:\/\/youtu\.be\/)([a-z0-9\-_]+)/i)


Make saving unimplemented.

Make saving unimplemented.

--- public/javascripts/main.coffee
+++ public/javascripts/main.coffee
@@ -132,6 +132,9 @@

   $('#saveall button').click ->
+    alert('Unimplemented!')
+    return;
+
     $(this).attr('disabled', 'disabled');
     $(this).text('Saving...')

@@ -143,8 +146,8 @@

       success: (data) ->
         # if data == error
-        alert('Data saved')
-        window.location.reload(false)
+        # alert('Data saved')
+        # window.location.reload(false)

       error: (xhr, ajaxOptions, thrownError) ->
         window.alert('Something went wrong when saving alias information. Error: ' + thrownError);


Fix broken merge

Fix broken merge

--- js-library/Gruntfile.coffee
+++ js-library/Gruntfile.coffee
@@ -135,7 +135,6 @@
       }
     ]
     targets = [
-      'build/factlink.js'
       'build/factlink_loader_basic.js'
       'build/factlink_loader_bookmarklet.js'
       'build/factlink_loader_publishers.js'


Fix typo in test

Fix typo in test

--- test/varity.coffee
+++ test/varity.coffee
@@ -12,7 +12,7 @@
   afterEach ->
     varity.reset()

-  it 'should return a functions', ->
+  it 'should return a function', ->
     varity.should.be.a.Function

   it 'should wrap the passed function', ->


:art: set start position to the c-base! <3


use proper headers for amazon so we don't get gzipped output

--- src/amazon.coffee
+++ src/amazon.coffee
@@ -100,6 +100,9 @@

   requestOptions: ({orderID, orderingShipmentId}) ->
     method: 'GET'
+    headers:
+      'accept': 'text/html'
+      'accept-encoding': 'gzip;q=0'
     uri: "https://www.amazon.com/gp/css/shiptrack/view.html" +
       "/ref=pe_385040_121528360_TE_SIMP_typ?ie=UTF8" +
       "&orderID=#{orderID}" +


Test that content-type is set for mobile-safari user-agents.

--- services/web/test/UnitTests/coffee/FileStore/FileStoreControllerTests.coffee
+++ services/web/test/UnitTests/coffee/FileStore/FileStoreControllerTests.coffee
@@ -50,7 +50,7 @@
 				done()
 			@controller.getFile @req, @res

-		it "should get the file from the db", (done)->
+		it "should get the file from the db", (done)->
 			@stream.pipe = (des)=>
 				opts =
 					project_id: @project_id
@@ -65,5 +65,45 @@
 				@res.setHeader.calledWith("Content-Disposition", "attachment; filename=#{@file.name}").should.equal true
 				done()
 			@controller.getFile @req, @res
-

+		describe "with an HTML file", ->
+
+			beforeEach ->
+				@user_agent = 'A generic browser'
+				@file.name = 'really_bad.html'
+				@req.get = (key) =>
+					if key == 'User-Agent'
+						@user_agent
+
+			describe "from firefox", ->
+
+				beforeEach ->
+					@user_agent = "A Firefox browser"
+
+				it "should not set Content-Type", (done) ->
+					@stream.pipe = (des) =>
+						@res.setHeader.calledWith("Content-Type", "text/plain").should.equal false
+						done()
+					@controller.getFile @req, @res
+
+			describe "from an iPhone", ->
+
+				beforeEach ->
+					@user_agent = "An iPhone browser"
+
+				it "should set Content-Type to 'text/plain'", (done) ->
+					@stream.pipe = (des) =>
+						@res.setHeader.calledWith("Content-Type", "text/plain").should.equal true
+						done()
+					@controller.getFile @req, @res
+
+			describe "from an iPad", ->
+
+				beforeEach ->
+					@user_agent = "An iPad browser"
+
+				it "should set Content-Type to 'text/plain'", (done) ->
+					@stream.pipe = (des) =>
+						@res.setHeader.calledWith("Content-Type", "text/plain").should.equal true
+						done()
+					@controller.getFile @req, @res


Fix typos in the RestStorage adapter.

Fix typos in the RestStorage adapter.

--- src/batman.coffee
+++ src/batman.coffee
@@ -2222,7 +2222,7 @@

   readAll: (_, recordsOptions, callback) ->
     @optionsForCollection recordsOptions, (err, options) ->
-      [err, recordOptions] = @_filterData('before', 'readAll', err, recordOptions)
+      [err, recordsOptions] = @_filterData('before', 'readAll', err, recordsOptions)
       if err
         callback(err)
         return
@@ -2230,8 +2230,8 @@
       new Batman.Request $mixin options,
         data: recordsOptions
         method: 'GET'
-        success: (data) => callback(@_filterData('after', 'readAll', undefined, data, recordOptions)...)
-        error:  (error) => callback(@_filterData('after', 'readAll', error, error.request.get('response'), recordOptions)...)
+        success: (data) => callback(@_filterData('after', 'readAll', undefined, data, recordsOptions)...)
+        error:  (error) => callback(@_filterData('after', 'readAll', error, error.request.get('response'), recordsOptions)...)

   @::after 'readAll', $passError ([data, options]) ->
     recordData = if data[@collectionJsonNamespace] then data[@collectionJsonNamespace] else data


Added test for #24

Added test for #24

--- tests/views.coffee
+++ tests/views.coffee
@@ -19,6 +19,24 @@
       t.equal 1, res.body, '
CoffeeKup inline template: bar
'
     c.get '/foobar', (err, res) ->
       t.equal 2, res.body, '
CoffeeKup inline template: foobar
'
+
+  'inline + default layout': (t) ->
+    t.expect 1
+    t.wait 3000
+
+    zapp = zappa port++, ->
+      @enable 'default layout'
+
+      @get '/': ->
+        @render 'index', foo: 'bar'
+
+      @view index: ->
+        @title = 'CoffeeKup default layout'
+        h2 "CoffeeKup inline template: #{@foo}"
+
+    c = t.client(zapp.app)
+    c.get '/', (err, res) ->
+      t.equal 1, res.body, '<!DOCTYPE html><title>CoffeeKup default layout</title>
CoffeeKup inline template: bar
'

   'inline + inline layout': (t) ->
     t.expect 1


Set also input and select fields to read only on form disable.

--- app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
+++ app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
@@ -2479,12 +2479,16 @@
     lookupForm = @findForm(form)

     if lookupForm
-      App.Log.notice 'ControllerForm', 'disable...', lookupForm
-      lookupForm.find('button').attr('disabled', true)
-      lookupForm.find('[type="submit"]').attr('disabled', true)
-      lookupForm.find('[type="reset"]').attr('disabled', true)
+      App.Log.debug 'ControllerForm', 'disable form...', lookupForm
+
+      # set forms to read only during communication with backend
+      lookupForm.find('button, input, select').attr('readonly', true)
+
+      # disable additionals submits
+      lookupForm.find('button.btn').attr('disabled', true)
     else
       App.Log.notice 'ControllerForm', 'disable item...', form
+      form.attr('readonly', true)
       form.attr('disabled', true)

   @enable: (form) ->
@@ -2492,12 +2496,16 @@
     lookupForm = @findForm(form)

     if lookupForm
-      App.Log.notice 'ControllerForm', 'enable...', lookupForm
-      lookupForm.find('button').attr('disabled', false)
-      lookupForm.find('[type="submit"]').attr('disabled', false)
-      lookupForm.find('[type="reset"]').attr('disabled', false)
+      App.Log.debug 'ControllerForm', 'enable form...', lookupForm
+
+      # enable fields again
+      lookupForm.find('button, input, select').attr('readonly', false)
+
+      # enable submits again
+      lookupForm.find('button.btn').attr('disabled', false)
     else
       App.Log.notice 'ControllerForm', 'enable item...', form
+      form.attr('readonly', false)
       form.attr('disabled', false)

   @validate: (data) ->


Remove touchstart event

Remove touchstart event

iOS9 requires the click event in order to trigger file selection #1068

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -712,12 +712,7 @@
             # Only the actual dropzone or the message element should trigger file selection
             if (clickableElement != @element) or (evt.target == @element or Dropzone.elementInside evt.target, @element.querySelector ".dz-message")
               @hiddenFileInput.click() # Forward the click
-          "touchstart": (evt) =>
-            noPropagation evt
-            # Only the actual dropzone or the message element should trigger file selection
-            if (clickableElement != @element) or (evt.target == @element or Dropzone.elementInside evt.target, @element.querySelector ".dz-message")
-              @hiddenFileInput.click() # Forward the click
-
+            return true

     @enable()


Client Side resize: Coded directly in JS then migrated to coffee

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -139,7 +139,11 @@
     maxThumbnailFilesize: 10 # in MB. When the filename exceeds this limit, the thumbnail will not be generated.
     thumbnailWidth: 120
     thumbnailHeight: 120
-
+    #The default values for the clientSide resize. Set resizeImage to false to disable it
+    resizeImage: true
+    resizeHeight : 1024
+    resizeWidth : 1024
+    resizeQuality: 0.8
     # The base that is used to calculate the filesize. You can change this to
     # 1024 if you would rather display kibibytes, mebibytes, etc...
     # 1024 is technically incorrect,
@@ -296,7 +300,7 @@
     #
     #  srcX, srcy, trgX and trgY can be omitted (in which case 0 is assumed).
     #  trgWidth and trgHeight can be omitted (in which case the options.thumbnailWidth / options.thumbnailHeight are used)
-    resize: (file) ->
+    resize: (file, type) ->
       info =
         srcX: 0
         srcY: 0
@@ -305,8 +309,12 @@

       srcRatio = file.width / file.height

-      info.optWidth = @options.thumbnailWidth
-      info.optHeight = @options.thumbnailHeight
+      if type == "imageResize"
+        info.optWidth = @options.resizeWidth
+        info.optHeight = @options.resizeHeight
+      else
+        info.optWidth = @options.thumbnailWidth
+        info.optHeight = @options.thumbnailHeight

       # automatically calculate dimensions if not specified
       if !info.optWidth? and !info.optHeight?
@@ -323,15 +331,15 @@
         # This image is smaller than the canvas
         info.trgHeight = info.srcHeight
         info.trgWidth = info.srcWidth
-
       else
         # Image is bigger and needs rescaling
-        if srcRatio > trgRatio
-          info.srcHeight = file.height
-          info.srcWidth = info.srcHeight * trgRatio
+        if (srcRatio > 1) #horizontal
+          info.trgWidth = info.optWidth
+          info.trgHeight = info.optHeight / srcRatio
         else
-          info.srcWidth = file.width
-          info.srcHeight = info.srcWidth / trgRatio
+          info.trgWidth = info.optWidth * srcRatio
+          info.trgHeight = info.optHeight
+

       info.srcX = (file.width - info.srcWidth) / 2
       info.srcY = (file.height - info.srcHeight) / 2
@@ -920,20 +928,34 @@
     else
       @options.accept.call this, file, done

+
   addFile: (file) ->
+    if @options.resizeImage
+      reader = new FileReader
+      _this = this
+      reader.onload = (e) =>
+        _this.resizeImg this.result, (_file) =>
+          _this._processFileUpload _file
+
+      reader.readAsDataURL file
+    else
+      @_processFileUpload file
+
+  _processFileUpload: (file) ->
     file.upload =
       progress: 0
       # Setting the total upload size to file.size for the beginning
       # It's actual different than the size to be transmitted.
       total: file.size
       bytesSent: 0
+
+    @_enqueueThumbnail file
+
     @files.push file

     file.status = Dropzone.ADDED

     @emit "addedfile", file
-
-    @_enqueueThumbnail file

     @accept file, (error) =>
       if error
@@ -944,6 +966,62 @@
         @enqueueFile file if @options.autoQueue # Will set .accepted = true
       @_updateMaxFilesReachedClass()

+  resizeImg: (file, callback) ->
+
+    img = document.createElement "img"
+    _this = this
+    img.onload = =>
+      file.width = img.width
+      file.height = img.height
+
+      resizeInfo = @options.resize.call @, file, "imageResize"
+
+      resizeInfo.trgWidth ?= resizeInfo.optWidth
+      resizeInfo.trgHeight ?= resizeInfo.optHeight
+
+      canvas = document.createElement "canvas"
+      ctx = canvas.getContext "2d"
+      canvas.width = resizeInfo.trgWidth
+      canvas.height = resizeInfo.trgHeight
+
+      # This is a bugfix for iOS' scaling bug.
+      drawImageIOSFix ctx, img, cX ? 0, resizeInfo.srcY ? 0, resizeInfo.srcWidth, resizeInfo.srcHeight, resizeInfo.trgX ? 0, resizeInfo.trgY ? 0, resizeInfo.trgWidth, resizeInfo.trgHeight
+
+      image = canvas.toDataURL "image/png", @options.imageQuality
+      file = _this.dataUriToBlob image
+
+      callback() if callback?
+
+    img.src = file
+
+  dataUriToBlob: (dataURI) ->
+    byteString = if dataURI.split(',')[0].indexOf('base64') >= 0 then atob dataURI.split(',')[1] else unescape dataURI.split(',')[1]
+    mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
+
+    ab = new ArrayBuffer byteString.length
+    ia = new Uint8Array ab
+
+    i = 0
+    while i < byteString.length
+      ia[i] = byteString.charCodeAt(i)
+      i++
+
+    dataView = new DataView(ab)
+    blob = new Blob([ ia ], type: mimeString)
+
+    blob
+
+
+  compareWidthHeight : (width, height) ->
+    diff = []
+    if width > height
+      diff[0] = width - height
+      diff[1] = 0
+    else
+      diff[0] = 0
+      diff[1] = height - width
+
+    diff

   # Wrapper for enqueueFile
   enqueueFiles: (files) -> @enqueueFile file for file in files; null
@@ -956,7 +1034,6 @@
     else
       throw new Error "This file can't be queued because it has already been processed or was rejected."

-
   _thumbnailQueue: [ ]
   _processingThumbnail: no
   _enqueueThumbnail: (file) ->
@@ -1025,6 +1102,11 @@

       # This is a bugfix for iOS' scaling bug.
       drawImageIOSFix ctx, img, resizeInfo.srcX ? 0, resizeInfo.srcY ? 0, resizeInfo.srcWidth, resizeInfo.srcHeight, resizeInfo.trgX ? 0, resizeInfo.trgY ? 0, resizeInfo.trgWidth, resizeInfo.trgHeight
+
+      #I have this code in JS, don't know how to write in coffee, but the thumbnail drawing seems to perform better this way
+      #var diff = [0, 0];
+      #diff = _this.compareWidthHeight(img.width, img.height);
+      #drawImageIOSFix(ctx, img, (_ref = diff[0] / 2) != null ? _ref : 0, (_ref1 = diff[1] / 2) != null ? _ref1 : 0, img.width - diff[0], img.height - diff[1], (_ref2 = resizeInfo.trgX) != null ? _ref2 : 0, (_ref3 = resizeInfo.trgY) != null ? _ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight);

       thumbnail = canvas.toDataURL "image/png"


6109: Fixed new QuestionFormView for show mode

--- app/assets/javascripts/backbone/views/question_form_view.js.coffee
+++ app/assets/javascripts/backbone/views/question_form_view.js.coffee
@@ -2,15 +2,18 @@
 class ELMO.Views.QuestionFormView extends ELMO.Views.ApplicationView
   initialize: (options) ->
     @prefillableTypes = options.prefillableTypes # Will be null for Question form
-    @$('select[id$="_qtype_name"]').trigger('change')
+    @toggleFields()

   events:
     'change select[id$="_qtype_name"]': 'typeChanged'

   typeChanged: (e) ->
-    newType = @$(e.target).val()
-    @toggleAutoIncrement(newType)
-    @togglePrefillPattern(newType)
+    @toggleFields()
+
+  toggleFields: ->
+    type = @fieldValue('qtype_name')
+    @toggleAutoIncrement(type)
+    @togglePrefillPattern(type)

   toggleAutoIncrement: (type) ->
     @$('.question_auto_increment')[if type == 'counter' then 'show' else 'hide']()
@@ -21,3 +24,12 @@
         @$('.questioning_prefill_pattern').show()
       else
         @$('.questioning_prefill_pattern').hide()
+
+  # Gets form field value, or static value if field is read-only
+  fieldValue: (attrib) ->
+    div = @$(".question_fields .form_field[data-field-name=#{attrib}] .control")
+    if div.is('.read_only')
+      wrapper = div.find('.ro-val')
+      wrapper.data('val') || wrapper.text()
+    else
+      div.find('input, select, textarea').val()


Materials bin: support HTML tags in (sanitized) description

[#91659452] [#92150260]

Server calls sanitize for us, so description is always safe (would be nice to add test for that).

--- app/assets/javascripts/components/materials_bin/material.js.coffee
+++ app/assets/javascripts/components/materials_bin/material.js.coffee
@@ -55,6 +55,8 @@
     unless @props.visible then 'mb-hidden' else ''

   render: ->
-    (div {className: "mb-material-description #{@getVisibilityClass()}"},
-      @props.description
+    (div
+      className: "mb-material-description #{@getVisibilityClass()}"
+      # It's already sanitized by server!
+      dangerouslySetInnerHTML: {__html: @props.description}
     )


doing rollback if the record wasn't updated

--- app/assets/javascripts/views/clientes/clientes_edit_view.js.coffee
+++ app/assets/javascripts/views/clientes/clientes_edit_view.js.coffee
@@ -8,6 +8,7 @@
     self.successCallback cliente, "actualizado"

   failure: (response) ->
+    self.get("model").rollback()
     self.failureCallback response

   submit: ->


:bug: Fix remaining call to unsubscribe in binding

--- lib/minimap-git-diff-binding.coffee
+++ lib/minimap-git-diff-binding.coffee
@@ -31,7 +31,6 @@
   deactivate: ->
     @removeDecorations()
     @subscriptions.dispose()
-    @unsubscribe()
     @diffs = null

   scheduleUpdate: => setImmediate(@updateDiffs)


Changed internal classname

Changed internal classname

--- core/app/backbone/views/interacting_users/ndp_interacting_users_avatars_view.coffee
+++ core/app/backbone/views/interacting_users/ndp_interacting_users_avatars_view.coffee
@@ -1,4 +1,4 @@
-class NDPInteractingUsersAvatarView extends Backbone.Marionette.Layout
+class InteractingUsersAvatarView extends Backbone.Marionette.Layout
   tagName: 'li'
   className: 'discussion-interacting-users-avatar'
   template: 'interacting_users/avatar'
@@ -6,7 +6,7 @@
 class window.NDPInteractingUsersAvatarsView extends Backbone.Marionette.CompositeView
   className: 'discussion-interacting-users-avatars'
   template: "interacting_users/avatars"
-  itemView: NDPInteractingUsersAvatarView
+  itemView: InteractingUsersAvatarView

   itemViewContainer: ".js-interactor-avatars-collection"


应用位置输入框修改为div

应用位置输入框修改为div

--- lib/project/new-project-info.coffee
+++ lib/project/new-project-info.coffee
@@ -25,15 +25,16 @@
         @div class: 'form-row clearfix', =>
           @label '应用创建位置', class: 'row-title pull-left'
           @div class: 'row-content pull-left', =>
-            @subview 'appPath', new TextEditorView(mini: true)
+            # @subview 'appPath', new TextEditorView(mini: true)
+            @div class: 'textEditStyle',outlet: 'appPath'
             @span class: 'inline-block status-added icon icon-file-directory openFolder', click: 'openFolder'
         @div class: 'form-row msg clearfix', =>
-          @div '该目录已存在', class: 'text-warning hide errorMsg', outlet: 'errorMsg'
+          @div '该应用目录已存在', class: 'text-warning hide errorMsg', outlet: 'errorMsg'

   initialize: ->
-    @appId.getModel().onDidChange => @setPath()
+    @appId.getModel().onDidChange => @checkProjectName()
     @appName.getModel().onDidChange => @checkInput()
-    @appPath.getModel().onDidChange => @checkPath()
+    # @appPath.getModel().onDidChange => @checkPath()

   attached: ->
     console.log @parentView.options
@@ -41,23 +42,23 @@
     # if @type isnt 'template'
     @parentView.setNextBtn('finish')
     @parentView.disableNext()
-    @appPath.basePath = desc.newProjectDefaultPath
-    @appPath.setText @appPath.basePath
+    @appPath.html desc.newProjectDefaultPath

   openFolder: ->
     atom.pickFolder (paths) =>
       if paths?
         console.log paths[0]
-        path = pathM.join paths[0],@appId.getText()
-        console.log  path
-        @appPath.basePath = path
-        @appPath.setText @appPath.basePath
+        # path = pathM.join paths[0],@appId.getText()
+        # console.log  path
+        @appPath.html paths[0]

   getElement: ->
     @element

   getProjectInfo: ->
-    path = @appPath.getText().trim()
+    appId = @appId.getText().trim()
+    appPath = @appPath.html().trim()
+    path = pathM.join appPath,appId
     dir = new Directory(path)
     path = pathM.join desc.newProjectDefaultPath,dir.getBaseName() if dir.getParent().isRoot() is yes
     projectInfo =
@@ -71,7 +72,7 @@
   checkInput: ->
     flag1 = @appId.getText().trim() isnt ""
     flag2 = @appName.getText().trim() isnt ""
-    flag3 = @appPath.getText().trim() isnt ""
+    flag3 = @appPath.html().trim() isnt ""
     flag4 = @errorMsg.hasClass('hide')
     flag5 = @errorMsg2.hasClass('hide')

@@ -80,20 +81,22 @@
     else
       @parentView.disableNext()

-  setPath: ->
-    currPath = @appPath.basePath
+  checkProjectName: ->
+    # currPath = @appPath.basePath
     str = @appId.getText().trim()
     console.log Util.checkProjectName str
     if Util.checkProjectName str
       @errorMsg2.addClass('hide')
     else
       @errorMsg2.removeClass('hide')
-    @appPath.setText pathM.join currPath,str if currPath isnt ""
-    @checkInput()
+    # @appPath.setText pathM.join currPath,str if currPath isnt ""
+    @checkPath()
+    # @checkInput()

   checkPath: ->
-    path = @appPath.getText().trim()
-    @appPath.basePath = path if @appPath.hasFocus()
+    appId = @appId.getText().trim()
+    appPath = @appPath.html().trim()
+    path = pathM.join appPath,appId
     if path isnt ""
       dir = new Directory(path);
       dir.exists()


focus calls done in nextTick() so synchronous calls won't prematurely return

--- lib/focus.coffee
+++ lib/focus.coffee
@@ -11,14 +11,15 @@
     called = false

     (err, result) ->
-      return if error
-      return if called
+      process.nextTick ->
+        return if error
+        return if called

-      called = true
-      if err
-        error = err
-        return done err, results
+        called = true
+        if err
+          error = err
+          return done err, results

-      results[ref] = result
-      if --counter is 0
-        done null, results
+        results[ref] = result
+        if --counter is 0
+          done null, results


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/camera.coffee
+++ src/camera.coffee
@@ -36,7 +36,7 @@
       height: 2 * deadzone.y

     if objectCenter.x > centerRect.right
-      I.scroll.
+      I
     else if objectCenter.x < centerRect.left


Remove processorName from notifier name.

--- src/client.coffee
+++ src/client.coffee
@@ -94,7 +94,7 @@
         session: err.session or {}

       notice.context.notifier =
-        name: 'airbrake-js-' + processorName
+        name: 'airbrake-js'
         version: '<%= pkg.version %>'
         url: 'https://github.com/airbrake/airbrake-js'