Add GLRay.create_debug_canvas() and destroy_debug_canvas()

--- engine/glray.coffee
+++ engine/glray.coffee
@@ -94,6 +94,11 @@
     resize: (@width, @height) ->
         @pixels = new Uint8Array(@width * @height * 4)
         @pixels16 = new Uint16Array(@pixels.buffer)
+        if @debug_canvas
+            @debug_canvas.width = @width
+            @debug_canvas.height = @height
+            @ctx = null
+        @step = 0

     init: (scene, camera) ->
         @add_scene(scene)
@@ -277,9 +282,6 @@
                         requestAnimationFrame(step)
                 step()

-        if @step == 1 and @sorted_meshes
-            @build_longest_rows()
-
         # Extract pixels (some time after render is queued, to avoid stalls)
         if @step == @render_steps + @wait_steps
             # t = performance.now()
@@ -291,10 +293,10 @@
             quat.copy(@last_cam_rot, @cam_rot)
         if @debug_canvas?
             if not @ctx
-                @debug_canvas.width = @w
-                @debug_canvas.height = @h
+                @debug_canvas.width = @width
+                @debug_canvas.height = @height
                 @ctx = @debug_canvas.getContext('2d', {alpha: false})
-                @imagedata = @ctx.createImageData(@w, @h)
+                @imagedata = @ctx.createImageData(@width, @height)
             @imagedata.data.set(@pixels)
             d = @imagedata.data
             i = 3
@@ -307,32 +309,23 @@
                     i += 4
             @ctx.putImageData(@imagedata, 0, 0)
         return
-
-    build_longest_rows: ->
-        @longest_rows_len = []
-        @longest_rows_x = []
-        @longest_rows_y = []
-        inv_w = 1/(@w-1)
-        inv_h = 1/(@h-1)
-        pixels16 = @pixels16
-        i = 0
-        for y in [0...@h]
-            current_id = pixels16[i]
-            rlen = 1
-            i += 2
-            for x in [1...@w]
-                id = pixels16[i]
-                if current_id == id
-                    rlen += 1
-                else
-                    if rlen > (@longest_rows_len[current_id]|0)
-                        @longest_rows_len[current_id] = rlen
-                        @longest_rows_x[current_id] = (x - (rlen>>1)) * inv_w
-                        @longest_rows_y[current_id] = 1 - (y*inv_h)
-                    current_id = id
-                    rlen = 1
-                i += 2
-        return
+
+    create_debug_canvas: ->
+        if @debug_canvas?
+            @destroy_debug_canvas()
+        @debug_canvas = document.createElement 'canvas'
+        @debug_canvas.width = @width
+        @debug_canvas.height = @height
+        @debug_canvas.style.position = 'fixed'
+        @debug_canvas.style.top = '0'
+        @debug_canvas.style.left = '0'
+        @debug_canvas.style.transform = 'scaleY(-1)'
+        document.body.appendChild @debug_canvas
+        @ctx = null
+
+    destroy_debug_canvas: ->
+        document.body.removeChild @debug_canvas
+        @ctx = null

     debug_random: ->
         for i in [0...1000]


Use \b to surround keywords

Use \b to surround keywords

Fix #15

Fixed highlighting for variables starting with function, delegate, __FILE__, __MODULE__, __LINE__, __FUNCTION__, __PRETTY_FUNCTION__, out, body, this, unittest, interface, struct, union, enum, template, private, package, protected, public, export, else

--- grammars/d.cson
+++ grammars/d.cson
@@ -96,7 +96,7 @@
     'base-type': {
       'patterns': [
         {
-          'match': '(auto|bool|byte|ubyte|short|ushort|int|uint|long|ulong|char|wchar|dchar|float|double|real|ifloat|idouble|ireal|cfloat|cdouble|creal|void)\\**(?!\\w)',
+          'match': '(auto|bool|byte|ubyte|short|ushort|int|uint|long|ulong|char|wchar|dchar|float|double|real|ifloat|idouble|ireal|cfloat|cdouble|creal|void)\\**\\b',
           'name': 'storage.type.basic-type.d'
         },
       ]
@@ -104,7 +104,7 @@
     # defined in object.di, not actually part of grammar
     'defined-type': {
       'patterns': [
-        'match': '(wstring|dstring|string|size_t|ptrdiff_t)\\**(?!\\w)',
+        'match': '(wstring|dstring|string|size_t|ptrdiff_t)\\**\\b',
         'name': 'storage.type.defined.d'
       ]
     },
@@ -130,7 +130,7 @@
     },
     'type-ctor': {
       'patterns': [
-        'match': '(const|immutable|inout|shared)(?!\\w)',
+        'match': '(const|immutable|inout|shared)\\b',
         'name': 'storage.type.modifier.d'
       ]
     },
@@ -214,7 +214,7 @@
     'identity-expression': {
       'patterns': [
         {
-          'match': '\\s+(is|!is)\\s+',
+          'match': '\\b(is|!is)\\b',
           'name': 'keyword.operator.identity.d'
         },
       ]
@@ -230,7 +230,7 @@
     'in-expression': {
       'patterns': [
         {
-          'match': '\\s+(in|!in)\\s+',
+          'match': '\\b(in|!in)\\b',
           'name': 'keyword.operator.in.d'
         },
       ]
@@ -303,7 +303,7 @@
     'function-literal': {
       'patterns': [
         {
-          'match': '(function|delegate)',
+          'match': '(function|delegate)\\b',
           'name': 'keyword.other.function-literal.d'
         },
       ]
@@ -358,7 +358,7 @@
     'type-specialization': {
       'patterns': [
         {
-          'match': '(struct|union|class|interface|enum|function|delegate|super|const|immutable|inout|shared|return|__parameters)(?!\\w)',
+          'match': '(struct|union|class|interface|enum|function|delegate|super|const|immutable|inout|shared|return|__parameters)\\b',
           'name': 'keyword.other.storage.type-specialization.d'
         },
       ]
@@ -396,7 +396,7 @@
     },
     'special-keyword': {
       'patterns': [
-        'match': '__FILE__|__MODULE__|__LINE__|__FUNCTION__|__PRETTY_FUNCTION__',
+        'match': '(__FILE__|__MODULE__|__LINE__|__FUNCTION__|__PRETTY_FUNCTION__)\\b',
         'name': 'constant.language.special-keyword.d'
       ]
     },
@@ -926,7 +926,7 @@
           ]
         },
         {
-          'match': 'out',
+          'match': 'out\\b',
           'name': 'keyword.control.out.d'
         },
       ]
@@ -934,7 +934,7 @@
     'body-statement': {
       'patterns': [
         {
-          'match': 'body',
+          'match': 'body\\b',
           'name': 'keyword.control.body.d'
         },
       ]
@@ -942,7 +942,7 @@
     'constructor': {
       'patterns': [
         {
-          'match': 'this',
+          'match': 'this\\b',
           'name': 'entity.name.function.constructor.d'
         },
       ]
@@ -974,7 +974,7 @@
     'unit-test': {
       'patterns': [
         {
-          'match': 'unittest',
+          'match': 'unittest\\b',
           'name': 'entity.name.function.unit-test.d'
         },
       ]
@@ -1024,7 +1024,7 @@
     'interface-declaration': {
       'patterns': [
         {
-          'match': 'interface',
+          'match': 'interface\\b',
           'name': 'keyword.other.class.interface.d'
         },
       ]
@@ -1032,7 +1032,7 @@
     'struct-declaration': {
       'patterns': [
         {
-          'match': 'struct',
+          'match': 'struct\\b',
           'name': 'storage.type.struct.d'
         },
       ]
@@ -1040,7 +1040,7 @@
     'union-declaration': {
       'patterns': [
         {
-          'match': 'union',
+          'match': 'union\\b',
           'name': 'storage.type.union.d'
         },
       ]
@@ -1051,7 +1051,7 @@
     'enum-declaration': {
       'patterns': [
         {
-          'match': 'enum',
+          'match': 'enum\\b',
           'name': 'storage.type.enum.d'
         },
       ]
@@ -1062,7 +1062,7 @@
     'template-declaration': {
       'patterns': [
         {
-          'match': 'template',
+          'match': 'template\\b',
           'name': 'support.class.template.d',
         },
       ]
@@ -1134,7 +1134,7 @@

     'protection-attribute': {
       'patterns': [
-        'match': 'private|package|protected|public|export',
+        'match': '(private|package|protected|public|export)\\b',
         'name': 'keyword.other.protections.d'
       ]
     },
@@ -1192,7 +1192,7 @@
         { 'include': '#condition' },
         { 'include': '#declaration-block' },
         {
-          'match': 'else',
+          'match': 'else\\b',
           'name': 'keyword.control.else.d'
         },
         { 'include': '#colon' },
@@ -1204,7 +1204,7 @@
         { 'include': '#condition' },
         { 'include': '#no-scope-non-empty-statement' },
         {
-          'match': 'else',
+          'match': 'else\\b',
           'name': 'keyword.control.else.d'
         },
       ]


Input: binding more input events by default

--- src/components/inputs/inputview.coffee
+++ src/components/inputs/inputview.coffee
@@ -74,10 +74,8 @@
     o.hint                    or= null          # a String of HTML
     o.autogrow                 ?= no            # a Boolean
     o.enableTabKey             ?= no            # a Boolean # NOT YET READY needs some work
-    o.bind                    or= ""            # a String of event names
     o.forceCase               or= null          # a String of either "lowercase" or "uppercase"
-
-    o.bind += " blur change focus"
+    o.bind                      = KD.utils.curry 'blur change focus paste cut input copy', o.bind

     @setType o.type


Comment on internal testing.

Comment on internal testing.

--- test/EventBusTest.coffee
+++ test/EventBusTest.coffee
@@ -32,6 +32,7 @@
 		msg = bus._createMessage "address", "data", reply
 		test.equals "address", msg.address
 		test.equals "data", msg.data
+		# @todo Reply is testing the internals of EventMessage, not cool, but easier that executing the reply.
 		test.strictEqual reply, msg._replyHandler
 		# Noop creation.
 		msg2 = bus._createMessage msg


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/clamp_bounds.coffee
+++ test/clamp_bounds.coffee
@@ -1,6 +1,7 @@
 module "ClampBounds"

 test "should clamp the x-axis", ->
+  #

   player = GameObject
     includedModules: ["ClampBounds"]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -1,6 +1,6 @@
 module "Controllable"

-test "should move player", ->
+test "should move player left when", ->
   one = 1

   # Test for equality of two objects


extract: clean tests

extract: clean tests

--- test/core/extract.coffee
+++ test/core/extract.coffee
@@ -9,21 +9,19 @@

   they 'should see extension .tgz', (ssh, next) ->
     # Test a non existing extracted dir
-    mecano.extract
+    mecano
       ssh: ssh
+    .extract
       source: "#{__dirname}/../resources/a_dir.tgz"
       destination: scratch
     , (err, extracted) ->
       extracted.should.be.true()
-      # Test an existing extracted dir
-      # Note, there is no way for us to know which directory
-      # it is in advance
     .then next

   they 'should see extension .zip', (ssh, next) ->
-    # Test a non existing extracted dir
-    mecano.extract
+    mecano
       ssh: ssh
+    .extract
       source: "#{__dirname}/../resources/a_dir.zip"
       destination: scratch
     , (err, extracted) ->
@@ -31,9 +29,9 @@
     .then next

   they 'should see extension .tar.bz2', (ssh, next) ->
-    # Test a non existing extracted dir
-    mecano.extract
+    mecano
       ssh: ssh
+    .extract
       source: "#{__dirname}/../resources/a_dir.tar.bz2"
       destination: scratch
     , (err, extracted) ->
@@ -41,9 +39,9 @@
     .then next

   they 'should see extension .tar.xz', (ssh, next) ->
-    # Test a non existing extracted dir
-    mecano.extract
+    mecano
       ssh: ssh
+    .extract
       source: "#{__dirname}/../resources/a_dir.tar.xz"
       destination: scratch
     , (err, extracted) ->
@@ -52,28 +50,28 @@

   they 'should validate a created file', (ssh, next) ->
     # Test with invalid creates option
-    mecano.extract
+    mecano
       ssh: ssh
+    .extract
       source: "#{__dirname}/../resources/a_dir.tgz"
       destination: scratch
       creates: "#{scratch}/oh_no"
-    , (err, extracted) ->
+    .then (err, extracted) ->
       err.message.should.eql "Failed to create 'oh_no'"
       # Test with valid creates option
-      mecano.extract
-        ssh: ssh
-        source: "#{__dirname}/../resources/a_dir.tgz"
-        destination: scratch
-        creates: "#{scratch}/a_dir"
-      , (err, extracted) ->
-        return next err if err
-        extracted.should.be.true()
-        next()
+    .extract
+      source: "#{__dirname}/../resources/a_dir.tgz"
+      destination: scratch
+      creates: "#{scratch}/a_dir"
+    , (err, extracted) ->
+      extracted.should.be.true()
+    .then next

   they 'should # option # not_if_exists', (ssh, next) ->
     # Test with invalid creates option
-    mecano.extract
+    mecano
       ssh: ssh
+    .extract
       source: "#{__dirname}/../resources/a_dir.tgz"
       destination: scratch
       not_if_exists: __dirname


test fix: don't escape '"' when converting text nodes to HTML

--- test/fakeDocument.coffee
+++ test/fakeDocument.coffee
@@ -7,6 +7,7 @@
   SelfClosingTags[tag] = true

 JadeRt = require '../lib/runtime'
+Jade = require '../lib/jade'

 class Document
@@ -148,7 +149,10 @@
       # Don't unescape text on script nodes
       @txt
     else
-      JadeRt.escape(@txt)
+      @txt
+        .replace(/&(?!\w+;)/g, '&')
+        .replace(/</g, '<')
+        .replace(/>/g, '>')

 class CommentNode
   constructor: (@txt) ->


added session test

added session test

--- test/session.test.coffee
+++ test/session.test.coffee
@@ -118,6 +118,7 @@
 createChart = (i=0, el='rect', num=7) ->
     chartId = 0
     casper.thenOpen domain + '/chart/create', () ->
+        @test.assertNotEquals @getCurrentUrl(), domain + '/chart/create', 'redirected to chart editor'
         chartId = @getCurrentUrl().substr(domain.length + 7, 5)
         @test.assertExists '#upload-data-text', 'created chart ' + chartId
         data = @evaluate (i) ->