Tests for more complicated schema.

Tests for more complicated schema.

--- tests.coffee
+++ tests.coffee
@@ -9211,6 +9211,21 @@
         body: 'NestedFooBar1'
       ]
       subdocument:
+        person:
+          _id: @person1Id
+        persons: [
+          _id: @person1Id
+        ,
+          _id: @person2Id
+        ,
+          _id: @person3Id
+        ,
+          _id: @person1Id
+        ,
+          _id: @person2Id
+        ,
+          _id: @person3Id
+        ]
         body: 'SubdocumentFooBar1'
       body: 'FooBar1'
     ,
@@ -9228,6 +9243,21 @@
         body: 'NestedFooBar2'
       ]
       subdocument:
+        person:
+          _id: @person2Id
+        persons: [
+          _id: @person2Id
+        ,
+          _id: @person2Id
+        ,
+          _id: @person2Id
+        ,
+          _id: @person1Id
+        ,
+          _id: @person2Id
+        ,
+          _id: @person3Id
+        ]
         body: 'SubdocumentFooBar2'
       body: 'FooBar2'
     ,
@@ -9245,6 +9275,21 @@
         body: 'NestedFooBar3'
       ]
       subdocument:
+        person:
+          _id: @person1Id
+        persons: [
+          _id: @person1Id
+        ,
+          _id: @person1Id
+        ,
+          _id: @person1Id
+        ,
+          _id: @person1Id
+        ,
+          _id: @person2Id
+        ,
+          _id: @person3Id
+        ]
         body: 'SubdocumentFooBar3'
       body: 'FooBar3'
     ,
@@ -9272,6 +9317,35 @@
         username: 'person1'
         displayName: 'Person 1'
       subdocument:
+        person:
+          _id: @person1Id
+          displayName: 'Person 1'
+          username: 'person1'
+        persons: [
+          _id: @person1Id
+          displayName: 'Person 1'
+          username: 'person1'
+        ,
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person3Id
+          displayName: 'Person 3'
+          username: 'person3'
+        ,
+          _id: @person1Id
+          displayName: 'Person 1'
+          username: 'person1'
+        ,
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person3Id
+          displayName: 'Person 3'
+          username: 'person3'
+        ]
         slug: 'subdocument-prefix-foobar1-subdocumentfoobar1-suffix'
         body: 'SubdocumentFooBar1'
       nested: [
@@ -9297,6 +9371,35 @@
         username: 'person1'
         displayName: 'Person 1'
       subdocument:
+        person:
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        persons: [
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person1Id
+          displayName: 'Person 1'
+          username: 'person1'
+        ,
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person3Id
+          displayName: 'Person 3'
+          username: 'person3'
+        ]
         slug: 'subdocument-prefix-foobar2-subdocumentfoobar2-suffix'
         body: 'SubdocumentFooBar2'
       nested: [
@@ -9322,6 +9425,35 @@
         username: 'person1'
         displayName: 'Person 1'
       subdocument:
+        person:
+          _id: @person1Id
+          displayName: 'Person 1'
+          username: 'person1'
+        persons: [
+          _id: @person1Id
+          displayName: 'Person 1'
+          username: 'person1'
+        ,
+          _id: @person1Id
+          displayName: 'Person 1'
+          username: 'person1'
+        ,
+          _id: @person1Id
+          displayName: 'Person 1'
+          username: 'person1'
+        ,
+          _id: @person1Id
+          displayName: 'Person 1'
+          username: 'person1'
+        ,
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person3Id
+          displayName: 'Person 3'
+          username: 'person3'
+        ]
         slug: 'subdocument-prefix-foobar3-subdocumentfoobar3-suffix'
         body: 'SubdocumentFooBar3'
       nested: [
@@ -9346,12 +9478,12 @@
     @person3 = Person.documents.findOne @person3Id,
       transform: null # So that we can use test.equal

-    test.equal _.omit(@person1, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person1, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person1Id
       _schema: '1.0.0'
       username: 'person1'
       displayName: 'Person 1'
-      count: 3
+      count: 8

     testSetEqual test, @person1.posts,
       [
@@ -9379,16 +9511,96 @@
         ]
         body: 'FooBar3'
       ]
+    testSetEqual test, @person1.subdocumentPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1'
+        nested: [
+          body: 'NestedFooBar1'
+        ]
+        body: 'FooBar1'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3'
+        nested: [
+          body: 'NestedFooBar3'
+        ]
+        body: 'FooBar3'
+      ]
+    testSetEqual test, @person1.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1'
+        nested: [
+          body: 'NestedFooBar1'
+        ]
+        body: 'FooBar1'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2'
+        nested: [
+          body: 'NestedFooBar2'
+        ]
+        body: 'FooBar2'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3'
+        nested: [
+          body: 'NestedFooBar3'
+        ]
+        body: 'FooBar3'
+      ]
     testSetEqual test, @person1.nestedPosts, []

-    test.equal _.omit(@person2, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person2, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person2Id
       _schema: '1.0.0'
       username: 'person2'
       displayName: 'Person 2'
-      count: 1
+      count: 5

     testSetEqual test, @person2.posts, []
+    testSetEqual test, @person2.subdocumentPosts,
+      [
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2'
+        nested: [
+          body: 'NestedFooBar2'
+        ]
+        body: 'FooBar2'
+      ]
+    testSetEqual test, @person2.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1'
+        nested: [
+          body: 'NestedFooBar1'
+        ]
+        body: 'FooBar1'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2'
+        nested: [
+          body: 'NestedFooBar2'
+        ]
+        body: 'FooBar2'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3'
+        nested: [
+          body: 'NestedFooBar3'
+        ]
+        body: 'FooBar3'
+      ]
     testSetEqual test, @person2.nestedPosts,
       [
         _id: @postId1
@@ -9400,14 +9612,41 @@
         body: 'FooBar1'
       ]

-    test.equal _.omit(@person3, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person3, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person3Id
       _schema: '1.0.0'
       username: 'person3'
       displayName: 'Person 3'
-      count: 2
+      count: 5

     testSetEqual test, @person3.posts, []
+    testSetEqual test, @person3.subdocumentPosts, []
+    testSetEqual test, @person3.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1'
+        nested: [
+          body: 'NestedFooBar1'
+        ]
+        body: 'FooBar1'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2'
+        nested: [
+          body: 'NestedFooBar2'
+        ]
+        body: 'FooBar2'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3'
+        nested: [
+          body: 'NestedFooBar3'
+        ]
+        body: 'FooBar3'
+      ]
     testSetEqual test, @person3.nestedPosts,
       [
         _id: @postId2
@@ -9464,6 +9703,21 @@
         body: 'NestedFooBar4'
       ]
       subdocument:
+        person:
+          _id: @person1Id
+        persons: [
+          _id: @person1Id
+        ,
+          _id: @person1Id
+        ,
+          _id: @person1Id
+        ,
+          _id: @person1Id
+        ,
+          _id: @person2Id
+        ,
+          _id: @person2Id
+        ]
         body: 'SubdocumentFooBar4'
       body: 'FooBar4'
     ,
@@ -9489,6 +9743,21 @@
         body: 'NestedFooBar5'
       ]
       subdocument:
+        person:
+          _id: @person3Id
+        persons: [
+          _id: @person3Id
+        ,
+          _id: @person3Id
+        ,
+          _id: @person3Id
+        ,
+          _id: @person3Id
+        ,
+          _id: @person2Id
+        ,
+          _id: @person3Id
+        ]
         body: 'SubdocumentFooBar5'
       body: 'FooBar5'
     ,
@@ -9508,12 +9777,12 @@
     @person3 = Person.documents.findOne @person3Id,
       transform: null # So that we can use test.equal

-    test.equal _.omit(@person1, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person1, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person1Id
       _schema: '1.0.0'
       username: 'person1'
       displayName: 'Person 1'
-      count: 6
+      count: 13

     testSetEqual test, @person1.posts,
       [
@@ -9575,6 +9844,94 @@
         ]
         body: 'FooBar5'
       ]
+    testSetEqual test, @person1.subdocumentPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1'
+        nested: [
+          body: 'NestedFooBar1'
+        ]
+        body: 'FooBar1'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3'
+        nested: [
+          body: 'NestedFooBar3'
+        ]
+        body: 'FooBar3'
+      ,
+        _id: @postId4
+        subdocument:
+          body: 'SubdocumentFooBar4'
+        nested: [
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ]
+        body: 'FooBar4'
+      ]
+    testSetEqual test, @person1.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1'
+        nested: [
+          body: 'NestedFooBar1'
+        ]
+        body: 'FooBar1'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2'
+        nested: [
+          body: 'NestedFooBar2'
+        ]
+        body: 'FooBar2'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3'
+        nested: [
+          body: 'NestedFooBar3'
+        ]
+        body: 'FooBar3'
+      ,
+        _id: @postId4
+        subdocument:
+          body: 'SubdocumentFooBar4'
+        nested: [
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ]
+        body: 'FooBar4'
+      ]
     testSetEqual test, @person1.nestedPosts,
       [
         _id: @postId4
@@ -9600,14 +9957,84 @@
         body: 'FooBar4'
       ]

-    test.equal _.omit(@person2, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person2, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person2Id
       _schema: '1.0.0'
       username: 'person2'
       displayName: 'Person 2'
-      count: 2
+      count: 8

     testSetEqual test, @person2.posts, []
+    testSetEqual test, @person2.subdocumentPosts,
+      [
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2'
+        nested: [
+          body: 'NestedFooBar2'
+        ]
+        body: 'FooBar2'
+      ]
+    testSetEqual test, @person2.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1'
+        nested: [
+          body: 'NestedFooBar1'
+        ]
+        body: 'FooBar1'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2'
+        nested: [
+          body: 'NestedFooBar2'
+        ]
+        body: 'FooBar2'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3'
+        nested: [
+          body: 'NestedFooBar3'
+        ]
+        body: 'FooBar3'
+      ,
+        _id: @postId4
+        subdocument:
+          body: 'SubdocumentFooBar4'
+        nested: [
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ]
+        body: 'FooBar4'
+      ,
+        _id: @postId5
+        subdocument:
+          body: 'SubdocumentFooBar5'
+        nested: [
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5'
+        ]
+        body: 'FooBar5'
+      ]
     testSetEqual test, @person2.nestedPosts,
       [
         _id: @postId1
@@ -9641,14 +10068,66 @@
         body: 'FooBar4'
       ]

-    test.equal _.omit(@person3, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person3, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person3Id
       _schema: '1.0.0'
       username: 'person3'
       displayName: 'Person 3'
-      count: 4
+      count: 9

     testSetEqual test, @person3.posts, []
+    testSetEqual test, @person3.subdocumentPosts,
+      [
+        _id: @postId5
+        subdocument:
+          body: 'SubdocumentFooBar5'
+        nested: [
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5'
+        ]
+        body: 'FooBar5'
+      ]
+    testSetEqual test, @person3.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1'
+        nested: [
+          body: 'NestedFooBar1'
+        ]
+        body: 'FooBar1'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2'
+        nested: [
+          body: 'NestedFooBar2'
+        ]
+        body: 'FooBar2'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3'
+        nested: [
+          body: 'NestedFooBar3'
+        ]
+        body: 'FooBar3'
+      ,
+        _id: @postId5
+        subdocument:
+          body: 'SubdocumentFooBar5'
+        nested: [
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5'
+        ]
+        body: 'FooBar5'
+      ]
     testSetEqual test, @person3.nestedPosts,
       [
         _id: @postId2
@@ -9764,12 +10243,12 @@
     @person3 = Person.documents.findOne @person3Id,
       transform: null # So that we can use test.equal

-    test.equal _.omit(@person1, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person1, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person1Id
       _schema: '1.0.0'
       username: 'person1'
       displayName: 'Person 1'
-      count: 6
+      count: 13

     testSetEqual test, @person1.posts,
       [
@@ -9831,6 +10310,94 @@
         ]
         body: 'FooBar5a'
       ]
+    testSetEqual test, @person1.subdocumentPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1a'
+        nested: [
+          body: 'NestedFooBar1a'
+        ]
+        body: 'FooBar1a'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3a'
+        nested: [
+          body: 'NestedFooBar3a'
+        ]
+        body: 'FooBar3a'
+      ,
+        _id: @postId4
+        subdocument:
+          body: 'SubdocumentFooBar4a'
+        nested: [
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ]
+        body: 'FooBar4a'
+      ]
+    testSetEqual test, @person1.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1a'
+        nested: [
+          body: 'NestedFooBar1a'
+        ]
+        body: 'FooBar1a'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2a'
+        nested: [
+          body: 'NestedFooBar2a'
+        ]
+        body: 'FooBar2a'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3a'
+        nested: [
+          body: 'NestedFooBar3a'
+        ]
+        body: 'FooBar3a'
+      ,
+        _id: @postId4
+        subdocument:
+          body: 'SubdocumentFooBar4a'
+        nested: [
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ]
+        body: 'FooBar4a'
+      ]
     testSetEqual test, @person1.nestedPosts,
       [
         _id: @postId4
@@ -9856,14 +10423,84 @@
         body: 'FooBar4a'
       ]

-    test.equal _.omit(@person2, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person2, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person2Id
       _schema: '1.0.0'
       username: 'person2'
       displayName: 'Person 2'
-      count: 2
+      count: 8

     testSetEqual test, @person2.posts, []
+    testSetEqual test, @person2.subdocumentPosts,
+      [
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2a'
+        nested: [
+          body: 'NestedFooBar2a'
+        ]
+        body: 'FooBar2a'
+      ]
+    testSetEqual test, @person2.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1a'
+        nested: [
+          body: 'NestedFooBar1a'
+        ]
+        body: 'FooBar1a'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2a'
+        nested: [
+          body: 'NestedFooBar2a'
+        ]
+        body: 'FooBar2a'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3a'
+        nested: [
+          body: 'NestedFooBar3a'
+        ]
+        body: 'FooBar3a'
+      ,
+        _id: @postId4
+        subdocument:
+          body: 'SubdocumentFooBar4a'
+        nested: [
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ]
+        body: 'FooBar4a'
+      ,
+        _id: @postId5
+        subdocument:
+          body: 'SubdocumentFooBar5a'
+        nested: [
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5a'
+        ,
+          body: 'NestedFooBar5'
+        ]
+        body: 'FooBar5a'
+      ]
     testSetEqual test, @person2.nestedPosts,
       [
         _id: @postId1
@@ -9897,14 +10534,66 @@
         body: 'FooBar4a'
       ]

-    test.equal _.omit(@person3, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person3, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person3Id
       _schema: '1.0.0'
       username: 'person3'
       displayName: 'Person 3'
-      count: 4
+      count: 9

     testSetEqual test, @person3.posts, []
+    testSetEqual test, @person3.subdocumentPosts,
+      [
+        _id: @postId5
+        subdocument:
+          body: 'SubdocumentFooBar5a'
+        nested: [
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5a'
+        ,
+          body: 'NestedFooBar5'
+        ]
+        body: 'FooBar5a'
+      ]
+    testSetEqual test, @person3.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1a'
+        nested: [
+          body: 'NestedFooBar1a'
+        ]
+        body: 'FooBar1a'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2a'
+        nested: [
+          body: 'NestedFooBar2a'
+        ]
+        body: 'FooBar2a'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3a'
+        nested: [
+          body: 'NestedFooBar3a'
+        ]
+        body: 'FooBar3a'
+      ,
+        _id: @postId5
+        subdocument:
+          body: 'SubdocumentFooBar5a'
+        nested: [
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5a'
+        ,
+          body: 'NestedFooBar5'
+        ]
+        body: 'FooBar5a'
+      ]
     testSetEqual test, @person3.nestedPosts,
       [
         _id: @postId2
@@ -9984,6 +10673,35 @@
         username: 'person1'
         displayName: 'Person 1'
       subdocument:
+        person:
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        persons: [
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person1Id
+          displayName: 'Person 1'
+          username: 'person1'
+        ,
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person3Id
+          displayName: 'Person 3'
+          username: 'person3'
+        ]
         slug: 'subdocument-prefix-foobar2a-subdocumentfoobar2a-suffix'
         body: 'SubdocumentFooBar2a'
       nested: [
@@ -10016,12 +10734,12 @@
     @person3 = Person.documents.findOne @person3Id,
       transform: null # So that we can use test.equal

-    test.equal _.omit(@person1, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person1, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person1Id
       _schema: '1.0.0'
       username: 'person1'
       displayName: 'Person 1'
-      count: 6
+      count: 13

     testSetEqual test, @person1.posts,
       [
@@ -10085,6 +10803,96 @@
         ]
         body: 'FooBar5a'
       ]
+    testSetEqual test, @person1.subdocumentPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1a'
+        nested: [
+          body: 'NestedFooBar1a'
+        ]
+        body: 'FooBar1a'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3a'
+        nested: [
+          body: 'NestedFooBar3a'
+        ]
+        body: 'FooBar3a'
+      ,
+        _id: @postId4
+        subdocument:
+          body: 'SubdocumentFooBar4a'
+        nested: [
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ]
+        body: 'FooBar4a'
+      ]
+    testSetEqual test, @person1.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1a'
+        nested: [
+          body: 'NestedFooBar1a'
+        ]
+        body: 'FooBar1a'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2a'
+        nested: [
+          body: 'NestedFooBar2a'
+        ,
+          body: 'NestedFooBarNew'
+        ]
+        body: 'FooBar2a'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3a'
+        nested: [
+          body: 'NestedFooBar3a'
+        ]
+        body: 'FooBar3a'
+      ,
+        _id: @postId4
+        subdocument:
+          body: 'SubdocumentFooBar4a'
+        nested: [
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ]
+        body: 'FooBar4a'
+      ]
     testSetEqual test, @person1.nestedPosts,
       [
         _id: @postId4
@@ -10110,14 +10918,88 @@
         body: 'FooBar4a'
       ]

-    test.equal _.omit(@person2, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person2, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person2Id
       _schema: '1.0.0'
       username: 'person2'
       displayName: 'Person 2'
-      count: 3
+      count: 9

     testSetEqual test, @person2.posts, []
+    testSetEqual test, @person2.subdocumentPosts,
+      [
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2a'
+        nested: [
+          body: 'NestedFooBar2a'
+        ,
+          body: 'NestedFooBarNew'
+        ]
+        body: 'FooBar2a'
+      ]
+    testSetEqual test, @person2.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1a'
+        nested: [
+          body: 'NestedFooBar1a'
+        ]
+        body: 'FooBar1a'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2a'
+        nested: [
+          body: 'NestedFooBar2a'
+        ,
+          body: 'NestedFooBarNew'
+        ]
+        body: 'FooBar2a'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3a'
+        nested: [
+          body: 'NestedFooBar3a'
+        ]
+        body: 'FooBar3a'
+      ,
+        _id: @postId4
+        subdocument:
+          body: 'SubdocumentFooBar4a'
+        nested: [
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ]
+        body: 'FooBar4a'
+      ,
+        _id: @postId5
+        subdocument:
+          body: 'SubdocumentFooBar5a'
+        nested: [
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5a'
+        ,
+          body: 'NestedFooBar5'
+        ]
+        body: 'FooBar5a'
+      ]
     testSetEqual test, @person2.nestedPosts,
       [
         _id: @postId1
@@ -10161,14 +11043,68 @@
         body: 'FooBar2a'
       ]

-    test.equal _.omit(@person3, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person3, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person3Id
       _schema: '1.0.0'
       username: 'person3'
       displayName: 'Person 3'
-      count: 4
+      count: 9

     testSetEqual test, @person3.posts, []
+    testSetEqual test, @person3.subdocumentPosts,
+      [
+        _id: @postId5
+        subdocument:
+          body: 'SubdocumentFooBar5a'
+        nested: [
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5a'
+        ,
+          body: 'NestedFooBar5'
+        ]
+        body: 'FooBar5a'
+      ]
+    testSetEqual test, @person3.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1a'
+        nested: [
+          body: 'NestedFooBar1a'
+        ]
+        body: 'FooBar1a'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2a'
+        nested: [
+          body: 'NestedFooBar2a'
+        ,
+          body: 'NestedFooBarNew'
+        ]
+        body: 'FooBar2a'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3a'
+        nested: [
+          body: 'NestedFooBar3a'
+        ]
+        body: 'FooBar3a'
+      ,
+        _id: @postId5
+        subdocument:
+          body: 'SubdocumentFooBar5a'
+        nested: [
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5a'
+        ,
+          body: 'NestedFooBar5'
+        ]
+        body: 'FooBar5a'
+      ]
     testSetEqual test, @person3.nestedPosts,
       [
         _id: @postId2
@@ -10247,6 +11183,35 @@
         username: 'person1'
         displayName: 'Person 1'
       subdocument:
+        person:
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        persons: [
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person1Id
+          displayName: 'Person 1'
+          username: 'person1'
+        ,
+          _id: @person2Id
+          displayName: 'Person 2'
+          username: 'person2'
+        ,
+          _id: @person3Id
+          displayName: 'Person 3'
+          username: 'person3'
+        ]
         slug: 'subdocument-prefix-foobar2a-subdocumentfoobar2a-suffix'
         body: 'SubdocumentFooBar2a'
       nested: [
@@ -10271,12 +11236,12 @@
     @person3 = Person.documents.findOne @person3Id,
       transform: null # So that we can use test.equal

-    test.equal _.omit(@person1, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person1, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person1Id
       _schema: '1.0.0'
       username: 'person1'
       displayName: 'Person 1'
-      count: 6
+      count: 13

     testSetEqual test, @person1.posts,
       [
@@ -10338,6 +11303,94 @@
         ]
         body: 'FooBar5a'
       ]
+    testSetEqual test, @person1.subdocumentPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1a'
+        nested: [
+          body: 'NestedFooBar1a'
+        ]
+        body: 'FooBar1a'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3a'
+        nested: [
+          body: 'NestedFooBar3a'
+        ]
+        body: 'FooBar3a'
+      ,
+        _id: @postId4
+        subdocument:
+          body: 'SubdocumentFooBar4a'
+        nested: [
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ]
+        body: 'FooBar4a'
+      ]
+    testSetEqual test, @person1.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1a'
+        nested: [
+          body: 'NestedFooBar1a'
+        ]
+        body: 'FooBar1a'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2a'
+        nested: [
+          body: 'NestedFooBar2a'
+        ]
+        body: 'FooBar2a'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3a'
+        nested: [
+          body: 'NestedFooBar3a'
+        ]
+        body: 'FooBar3a'
+      ,
+        _id: @postId4
+        subdocument:
+          body: 'SubdocumentFooBar4a'
+        nested: [
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ]
+        body: 'FooBar4a'
+      ]
     testSetEqual test, @person1.nestedPosts,
       [
         _id: @postId4
@@ -10363,14 +11416,84 @@
         body: 'FooBar4a'
       ]

-    test.equal _.omit(@person2, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person2, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person2Id
       _schema: '1.0.0'
       username: 'person2'
       displayName: 'Person 2'
-      count: 2
+      count: 8

     testSetEqual test, @person2.posts, []
+    testSetEqual test, @person2.subdocumentPosts,
+      [
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2a'
+        nested: [
+          body: 'NestedFooBar2a'
+        ]
+        body: 'FooBar2a'
+      ]
+    testSetEqual test, @person2.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1a'
+        nested: [
+          body: 'NestedFooBar1a'
+        ]
+        body: 'FooBar1a'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2a'
+        nested: [
+          body: 'NestedFooBar2a'
+        ]
+        body: 'FooBar2a'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3a'
+        nested: [
+          body: 'NestedFooBar3a'
+        ]
+        body: 'FooBar3a'
+      ,
+        _id: @postId4
+        subdocument:
+          body: 'SubdocumentFooBar4a'
+        nested: [
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4a'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ,
+          body: 'NestedFooBar4'
+        ]
+        body: 'FooBar4a'
+      ,
+        _id: @postId5
+        subdocument:
+          body: 'SubdocumentFooBar5a'
+        nested: [
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5a'
+        ,
+          body: 'NestedFooBar5'
+        ]
+        body: 'FooBar5a'
+      ]
     testSetEqual test, @person2.nestedPosts,
       [
         _id: @postId1
@@ -10404,14 +11527,66 @@
         body: 'FooBar4a'
       ]

-    test.equal _.omit(@person3, 'posts', 'nestedPosts'),
+    test.equal _.omit(@person3, 'posts', 'subdocumentPosts', 'subdocumentsPosts', 'nestedPosts'),
       _id: @person3Id
       _schema: '1.0.0'
       username: 'person3'
       displayName: 'Person 3'
-      count: 4
+      count: 9

     testSetEqual test, @person3.posts, []
+    testSetEqual test, @person3.subdocumentPosts,
+      [
+        _id: @postId5
+        subdocument:
+          body: 'SubdocumentFooBar5a'
+        nested: [
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5a'
+        ,
+          body: 'NestedFooBar5'
+        ]
+        body: 'FooBar5a'
+      ]
+    testSetEqual test, @person3.subdocumentsPosts,
+      [
+        _id: @postId1
+        subdocument:
+          body: 'SubdocumentFooBar1a'
+        nested: [
+          body: 'NestedFooBar1a'
+        ]
+        body: 'FooBar1a'
+      ,
+        _id: @postId2
+        subdocument:
+          body: 'SubdocumentFooBar2a'
+        nested: [
+          body: 'NestedFooBar2a'
+        ]
+        body: 'FooBar2a'
+      ,
+        _id: @postId3
+        subdocument:
+          body: 'SubdocumentFooBar3a'
+        nested: [
+          body: 'NestedFooBar3a'
+        ]
+        body: 'FooBar3a'
+      ,
+        _id: @postId5
+        subdocument:
+          body: 'SubdocumentFooBar5a'
+        nested: [
+          body: 'NestedFooBar5'
+        ,
+          body: 'NestedFooBar5a'
+        ,
+          body: 'NestedFooBar5'
+        ]
+        body: 'FooBar5a'
+      ]
     testSetEqual test, @person3.nestedPosts,
       [
         _id: @postId2


example of two macros

example of two macros

--- tests.coffee
+++ tests.coffee
@@ -3731,8 +3731,80 @@
   "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz "

   # ---------------------------------------------------------------------------
+  # a macro
+  """
+  a = '( (1...3) (console print x + y + z) x y z)
+  console print a length
+  numParams = a length - 2
+  console print numParams
+
+  bodies = '()
+
+  for each number in:
+  ﹍﹍0...numParams
+  ﹍do:
+  ﹍﹍bodies[number] = '()
+  ﹍﹍bodies[number] = bodies[number] + 'for + 'each + a[2+number] + 'in + ': + a[0]
+  ﹍﹍bodies[number] = bodies[number] + 'do + ':
+
+  bodies[numParams-1] = bodies[numParams-1] + a[1]
+  console print bodies
+
+  console print " ---------- "
+  for each number in:
+  ﹍﹍numParams...1
+  ﹍do:
+  ﹍﹍bodies[number-2] = bodies[number-2] + bodies[number-1]
+
+  console print " ---- "
+  console print bodies
+
+  toRun = bodies[0]
+  console print " ---- "
+  console print toRun
+
+  toRun eval
+  """
+  '53( ( for each x in : ( 1 ... 3 ) do : ) ( for each y in : ( 1 ... 3 ) do : ) ( for each z in : ( 1 ... 3 ) do : ( [object of class "Console"] print x + y + z ) ) ) ----------  ---- ( ( for each x in : ( 1 ... 3 ) do : ( for each y in : ( 1 ... 3 ) do : ( for each z in : ( 1 ... 3 ) do : ( [object of class "Console"] print x + y + z ) ) ) ) ( for each y in : ( 1 ... 3 ) do : ( for each z in : ( 1 ... 3 ) do : ( [object of class "Console"] print x + y + z ) ) ) ( for each z in : ( 1 ... 3 ) do : ( [object of class "Console"] print x + y + z ) ) ) ---- ( for each x in : ( 1 ... 3 ) do : ( for each y in : ( 1 ... 3 ) do : ( for each z in : ( 1 ... 3 ) do : ( [object of class "Console"] print x + y + z ) ) ) )34454556'
+
+  # ---------------------------------------------------------------------------
+  # same macro, used more cleanly as a keyword
+
   """
   to nestedRepeat:
+  ﹍(rangeBodyAndVars)
+  do:
+  ﹍accessUpperContext
+  ﹍numParams = rangeBodyAndVars length - 2
+  ﹍body = rangeBodyAndVars[1]
+  ﹍range =  rangeBodyAndVars[0]
+  ﹍
+  ﹍bodies = '()
+  ﹍
+  ﹍for each number in:
+  ﹍﹍﹍0...numParams
+  ﹍﹍do:
+  ﹍﹍﹍bodies[number] = '()
+  ﹍﹍﹍bodies[number] = bodies[number] + 'for + 'each + rangeBodyAndVars[2+number] + 'in + ': + range
+  ﹍﹍﹍bodies[number] = bodies[number] + 'do + ':
+  ﹍
+  ﹍bodies[numParams-1] = bodies[numParams-1] + body
+  ﹍
+  ﹍for each number in:
+  ﹍﹍﹍(numParams-1)...0
+  ﹍﹍do:
+  ﹍﹍﹍bodies[number-1] = bodies[number-1] + bodies[number]
+  ﹍
+  ﹍bodies[0] eval
+
+  nestedRepeat '( (1...3) (console print x + y + z) x y z)
+  """
+  "34454556"
+
+
+  # ---------------------------------------------------------------------------
+  """
+  to aToObjectFunction:
   ﹍
   do:
   ﹍for each word in:
@@ -3740,7 +3812,7 @@
   ﹍do:
   ﹍﹍console print word

-  nestedRepeat
+  aToObjectFunction
   """
   "1+1"


Updated tests.

Updated tests.

--- tests.coffee
+++ tests.coffee
@@ -136,67 +136,67 @@

     results = []
     handle = Tracker.autorun =>
-      results.push parentComponent.componentChildren()
+      results.push parentComponent.childrenComponents()

     resultsWith = []
     handleWith = Tracker.autorun =>
-      resultsWith.push parentComponent.componentChildrenWith(field: 'foobar')
-
-    component.componentParent parentComponent
-    parentComponent.addComponentChild component
-
-    @assertEqual component.componentParent(), parentComponent
-    @assertEqual parentComponent.componentChildren(), [component]
-    @assertEqual parentComponent.componentChildren(component), [component]
-    @assertEqual parentComponent.componentChildren(parentComponent), []
-    @assertEqual parentComponent.componentChildren(DummyComponent), [component]
-    @assertEqual parentComponent.componentChildren(UnregisteredComponent), []
-    @assertEqual parentComponent.componentChildren('DummyComponent'), [component]
-    @assertEqual parentComponent.componentChildren('UnregisteredComponent'), []
-    @assertEqual parentComponent.componentChildrenWith('field'), [component]
-    @assertEqual parentComponent.componentChildrenWith('fieldValue'), [component]
-    @assertEqual parentComponent.componentChildrenWith('nonexisting'), []
-    @assertEqual parentComponent.componentChildrenWith(field: 'foobar'), [component]
-    @assertEqual parentComponent.componentChildrenWith(field: 'faabar'), []
-    @assertEqual parentComponent.componentChildrenWith(fieldValue: 'foobar'), [component]
-    @assertEqual parentComponent.componentChildrenWith(fieldValue: 'faabar'), []
+      resultsWith.push parentComponent.childrenComponentsWith(field: 'foobar')
+
+    component.parentComponent parentComponent
+    parentComponent.addChildComponent component
+
+    @assertEqual component.parentComponent(), parentComponent
+    @assertEqual parentComponent.childrenComponents(), [component]
+    @assertEqual parentComponent.childrenComponents(component), [component]
+    @assertEqual parentComponent.childrenComponents(parentComponent), []
+    @assertEqual parentComponent.childrenComponents(DummyComponent), [component]
+    @assertEqual parentComponent.childrenComponents(UnregisteredComponent), []
+    @assertEqual parentComponent.childrenComponents('DummyComponent'), [component]
+    @assertEqual parentComponent.childrenComponents('UnregisteredComponent'), []
+    @assertEqual parentComponent.childrenComponentsWith('field'), [component]
+    @assertEqual parentComponent.childrenComponentsWith('fieldValue'), [component]
+    @assertEqual parentComponent.childrenComponentsWith('nonexisting'), []
+    @assertEqual parentComponent.childrenComponentsWith(field: 'foobar'), [component]
+    @assertEqual parentComponent.childrenComponentsWith(field: 'faabar'), []
+    @assertEqual parentComponent.childrenComponentsWith(fieldValue: 'foobar'), [component]
+    @assertEqual parentComponent.childrenComponentsWith(fieldValue: 'faabar'), []

     self = @

-    @assertEqual parentComponent.componentChildrenWith(
+    @assertEqual parentComponent.childrenComponentsWith(
       (child) ->
         self.assertEqual @, parentComponent
         self.assertEqual child, component
         true
     ), [component]
-    @assertEqual parentComponent.componentChildrenWith((child) -> false), []
+    @assertEqual parentComponent.childrenComponentsWith((child) -> false), []

     Tracker.flush()

-    component.componentParent null
-    parentComponent.removeComponentChild component
-
-    @assertEqual component.componentParent(), null
-    @assertEqual parentComponent.componentChildren(), []
-    @assertEqual parentComponent.componentChildren(component), []
-    @assertEqual parentComponent.componentChildren(DummyComponent), []
-    @assertEqual parentComponent.componentChildren(UnregisteredComponent), []
-    @assertEqual parentComponent.componentChildren('DummyComponent'), []
-    @assertEqual parentComponent.componentChildren('UnregisteredComponent'), []
-    @assertEqual parentComponent.componentChildrenWith('field'), []
-    @assertEqual parentComponent.componentChildrenWith('fieldValue'), []
-    @assertEqual parentComponent.componentChildrenWith('nonexisting'), []
-    @assertEqual parentComponent.componentChildrenWith(field: 'foobar'), []
-    @assertEqual parentComponent.componentChildrenWith(field: 'faabar'), []
-    @assertEqual parentComponent.componentChildrenWith(fieldValue: 'foobar'), []
-    @assertEqual parentComponent.componentChildrenWith(fieldValue: 'faabar'), []
+    component.parentComponent null
+    parentComponent.removeChildComponent component
+
+    @assertEqual component.parentComponent(), null
+    @assertEqual parentComponent.childrenComponents(), []
+    @assertEqual parentComponent.childrenComponents(component), []
+    @assertEqual parentComponent.childrenComponents(DummyComponent), []
+    @assertEqual parentComponent.childrenComponents(UnregisteredComponent), []
+    @assertEqual parentComponent.childrenComponents('DummyComponent'), []
+    @assertEqual parentComponent.childrenComponents('UnregisteredComponent'), []
+    @assertEqual parentComponent.childrenComponentsWith('field'), []
+    @assertEqual parentComponent.childrenComponentsWith('fieldValue'), []
+    @assertEqual parentComponent.childrenComponentsWith('nonexisting'), []
+    @assertEqual parentComponent.childrenComponentsWith(field: 'foobar'), []
+    @assertEqual parentComponent.childrenComponentsWith(field: 'faabar'), []
+    @assertEqual parentComponent.childrenComponentsWith(fieldValue: 'foobar'), []
+    @assertEqual parentComponent.childrenComponentsWith(fieldValue: 'faabar'), []

     Tracker.flush()

     componentZoobar = new DummyComponent 'zoobar'

-    componentZoobar.componentParent parentComponent
-    parentComponent.addComponentChild componentZoobar
+    componentZoobar.parentComponent parentComponent
+    parentComponent.addChildComponent componentZoobar

     Tracker.flush()


detect and show prefix errors

detect and show prefix errors

--- tree2.coffee
+++ tree2.coffee
@@ -65,6 +65,7 @@
 link_style = stroke: 'white', width: 1.5
 light_link_style = stroke: 'white', width: .5
 node_text_style = fill: 'white', font: '16px monospace'
+node_text_error_style = fill: 'red', font: '16px monospace'
 node_text_spacing = 18
 menu_text_style = fill: 'white', font: '16px sans'
 menu_text_invert_style = fill: 'black', font: '16px sans'
@@ -296,7 +297,10 @@

   render: (ctx) ->
     ctx.save()
-    setStyle(ctx, node_text_style)
+    if @error?
+      setStyle(ctx, node_text_error_style)
+    else
+      setStyle(ctx, node_text_style)
     ctx.textAlign = 'left'
     ctx.textBaseline = 'top'
     msg = "#{@symbol} = #{@code}"
@@ -901,6 +905,7 @@
   constructor: (shape) ->
     super shape
     @codes = {}
+    @errors = false

   addCodes: (codes, pos) ->
     for symbol, code of codes
@@ -926,12 +931,38 @@
       @nodes.push(newval)

     @codes[symbol] = newval
-    # TODO: needs to check for errors, I think this should be done
-    # here even if it is less efficient just to be safe
+
+    @checkForErrors()
+
+  checkForErrors: () ->
+    @errors = false
+
+    for n in @nodes
+      n.error = null
+
+    for symbol, codeobj of @codes
+      ok = true
+
+      for symbol2, codeobj2 of @codes
+        if symbol == symbol2
+          continue
+        if codeobj2.code.indexOf(codeobj.code) == 0
+          ok = false
+          @errors = true
+          # codeobj appears in codeobj2
+          err = (start: 0, end: codeobj.code.length)
+          if codeobj2.error?
+            codeobj2.error.push(err)
+          else
+            codeobj2.error = [err]
+      if not ok
+        codeobj.error = [(start: 0, end: codeobj.code.length)]
+
+    return

   arrangeCodes: (start_x, start_y) ->
     y = start_y
-    for symbol, codeobj of @nodes
+    for symbol, codeobj of @codes
       codeobj.x = start_x
       codeobj.y = y
       y += node_text_spacing


further fix indentation

further fix indentation

--- src/ui/views/messages.coffee
+++ src/ui/views/messages.coffee
@@ -207,7 +207,7 @@
     div class: 'sender-wrapper', ->
         a href:linkto(u.cid), title: sender, {onclick}, class:'sender', ->
             drawAvatar(u.cid, viewstate, entity)
-      span sender
+        span sender

 groupEventsByMessageType = (event) ->
     res = []


Remove the added console log

Remove the added console log

--- packages/rocketchat-message-pin/client/actionButton.coffee
+++ packages/rocketchat-message-pin/client/actionButton.coffee
@@ -36,6 +36,5 @@
 			if RocketChat.settings.get('Message_AllowPinningByAnyone') or RocketChat.authz.hasRole Meteor.userId(), 'admin'
 				return true

-			console.log 'UNpin-message', '---- is user the room owner', room.u?._id is Meteor.userId()
 			return room.u?._id is Meteor.userId()
 		order: 21


Fix some typos and grammar mistakes

Fix some typos and grammar mistakes

--- t9n/uk.coffee
+++ t9n/uk.coffee
@@ -15,7 +15,7 @@
   dontHaveAnAccount: "Немає аккаунта?"
   email: "Email"
   emailAddress: "Email"
-  emailResetLink: "Надіслати посилання для вілновлення"
+  emailResetLink: "Отримати посилання для оновлення паролю"
   forgotPassword: "Забули пароль?"
   ifYouAlreadyHaveAnAccount: "Якщо у вас вже є аккаунт:"
   newPassword: "Новий пароль"
@@ -24,7 +24,7 @@
   OR: "АБО"
   password: "Пароль"
   passwordAgain: "Пароль (ще раз)"
-  privacyPolicy: "Політики безопеки"
+  privacyPolicy: "Політики безпеки"
   remove: "Видалити"
   resetYourPassword: "Відновити пароль"
   setPassword: "Встановити пароль"
@@ -55,7 +55,7 @@
     pwdsDontMatch: "Паролі не співпадають"
     pwOneDigit: "Пароль повинен містити хоча б одну цифру."
     pwOneLetter: "Пароль повинен містити хоча б одну букву."
-    signInRequired: "Для продовження нелбхідно ввійти."
+    signInRequired: "Для продовження необхідно увійти."
     signupCodeIncorrect: "Невірний реєстраційний код."
     signupCodeRequired: "Необхідний реєстраційний код."
     usernameIsEmail: "Ім’я користувача не може бути email адресою."
@@ -73,7 +73,7 @@
       "Login forbidden": "Вхід заборонено"
       #"Service " + options.service + " already configured":
       "Service unknown": "Невідомий сервіс"
-      "Unrecognized options for login request": "Невідомі параметрм для запиту входу"
+      "Unrecognized options for login request": "Невідомі параметри для запиту входу"
       "User validation failed": "Перевірка користувача не вдалася"
       "Username already exists.": "Користувач існує."
       "You are not logged in.": "Ви не ввійшли."
@@ -86,13 +86,13 @@

       #---- accounts-password-client
-      "Password is old. Please reset your password.": "Пароль застарів. Будь ласка скиньте Ваш пароль."
+      "Password is old. Please reset your password.": "Пароль застарів. Будь ласка, скиньте Ваш пароль."

       #---- accounts-password
       "Incorrect password": "Невірний пароль"
       "Invalid email": "Неіснуючий Email"
-      "Must be logged in": "Необхідно ввійти"
+      "Must be logged in": "Необхідно увійти"
       "Need to set a username or email": "Необхідно ім’я користувача або email"
       "old password format": "старий формат паролю"
       "Password may not be empty": "Пароль не може бути пустим"
@@ -100,7 +100,7 @@
       "Token expired": "Час дії токена вичерпано"
       "Token has invalid email address": "Невірна email адреса для токена"
       "User has no password set": "У користувача не встановлено пароль"
-      "User not found":  "Користувач не знайден"
+      "User not found":  "Користувач не знайдений"
       "Verify email link expired": "Посилання підтвердження email застаріло"
       "Verify email link is for unknown address": "Посилання підтвердження email для невідомої адреси"


fixed an issue with scatter plots always using a discrete axis

--- src/viz/types/scatter.coffee
+++ src/viz/types/scatter.coffee
@@ -38,9 +38,9 @@
 scatter.requirements = ["data", "x", "y"]
 scatter.scale = 1.1
 scatter.setup = (vars) ->
-  unless vars.axes.discrete
-    axis = if vars.time.value is vars.y.value then "y" else "x"
-    vars.self[axis] scale: "discrete"
+  if vars.time.value and !vars.axes.discrete
+    vars.self.x scale: "discrete" if vars.time.value is vars.x.value
+    vars.self.y scale: "discrete" if vars.time.value is vars.y.value
 scatter.shapes = ["circle", "square", "donut"]
 scatter.tooltip = "static"


Update mine bullet sizes

Update mine bullet sizes

--- src/lazerscripts/stage1/stage1boss.coffee
+++ src/lazerscripts/stage1/stage1boss.coffee
@@ -526,8 +526,8 @@
         projectile = Crafty.e('Sphere, Hostile, Projectile')
           .blink()
           .attr(
-            w: 14
-            h: 14
+            w: 20
+            h: 20
             speed: 400
             damage: 1
           )
@@ -1151,8 +1151,8 @@
         projectile = Crafty.e('Sphere, Hostile, Projectile')
           .blink()
           .attr(
-            w: 14
-            h: 14
+            w: 20
+            h: 20
             speed: 400
             damage: 1
           )


fix:usr: Remove click handler from clickable panel heading once clicked

--- app/assets/javascripts/activeadmin-async_panel.js.coffee
+++ app/assets/javascripts/activeadmin-async_panel.js.coffee
@@ -41,6 +41,7 @@
     registerHandler = ->
       item.addClass('clickable')
       $('h3', item).on 'click', ->
+        $('h3', item).off('click')
         item.removeClass('clickable')
         worker()


Don't display buttons unless they need to be.

--- app/assets/javascripts/backend/bank_statements.js.coffee
+++ app/assets/javascripts/backend/bank_statements.js.coffee
@@ -287,21 +287,25 @@
     _showOrHideNewPaymentButtons: ->
       selectedBankStatements = @_bankStatementLines().filter(".selected")
       selectedJournalItems   = @_journalEntryLines().filter(".selected")
-      if selectedBankStatements.length > 0 or selectedJournalItems.length > 0
+      if selectedBankStatements.length > 0
         @_updateIdsInButtons()
-
-        if selectedBankStatements.length > 0
-          $("a.from-selected-bank").show()
-          $("a.from-selected-bank").parents('.btn-group').show()
-
+        $("a.from-selected-bank").show()
+        $("a.from-selected-bank").parents('.btn-group').show()
+      else
+        $("a.from-selected-bank").hide()
+        $("a.from-selected-bank").parents('.btn-group').hide()
+
+      if selectedJournalItems.length > 0
+        @_updateIdsInButtons()
         $("a.from-selected-journal").show()
         $("a.from-selected-journal").parents('.btn-group').show()
       else
-        $("a.from-selected-bank").hide()
         $("a.from-selected-journal").hide()
-
-        $("a.from-selected-bank").parents('.btn-group').hide()
         $("a.from-selected-journal").parents('.btn-group').hide()
+
+      unless selectedBankStatements.length > 0 and selectedJournalItems.length > 0
+        $("a.from-selected-journal.from-selected-bank").hide()
+        $("a.from-selected-journal.from-selected-bank").parents('.btn-group').hide()

     _showOrHideReconciliatedLines: ->
       if $("#hide-lettered").is(":checked")


Remove testing code

Remove testing code

--- app/assets/javascripts/models/page/search_page.js.coffee
+++ app/assets/javascripts/models/page/search_page.js.coffee
@@ -31,7 +31,7 @@
       @query = new QueryModel()

       # For testing GIBS Visualizations
-      @query = new QueryModel(echo_collection_id: ['C14758250-LPDAAC_ECS', 'C1000000016-LANCEMODIS', 'C1000000019-LANCEMODIS', 'C90757596-LAADS'])
+      #@query = new QueryModel(echo_collection_id: ['C14758250-LPDAAC_ECS', 'C1000000016-LANCEMODIS', 'C1000000019-LANCEMODIS', 'C90757596-LAADS'])

       @user = new UserModel()
       @datasets = new DatasetsModel(@query)


OX-698 First primitive version of per-node color

--- app/assets/javascripts/oxalis/geometries/skeleton.coffee
+++ app/assets/javascripts/oxalis/geometries/skeleton.coffee
@@ -45,7 +45,10 @@
     @branchesBuffer = new ResizableBuffer(3)
     @branchesColorsBuffer = new ResizableBuffer(3)

+    @reset()
+
     @updateBranches()
+    @updateNodes()

     @cellTracing.on
       newActiveNode : =>
@@ -59,11 +62,15 @@
       mergeTree : (lastTreeID, lastNode, activeNode) =>
         @mergeTree(lastTreeID, lastNode, activeNode)
         @updateBranches()
+        @updateNodes()
       newNode : (centered) => @setWaypoint(centered)
       setBranch : (isBranchPoint, nodeID) =>
         @setBranchPoint(isBranchPoint, nodeID)
         @updateBranches()
-      deleteBranch : => @updateBranches()
+        @updateNodes()
+      deleteBranch : =>
+        @updateBranches()
+        @updateNodes()
       reloadTrees : (trees, finishedDeferred) =>
         @cellTracing.one("finishedRender", =>
           @cellTracing.one("finishedRender", =>
@@ -75,8 +82,6 @@
     @model.user.on "particleSizeChanged", (particleSize) =>
       @setParticleSize(particleSize)

-    @reset()
-

   createNewTree : (treeId, treeColor) ->
     # create cellTracing to show in TDView and pre-allocate buffers
@@ -99,9 +104,10 @@
     @nodes.push(new THREE.ParticleSystem(
       nodeGeometry,
       new THREE.ParticleBasicMaterial({
-        color: @darkenHex(treeColor),
-        size: @model.user.particleSize,
+        size: @model.user.particleSize,
+        vertexColors: true,
         sizeAttenuation : @mode == constants.MODE_ARBITRARY})))
+    @nodesColorBuffers.push( new ResizableBuffer(3) )

     @ids.push(treeId)

@@ -125,11 +131,12 @@
         threeParticleSystem.geometry.dispose()
         threeParticleSystem.material.dispose()

-    @edges         = []
-    @nodes         = []
-    @ids           = []
-    @edgesBuffers  = []
-    @nodesBuffers  = []
+    @edges             = []
+    @nodes             = []
+    @ids               = []
+    @edgesBuffers      = []
+    @nodesBuffers      = []
+    @nodesColorBuffers = []

     for tree in @cellTracing.getTrees()
       @createNewTree(tree.treeId, tree.color)
@@ -167,6 +174,7 @@

       @updateGeometries(index)
     @updateBranches()
+    @updateNodes()

     @setActiveNode()

@@ -190,6 +198,7 @@
       @activeNodeParticle.position = new THREE.Vector3(position[0] + 0.02, position[1] + 0.02, position[2] - 0.02)
     else
       @activeNodeParticle.visible = false
+    @updateNodes()
     @flycam.update()

@@ -230,12 +239,13 @@
     @edges[index].material.needsUpdate = true

     @updateBranches()
+    @updateNodes()
     @setActiveNode()

   getMeshes : =>

-    return [@activeNodeParticle].concat(@nodes).concat(@edges).concat(@branches)
+    return @nodes.concat(@edges).concat(@branches)

   setWaypoint : (centered) =>
@@ -382,6 +392,29 @@
     @flycam.update()

+  updateNodes : ->
+
+    activeNodeId = @cellTracing.getActiveNodeId()
+
+    for i in [0...@nodes.length]
+
+      tree = @cellTracing.getTree( @ids[i] )
+
+      @nodesColorBuffers[i].clear()
+      for node in tree.nodes
+        if node.id == activeNodeId
+          @nodesColorBuffers[i].push( @invertHexToRGB( 0xffff00 ))
+        else
+          @nodesColorBuffers[i].push( @invertHexToRGB( 0xff0000 ))
+
+      @nodes[i].geometry.__colorArray = @nodesColorBuffers[i].getBuffer()
+      console.log @nodes[i].geometry.__colorArray
+      @nodes[i].geometry.colorsNeedUpdate = true
+
+      @updateGeometries( i )
+      @flycam.update()
+
+
   getIndexFromTreeId : (treeId) ->

     unless treeId


Fix missing branch points

Fix missing branch points

--- app/assets/javascripts/oxalis/model/tracingparser.coffee
+++ app/assets/javascripts/oxalis/model/tracingparser.coffee
@@ -83,7 +83,9 @@

     @buildTrees()

-    nodeList = @celltracing.getNodeListOfAllTrees()
+    nodeList = []
+    for tree in @trees
+      nodeList = nodeList.concat(tree.nodes)

     @setBranchpoints(nodeList)
     @setComments(nodeList)


OX-543 Add performance test

OX-543 Add performance test

--- app/assets/javascripts/oxalis/model/volumetracing.coffee
+++ app/assets/javascripts/oxalis/model/volumetracing.coffee
@@ -45,8 +45,10 @@
     unless @currentLayer?
       return

+    start = (new Date()).getTime()
     iterator = @currentLayer.getVoxelIterator()
     @cube.labelVoxels(iterator, @activeCell.id % 6 + 1)
+    console.log "Labeling time:", ((new Date()).getTime() - start)

     @currentLayer = null
     @flycam.hasChanged = true


Visual/edit modes: better word-movement/"w".

--- content_scripts/mode_visual_edit.coffee
+++ content_scripts/mode_visual_edit.coffee
@@ -69,6 +69,31 @@

   paste: (callback) ->
     chrome.runtime.sendMessage handler: "pasteFromClipboard", (response) -> callback response
+
+  # Return a value which changes whenever the selection changes, regardless of whether the selection is
+  # collapsed or not.
+  hashSelection: ->
+    [ @element?.selectionStart, @selection.toString().length ].join "/"
+
+  # Call a function.  Return true if the selection changed as a side effect, false otherwise.
+  selectionChanged: (func) ->
+    before = @hashSelection()
+    func()
+    console.log before, @hashSelection()
+    @hashSelection() != before
+
+  # Run a movement.  The single movement argument can be a string of the form "direction amount", e.g.
+  # "forward word", or a list, e.g. [ "forward", "word" ].
+  runMovement: (movement) ->
+    movement = movement.split(" ") if typeof movement == "string"
+    console.log movement.join " "
+    @selection.modify @alterMethod, movement...
+
+  # Run a sequence of movements, stopping if a movement fails to change the selection.
+  runMovements: (movements...) ->
+    for movement in movements
+      return false unless @selectionChanged => @runMovement movement
+    true

   # Swap the anchor node/offset and the focus node/offset.
   reverseSelection: ->
@@ -91,12 +116,6 @@
       which = if direction == forward then "start" else "end"
       @selection.extend original["#{which}Container"], original["#{which}Offset"]

-  # Run a movement command.  The single movement argument can be a string of the form "direction amount", e.g.
-  # "forward word", or a list, e.g. [ "forward", "word" ].
-  runMovement: (movement) ->
-    movement = movement.split(" ") if typeof movement == "string"
-    @selection.modify @alterMethod, movement...
-
   # Try to move one character in "direction".  Return 1, -1 or 0, indicating whether the selection got bigger,
   # or smaller, or is unchanged.
   moveInDirection: (direction) ->
@@ -115,9 +134,13 @@
         return if 0 < success then direction else @opposite[direction]
     backward

-  # An approximation of the vim "w" movement.
-  moveForwardWord: (direction) ->
-    @runMovement movement for movement in [ "forward word", "forward word", "backward word" ]
+  # An approximation of the vim "w" movement; only ever used in the forward direction.  The extra character
+  # movements at the end allow us to also get to the end of the very-last word.
+  moveForwardWord: () ->
+    # First, move to the end of the preceding word...
+    if @runMovements "forward character", "backward word", "forward word"
+      # And then to the start of the following word...
+      @runMovements "forward word", "forward character", "backward character", "backward word"

   collapseSelection: ->
     if 0 < @selection.toString().length


Improve doc on types

Improve doc on types

--- lib/Schema.coffee
+++ lib/Schema.coffee
@@ -5,6 +5,21 @@
 Schema
 ======
 Define a new schema.
+
+`ron`               Reference to the Ron instance
+
+`options`           Schema definition. Options include:
+
+*   `name`          Name of the schema.
+*   `properties`    Properties definition, an object or an array.
+
+Record properties may be defined by the following keys:
+
+*   `type`          Use to cast the value inside Redis, one of `string`, `int` or `date`.
+*   `identifier`    Mark this property as the identifier, only one property may be an identifier.
+*   `index`         Create an index on the property.
+*   `unique`        Create a unique index on the property.
+*   `email`         Validate the string as an email.

 Sample
 ------


Don't use override_computed_property() in HelpTool

--- bokehjs/src/coffee/models/tools/actions/help_tool.coffee
+++ bokehjs/src/coffee/models/tools/actions/help_tool.coffee
@@ -14,19 +14,19 @@
   icon: "bk-tool-icon-help"

   @define {
-      help_tooltip: [
-        p.String,
-        'Click the question mark to learn more about Bokeh plot tools.'
-      ]
-      redirect:     [
-        p.String
-        'http://bokeh.pydata.org/en/latest/docs/user_guide/tools.html'
-      ]
-    }
+    help_tooltip: [
+      p.String,
+      'Click the question mark to learn more about Bokeh plot tools.'
+    ]
+    redirect:     [
+      p.String
+      'http://bokeh.pydata.org/en/latest/docs/user_guide/tools.html'
+    ]
+  }

-  initialize: (attrs, options) ->
-    super(attrs, options)
-    @override_computed_property('tooltip', () ->@get('help_tooltip'))
+  @getters {
+    tooltip: () -> @help_tooltip
+  }

 module.exports =
   Model: HelpTool,


Add debugging information to error message test (#5239)

One of the test cases in test/error_messages.coffee fails intermittently
in the Node.js ecosystem-testing tool CITGM. In an effort to help debug
what's going on when this occurs, this adds more information to the
AssertionError message in question.

--- test/error_messages.coffee
+++ test/error_messages.coffee
@@ -88,7 +88,7 @@

     eq error.message, 'hello world'
     doesNotThrow(-> error.stack)
-    notEqual error.stack.toString().indexOf(filePath), -1
+    notEqual error.stack.toString().indexOf(filePath), -1, "Expected " + filePath + "in stack trace: " + error.stack.toString()

   test "#4418: stack traces for compiled files reference the correct line number", ->
     # The browser is already compiling other anonymous scripts (the tests)


Improved fetching of records (race conditions).

--- app/assets/javascripts/app/models/_application_model.js.coffee
+++ app/assets/javascripts/app/models/_application_model.js.coffee
@@ -106,25 +106,41 @@
       if callback
         callback( data )
       return data
+
+    if force
+      console.log 'debug', 'find forced to load!', @className, id
     else
-      if force
-        console.log 'debug', 'find forced to load!', @className, id
-      else
-        console.log 'debug', 'find not loaded!', @className, id
-      if callback
-
-        # execute callback if record got loaded
-        App[ @className ].one 'refresh', (records) ->
+      console.log 'debug', 'find not loaded, load now!', @className, id
+    if callback
+
+      # store callback and requested id
+      if !@RETRIEVE_CALLBACK
+        @RETRIEVE_CALLBACK = {}
+      if !@RETRIEVE_CALLBACK[id]
+        @RETRIEVE_CALLBACK[id] = {}
+      key = @className + '-' + Math.floor( Math.random() * 99999 )
+      @RETRIEVE_CALLBACK[id][key] = callback
+
+      # bind refresh event
+      if !@RETRIEVE_BIND
+        @RETRIEVE_BIND = true
+
+        # check if bind for requested id exists
+        App[ @className ].bind 'refresh', (records) ->
           for record in records
-            if record.id.toString() is id.toString()
-              data = App[ @className ].find( record.id )
-              callback( data )
-
-        # fetch object
-        console.log 'debug', 'loading..' + @className +  '..', id
-        App[ @className ].fetch( id: id )
-        return true
-      return false
+            if @RETRIEVE_CALLBACK[ record.id ]
+              for key, callback of @RETRIEVE_CALLBACK[ record.id ]
+                data = App[ @className ].find( record.id )
+                callback( data )
+                delete @RETRIEVE_CALLBACK[ record.id ][ key ]
+              if _.isEmpty @RETRIEVE_CALLBACK[ record.id ]
+                delete @RETRIEVE_CALLBACK[ record.id ]
+
+      # fetch object
+      console.log 'debug', 'loading..' + @className +  '..', id
+      App[ @className ].fetch( id: id )
+      return true
+    return false

   @subscribe: (callback, param = {}) ->
     if !@SUBSCRIPTION_COLLECTION
@@ -134,8 +150,8 @@
       @bind(
         'refresh change'
         =>
-          for key, callbackSingle of @SUBSCRIPTION_COLLECTION
-            callbackSingle()
+          for key, callback of @SUBSCRIPTION_COLLECTION
+            callback()
       )

       # trigger deleteAll() and fetch() on network notify
@@ -144,16 +160,10 @@
         events
         =>
           @deleteAll()
-#          callbacks = =>
-#            for key, callbackSingle of @SUBSCRIPTION_COLLECTION
-#              callbackSingle()
-#          @one 'refresh', (collection) =>
-#            callbacks(collection)
           @fetch()

         'Collection::Subscribe::' + @className
       )
-

     key = @className + '-' + Math.floor( Math.random() * 99999 )
     @SUBSCRIPTION_COLLECTION[key] = callback
@@ -167,26 +177,26 @@
     return key

   subscribe: (callback) ->
+
+    # init bind
     if !App[ @constructor.className ]['SUBSCRIPTION_ITEM']
       App[ @constructor.className ]['SUBSCRIPTION_ITEM'] = {}
-    if !App[ @constructor.className ]['SUBSCRIPTION_ITEM'][@id]
-      App[ @constructor.className ]['SUBSCRIPTION_ITEM'][@id] = {}

       events = "#{@constructor.className}:created #{@constructor.className}:updated #{@constructor.className}:destroy"
       App.Event.bind(
         events
         (record) =>
           if @id.toString() is record.id.toString()
-            App[ @constructor.className ].one 'refresh', (record) =>
-              user = App[ @constructor.className ].find(@id)
-              for key, callback of App[ @constructor.className ]['SUBSCRIPTION_ITEM'][@id]
-                callback(user)
-            App[ @constructor.className ].fetch( id: @id )
+            for key, callback of App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ @id ]
+              App[ @constructor.className ].retrieve( @id, callback, true )
         'Item::Subscribe::' + @constructor.className
       )

+    # remember record id and callback
+    if !App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ @id ]
+      App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ @id ] = {}
     key = @constructor.className + '-' + Math.floor( Math.random() * 99999 )
-    App[ @constructor.className ]['SUBSCRIPTION_ITEM'][@id][key] = callback
+    App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ @id ][key] = callback
     return key

   @unsubscribe: (data) ->


add metrics support

add metrics support

--- lib/browse.coffee
+++ lib/browse.coffee
@@ -28,11 +28,17 @@
     @subscriptions.add atom.commands.add "atom-workspace", "#{name}:reveal-file-from-treeview": => @revealFileFromTreeview()
     @subscriptions.add atom.commands.add "atom-workspace", "#{name}:application-folder": => @appFolder()

+    require("./ga").sendEvent("browse", "activate");
+
   deactivate: ->
+    require("./ga").sendEvent("browse", "deactivate");
+
     @subscriptions?.dispose()
     @subscriptions = null

   appFolder: ->
+    require("./ga").sendEvent("browse", "application-folder");
+
     { platform } = require "os"
     { dirname, join, resolve } = require "path"

@@ -48,6 +54,8 @@
     @openFolder(appFolder)

   browsePackages: ->
+    require("./ga").sendEvent("browse", "packages-folder");
+
     { accessSync, F_OK } = require "fs"

     packageDirs = atom.packages.getPackageDirPaths()
@@ -63,11 +71,13 @@
       @openFolder(packageDir)

   revealFile: ->
+    require("./ga").sendEvent("browse", "reveal-file");
+
     editor = atom.workspace.getActivePaneItem()

     if editor?.constructor.name is "TextEditor" or editor?.constructor.name is "ImageEditor"
       file = if editor?.buffer?.file then editor.buffer.file else if editor?.file then editor.file
-
+
       if file?.path
         @selectFile(file.path)
         return
@@ -75,6 +85,8 @@
     atom.notifications.addWarning("**#{name}**: No active file", dismissable: false)

   revealFiles: ->
+    require("./ga").sendEvent("browse", "reveal-all-open-files");
+
     editors = atom.workspace.getPaneItems()

     if editors.length > 0
@@ -92,8 +104,9 @@

     atom.notifications.addWarning("**#{name}**: No open files", dismissable: false)

-
   revealFileFromTreeview: ->
+    require("./ga").sendEvent("browse", "reveal-file-from-treeview");
+
     panes = atom.workspace.getPaneItems()

     if panes.length > 0
@@ -112,6 +125,8 @@
     atom.notifications.addWarning("**#{name}**: No selected files", dismissable: false)

   browseProjects: ->
+    require("./ga").sendEvent("browse", "project-folders");
+
     { accessSync, F_OK } = require "fs"

     projects = atom.project.getPaths()
@@ -133,6 +148,8 @@
       @openFolder(project)

   browseConfig: ->
+    require("./ga").sendEvent("browse", "configuration-folder");
+
     { accessSync, F_OK } = require "fs"
     { dirname } = require "path"

@@ -151,6 +168,8 @@
       @openFolder(configPath)

   selectFile: (path) ->
+    require("./ga").sendEvent("browse", "configuration-folder");
+
     { basename } = require "path"

     # Custom file manager


refactor, now selection gets reset, if filter changes

--- app/assets/javascripts/components/kid_mentor_schedules.js.cjsx
+++ app/assets/javascripts/components/kid_mentor_schedules.js.cjsx
@@ -8,6 +8,7 @@

 @KidMentorSchedules = React.createClass
   getInitialState: ->
+    mentorsToDisplay: _.keys @props.mentors
     filters:
       ect: null
       sex: null
@@ -19,12 +20,23 @@
         delete filteredMentors[id] if mentor.ects isnt @state.filters.ects
       if @state.filters?.sex?
         delete filteredMentors[id] if mentor.sex isnt @state.filters?.sex
+    total = _.size filteredMentors
+    for mentor, index in _.values filteredMentors
+      mentor.colors = @getColorsOfMentor total, index
     return filteredMentors
-
+  getSelectedMentors: (filteredMentors) ->
+    filteredMentors = _.pick filteredMentors, @state.mentorsToDisplay
+
+    return filteredMentors
+  onChangeSelectedMentorsToDisplay: (mentorIds) ->
+    @setState mentorsToDisplay: mentorIds
   onChangeFilter: (key, value) ->
     filters = @state.filters
     filters[key] = value
     @setState filters: filters
+    @selectAll()
+  selectAll: ->
+    @setState mentorsToDisplay: _.keys @props.mentors
   onSelectDate: (mentor, day, time) ->
     if confirm "Treffen vereinbaren?\n\n
       Schüler: #{@props.kid.prename} #{@props.kid.name}\n
@@ -32,9 +44,22 @@
       Zeitpunkt: #{day.label} um #{time.label}\n"
       alert "done"
       console.log mentor, day, time
+  getColorsOfMentor: (total, index) ->
+    # we rotate over the color circle to create a color for every mentor
+    # but we skip every second color so that mentors next to each other have not too similar colors
+    indexShifted = (index) ->
+      totalEven = total % 2 is 0
+      offset = if totalEven and index/total >= 0.5 then 1 else 0
+      (index*2+offset) % total
+
+    angle = 360 / total
+    hue = angle * indexShifted index
+    background: "hsla(#{hue}, 70%, 90%, 0.5)"
+    text: "hsl(#{hue}, 90%, 20%)"
+
   render: ->
     filteredMentors = @getFilteredMentors()
-    filteredMentorIds = _.keys filteredMentors
+    selectedMentors = @getSelectedMentors filteredMentors

       <Filters
@@ -42,12 +67,21 @@
         initialFilters=@state.filters
         onChange=@onChangeFilter
       />
-      <Table
-        mentors=filteredMentors
+

+        <MentorsForDisplayingFilter
+          mentors=filteredMentors
+          selection=@state.mentorsToDisplay
+          onChange=@onChangeSelectedMentorsToDisplay
+        />
+

+
+      <TimeTable
         kid=@props.kid
-        initialSelection=filteredMentorIds
+        mentors=selectedMentors
         onSelectDate=@onSelectDate
-        />
+      />
+
+

 MentorsForDisplayingFilter = React.createClass
@@ -57,6 +91,8 @@
       @props.onChange valuesAsString.split(@DELEMITER).map (id) -> parseInt id, 10
     else
       @props.onChange []
+  selectAll: ->
+    @props.onChange _.keys @props.mentors
   render: ->
     options = for id,mentor of @props.mentors
       label: "#{mentor.prename} #{mentor.name}"
@@ -65,8 +101,12 @@
         color: mentor.colors.text
         backgroundColor: mentor.colors.background
         borderColor: mentor.colors.text
-    value = (id for id of @props.initialSelection)
-    .join ";"
+    selectedIds = []
+    for id in @props.selection
+      selectedIds.push id if @props.mentors[id]?
+    value = selectedIds.join @DELEMITER
+
+
     if value.length == 0 then value = null

       <Select
@@ -76,6 +116,11 @@
         value=value
         onChange=@onChange
       />
+       <button
+        onClick=@selectAll
+        className="btn btn-default col-xs-2">
+          Select All ({_.size @props.mentors})
+      </button>

@@ -106,58 +151,6 @@

-Table = React.createClass
-  getInitialState: ->
-    mentorsToDisplay: @props.initialSelection
-  onChange: (mentors) ->
-    @setState mentorsToDisplay: mentors
-  getColorsOfMentor: (index) ->
-    total = _.size @state.mentorsToDisplay
-    # we rotate over the color circle to create a color for every mentor
-    # but we skip every second color so that mentors next to each other have not too similar colors
-    indexShifted = (index) ->
-      totalEven = total % 2 is 0
-      offset = if totalEven and index/total >= 0.5 then 1 else 0
-      (index*2+offset) % total
-
-    angle = 360 / total
-    hue = angle * indexShifted index
-    background: "hsla(#{hue}, 70%, 90%, 0.5)"
-    text: "hsl(#{hue}, 90%, 20%)"
-  getSelectedMentors: ->
-    mentors = {}
-    for id, index in @state.mentorsToDisplay
-      if @props.mentors[id]?
-        mentors[id] = @props.mentors[id]
-        mentors[id].colors = @getColorsOfMentor index
-    return mentors
-  selectAll: ->
-    @setState mentorsToDisplay: _.keys @props.mentors
-  render: ->
-    selectedMentors = @getSelectedMentors()
-
-

-

-        Selected {_.size selectedMentors} mentors of {_.size @props.mentors}
-

-

-        <MentorsForDisplayingFilter
-          mentors=@props.mentors
-          initialSelection=selectedMentors
-          onChange=@onChange
-        />
-

-      <button
-        onClick=@selectAll
-        className="btn btn-default col-xs-2">
-          Select All ({_.size @props.mentors})
-      </button>
-      <TimeTable
-        kid=@props.kid
-        mentors=selectedMentors
-        onSelectDate=@props.onSelectDate
-      />
-

 TimeTable = React.createClass
   createTimeArray: ->


Fix broken alert div not closing

Fix broken alert div not closing

--- app/assets/javascripts/plugins/awesome-share-buttons.js.coffee
+++ app/assets/javascripts/plugins/awesome-share-buttons.js.coffee
@@ -1,5 +1,5 @@
 $(document).on 'ready page:load page:restore', ->
-  $(document).foundation
+  $(document).foundation()

   $a = $('.awesome-share-buttons').find('a')
   $a.each (index, element) ->


fixed href

fixed href

--- app/assets/javascripts/views/items/item_on_list_view.js.coffee
+++ app/assets/javascripts/views/items/item_on_list_view.js.coffee
@@ -1,2 +1,6 @@
 Bandango.ItemOnListView = Bandango.ModelOnListView.extend
   templateName: "items/item_on_list"
+
+  href: (->
+    "#/categorias/#{@get("model.category.id")}/#{@get("model.id")}"
+  ).property()


Change options -> find_options and additional_options -> options

--- lib/clever.coffee
+++ lib/clever.coffee
@@ -20,7 +20,7 @@
     http_opts.headers ?= {}
     _(http_opts.headers).extend Authorization: "Bearer #{auth.token}"

-module.exports = (auth, url_base='https://api.clever.com', additional_options={}) ->
+module.exports = (auth, url_base='https://api.clever.com', options={}) ->
   throw new Error 'Must provide auth' if not auth
   auth = {api_key: auth} if _.isString auth
   clever =
@@ -99,7 +99,7 @@
         qs: _({where: @_conditions}).extend @_options
         json: true
         ca: certs
-      _(opts).extend(headers: additional_options.headers) if additional_options.headers
+      _(opts).extend(headers: options.headers) if options.headers
       apply_auth clever.auth, opts
       # convert stringify nested query params
       opts.qs[key] = JSON.stringify val for key, val of opts.qs when _(val).isObject()
@@ -119,7 +119,7 @@
         uri: @_uri
         json: @_values
         ca: certs
-      _(opts).extend(headers: additional_options.headers) if additional_options.headers
+      _(opts).extend(headers: options.headers) if options.headers
       apply_auth clever.auth, opts
       waterfall = [async.apply quest, opts].concat(@_post['exec'] or [])
       async.waterfall waterfall, cb
@@ -134,20 +134,20 @@
   class Resource
     @path: null

-    @_process_args: (conditions, fields, options={}, cb) ->
+    @_process_args: (conditions, fields, find_options={}, cb) ->
       if _(conditions).isFunction()
         cb = conditions
         conditions = {}
         fields = null
-        options = {}
+        find_options = {}
       else if _(fields).isFunction()
         cb = fields
         fields = null
-        options = {}
-      else if _(options).isFunction()
-        cb = options
-        options = {}
-      [conditions, fields, options, cb]
+        find_options = {}
+      else if _(find_options).isFunction()
+        cb = find_options
+        find_options = {}
+      [conditions, fields, find_options, cb]

     @_uri_to_class: (uri) ->
       klasses = _(clever).filter (val, key) -> val.path? # Filter out properties that aren't resources (e.g. api_path)
@@ -155,9 +155,9 @@
       throw new Error "Could not get type from uri: #{uri}, #{JSON.stringify klasses, undefined, 2}" if not Klass
       Klass

-    @find: (conditions, fields, options, cb) ->
-      [conditions, fields, options, cb] = @_process_args conditions, fields, options, cb
-      q = new Query "#{clever.url_base}#{@path}", conditions, options
+    @find: (conditions, fields, find_options, cb) ->
+      [conditions, fields, find_options, cb] = @_process_args conditions, fields, find_options, cb
+      q = new Query "#{clever.url_base}#{@path}", conditions, find_options
       q.select fields
       q.post 'exec', (resp, body, cb_post) =>
         q.paging = body.paging
@@ -173,21 +173,21 @@
       return q if not cb
       q.exec cb

-    @findOne: (conditions, fields, options, cb) ->
-      [conditions, fields, options, cb] = @_process_args conditions, fields, options, cb
-      _(options).extend {limit: 1}
+    @findOne: (conditions, fields, find_options, cb) ->
+      [conditions, fields, find_options, cb] = @_process_args conditions, fields, find_options, cb
+      _(find_options).extend {limit: 1}
       if not cb
-        q = @find conditions, fields, options
+        q = @find conditions, fields, find_options
         q.post 'exec', (results, cb_post) -> cb_post null, results[0]
         q
       else
-        @find conditions, fields, options, (err, docs) -> cb err, docs?[0]
-
-    @findById: (id, fields, options, cb) ->
+        @find conditions, fields, find_options, (err, docs) -> cb err, docs?[0]
+
+    @findById: (id, fields, find_options, cb) ->
       throw new Error 'must specify an ID for findById' unless _(id).isString()
       conditions = id: id
-      [conditions, fields, options, cb] = @_process_args conditions, fields, options, cb
-      @findOne conditions, fields, options, cb
+      [conditions, fields, find_options, cb] = @_process_args conditions, fields, find_options, cb
+      @findOne conditions, fields, find_options, cb

     constructor: (@_properties, @_uri, @_links) -> @_unsaved_values = {}

@@ -229,7 +229,7 @@
         uri: "#{clever.url_base}#{@constructor.path}/#{@_properties.id}/properties"
         json: obj
         ca: certs
-      _(opts).extend(headers: additional_options.headers) if additional_options.headers
+      _(opts).extend(headers: options.headers) if options.headers
       apply_auth clever.auth, opts
       if _(obj).isFunction()
         cb = obj


MainController: synclocalcontroller.coffee added to includes

--- client/Main/includes.coffee
+++ client/Main/includes.coffee
@@ -19,6 +19,7 @@
   "linkcontroller.coffee",
   "oauthcontroller.coffee",
   "widgetcontroller.coffee",
+  "synclocalcontroller.coffee",

   # COMMON VIEWS
   "CommonViews/applicationview/applicationtabview.coffee",


analytics: add more checks for existence of account profile

--- client/Main/mixpanel.coffee
+++ client/Main/mixpanel.coffee
@@ -5,6 +5,9 @@
     return  unless KD.isLoggedIn() and account and analytics

     {type, meta, profile} = account
+
+    return  unless profile
+
     {createdAt} = meta
     {firstName, lastName, nickname} = profile

@@ -29,7 +32,7 @@

   KD.gaEvent args[0]

-  args[1]["username"] = me.profile.nickname
+  args[1]["username"] = me?.profile?.nickname

   analytics.track args...


analytics: don't send email

analytics: don't send email

--- client/Main/mixpanel.coffee
+++ client/Main/mixpanel.coffee
@@ -4,23 +4,19 @@
   KD.getSingleton('mainController').on "AccountChanged", (account) ->
     return  unless KD.isLoggedIn() and account and analytics

-    account.fetchEmail (err, email)->
-      console.log err  if err
+    {type, meta, profile} = account
+    {createdAt} = meta
+    {firstName, lastName, nickname} = profile

-      {type, meta, profile} = account
-      {createdAt} = meta
-      {firstName, lastName, nickname} = profile
-
-      # register user to mixpanel
-      analytics.identify nickname, {
-        "$username"     : nickname
-        "$first_name"   : firstName
-        "$last_name"    : lastName
-        "$email"        : email
-        "$created"      : createdAt
-        "Status"        : type
-        "Randomizer"    : KD.utils.getRandomNumber 4
-      }
+    # register user to mixpanel
+    analytics.identify nickname, {
+      "$username"     : nickname
+      "$first_name"   : firstName
+      "$last_name"    : lastName
+      "$created"      : createdAt
+      "Status"        : type
+      "Randomizer"    : KD.utils.getRandomNumber 4
+    }

 # Access control wrapper around mixpanel object.
 KD.mixpanel = (args...)->
@@ -33,13 +29,9 @@

   KD.gaEvent args[0]

-  me.fetchEmail (err, email)->
-    console.log err  if err
+  args[1]["username"] = me.profile.nickname

-    args[1]["username"] = me.profile.nickname
-    args[1]["email"] = email
-
-    analytics.track args...
+  analytics.track args...

 KD.mixpanel.alias = (args...)->
   return  unless analytics and KD.config.logToExternal


Added check to ignore hidden system files that start with "." (such as .DS_Store on Mac)

--- lib/docpad.coffee
+++ lib/docpad.coffee
@@ -248,6 +248,10 @@
 				fullPath,
 				# File Action
 				(fileFullPath,fileRelativePath,next) ->
+                                         #Ignore hidden files in SRC directory (such as .DS_Store on Mac)
+					if path.basename(fileFullPath).startsWith('.')
+						console.log 'Skipping Hidden File:',fileFullPath
+						return next()
 					fs.stat fileFullPath, (err,fileStat) ->
 						throw err if err
 						parseFile fileFullPath,fileRelativePath,fileStat,(fileMeta) ->


Fix indentation

Fix indentation

--- lib/format.coffee
+++ lib/format.coffee
@@ -150,7 +150,7 @@
         # @editorCloseSubscriptions = new Observer()

   displayUnsupportedLanguageNotification: (language) ->
-   title = editor.getTitle()
+    title = editor.getTitle()
     ext = path.extname title
     message = ext.length > 0 ? ext : title
     atom.notifications.addWarning("Formatting '#{message}' files is not yet supported.", { dismissable: true });


Some performance improvements regarding beautify caching

--- lib/format.coffee
+++ lib/format.coffee
@@ -79,12 +79,15 @@
     opts.indent_size = editorSettings.tabLength
     opts.wrap_line_length = editorSettings.preferredLineLength

-    if @selectionsAreEmpty(editor)
-      editor.setText(jsbeautify(editor.getText(), opts))
+    beautifiedText = jsbeautify(editor.getText(), opts)

-    else
-      for selection in editor.getSelections()
-        selection.insertText(jsbeautify(selection.getText(), opts), {select:true})
+    if (editor.getText() != beautifiedText)
+      if @selectionsAreEmpty(editor)
+        editor.setText(jsbeautify(editor.getText(), opts))
+
+      else
+        for selection in editor.getSelections()
+          selection.insertText(jsbeautify(selection.getText(), opts), {select:true})

   selectionsAreEmpty: (editor) ->
     for selection in editor.getSelections()
@@ -110,7 +113,6 @@

             delete @editorSaveSubscriptions[editor.id]
             delete @editorCloseSubscriptions[editor.id]
-

           # saveSubscription = buffer.onWillSave =>
           #   buffer.transact =>


Timing of 'chosen:showing_dropdown' fixed

Currently the event is triggered before dropdown LI's
are created. This commit corrects it by moving the
trigger event at end of method. A JSFIDDLE was created
to demonstrate the issue.

http://jsfiddle.net/lagrz/3A5jq/

By moving the event it enables developers to add
whatever additional code or styling to the dropdown
menu options such as images.

--- coffee/chosen.jquery.coffee
+++ coffee/chosen.jquery.coffee
@@ -224,7 +224,6 @@
       return false

     @container.addClass "chosen-with-drop"
-    @form_field_jq.trigger("chosen:showing_dropdown", {chosen: this})

     @results_showing = true

@@ -232,6 +231,7 @@
     @search_field.val @search_field.val()

     this.winnow_results()
+    @form_field_jq.trigger("chosen:showing_dropdown", {chosen: this})

   update_results_content: (content) ->
     @search_results.html content


Remove unused variables

Remove unused variables

--- lib/imager.coffee
+++ lib/imager.coffee
@@ -1,9 +1,6 @@
 path        = require('path')
 gm          = require('gm')
 imageMagick = gm.subClass({ imageMagick: true })
-
-basePath      = path.join(__dirname, '..')
-generatedPath = path.join(basePath, '.generated')

 class Imager
   minSize: 40


Add try/catch

Add try/catch

--- lib/legacy.coffee
+++ lib/legacy.coffee
@@ -54,14 +54,17 @@
         temp.open(tmpOptions, (err, info) ->
           return reject(err) if err

-          fs.writeSync(info.fd, textEditor.getText())
-          fs.closeSync(info.fd)
+          try
+            fs.writeSync(info.fd, textEditor.getText())
+            fs.closeSync(info.fd)

-          linter.lintFile(info.path, (results) ->
-            fs.unlink(info.path)
+            linter.lintFile(info.path, (results) ->
+              fs.unlink(info.path)

-            resolve(transform(filePath, textEditor, results))
-          )
+              resolve(transform(filePath, textEditor, results))
+            )
+          catch error
+            reject(error)
         )
       )
   }


:racehorse: Defer linting until editor is properly loaded

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -41,8 +41,6 @@

     @subscriptions.add atom.config.observe 'linter.lintOnFly', (value) =>
       @lintOnFly = value
-    @subscriptions.add atom.project.onDidChangePaths =>
-      @commands.lint()

     @subscriptions.add atom.workspace.observeTextEditors (editor) => @createEditorLinter(editor)
     @commands.onShouldLint =>
@@ -53,6 +51,12 @@
         activeLinter.dispose()
       else
         @createEditorLinter(atom.workspace.getActiveTextEditor())
+
+    # Defer execution because onDidChangePaths is added on editor init,
+    # we don't want to lint until editor is initialized
+    setImmediate =>
+      @subscriptions.add atom.project.onDidChangePaths =>
+        @commands.lint()

   addUI: (ui) ->
     @ui.add(ui)


Get the version from package.json

Get the version from package.json

--- lib/malifi.coffee
+++ lib/malifi.coffee
@@ -14,6 +14,7 @@
 stripExtension= require('./strip_extension')
 Meta= require('./meta')
 action= require('./action')
+package = require('../package')
 extractHostAndPort= /([^:]+)(?::(.*))?/

 class ParseHost
@@ -67,4 +68,4 @@

 exports = module.exports = malifi

-exports.version = '0.0.1'
+exports.version = package.version


Rename grunt build action for extension_events

--- js-library/Gruntfile.coffee
+++ js-library/Gruntfile.coffee
@@ -103,7 +103,7 @@
         files: [
           { src: ['**/*.js', '**/*.png', '**/*.gif', 'robots.txt'], cwd: 'app', dest: 'build', expand: true }
         ]
-      start_stop_files:
+      extension_events:
         files: [
           { src: ['factlink.*.js'], cwd: 'build/js/extension_events', dest: 'build', expand: true }
         ]
@@ -160,7 +160,7 @@

   grunt.registerTask 'jail_iframe', []
   grunt.registerTask 'compile',  [
-    'clean', 'copy:build',  'copy:start_stop_files', 'coffee','copy:postFactlinkObject',
+    'clean', 'copy:build',  'copy:extension_events', 'coffee','copy:postFactlinkObject',
     'sass', 'cssUrlEmbed', 'cssmin',
     'concat', 'uglify', 'code_inliner',
     'shell:gzip_js_files', 'copy:dist'


fix(ngMockE2E): load when using backendless or running specs

--- tasks/scripts/scripts.coffee
+++ tasks/scripts/scripts.coffee
@@ -7,9 +7,9 @@
 	{onError, onRev, onScript} = require('../events') plugins
 	sources = do (ext ='.js') ->
 		SCRIPTS
-			.concat if not runSpecs       then ["!**/angular-mocks#{ext}"] else []
-			.concat if not useBackendless then ["!**/*.backend#{ext}"]     else []
-			.concat if not runSpecs       then ["!**/*.spec#{ext}"]        else []
+			.concat if not (runSpecs or useBackendless) then ["!**/angular-mocks#{ext}"] else []
+			.concat if not useBackendless               then ["!**/*.backend#{ext}"]     else []
+			.concat if not runSpecs                     then ["!**/*.spec#{ext}"]        else []

 	src =
 		gulp


fix issue where cursor was jumping one character if delete was happening before comma

--- app/assets/javascripts/angular/directives/smartNumber.js.coffee
+++ app/assets/javascripts/angular/directives/smartNumber.js.coffee
@@ -97,7 +97,10 @@
         else
           return initialPosition - 1
       else # cursor is adding numbers at the end of the input field
-        return initialPosition
+        if commaAfterCursor(elem)
+          return initialPosition + 1
+        else
+          return initialPosition

     findNewBackspaceCursorPosition = (elem, initialPosition) ->
       if elem.val().length == 5 || elem.val().length == 9 # a comma will be removed


this router should find items that belong to this category

--- app/assets/javascripts/routes/items/items_index_route.js.coffee
+++ app/assets/javascripts/routes/items/items_index_route.js.coffee
@@ -3,4 +3,5 @@
     Bandango.currentSession.requireLogin @

   model: ->
-    @get("store").findAll "item"
+    category = @modelFor "items"
+    @get("store").find "item", category_id: category.get("id")


Fixed dependency reference

Fixed dependency reference

--- lib/plunks.coffee
+++ lib/plunks.coffee
@@ -1,4 +1,4 @@
-schema = require("./json-schema")
+schema = require("./validate")
 mime = require("mime")
 _ = require("underscore")._


Add Report#set

Add Report#set

--- lib/report.coffee
+++ lib/report.coffee
@@ -3,6 +3,9 @@

   get: (attributeName) ->
     @attributes[attributeName]
+
+  set: (attributeName, value) ->
+    @attributes[attributeName] = value;

   getAttributes: ->
     @attributes


Returns error list from parse()

Returns error list from parse()

--- lib/ripper.coffee
+++ lib/ripper.coffee
@@ -30,14 +30,19 @@
       @context.setCode syntax
       rLine = /.*(?:\r?\n|\n?\r)/g
       @lines = (result[0].length while (result = rLine.exec code)?)
-      callback()
+      callback?() #TODO deprecate
+      return null
     catch err
       { lineNumber, column, description } = err
       if lineNumber? and column? and description?
-        callback [
+        err =
           range: new Range [lineNumber - 1, column], [lineNumber - 1, column]
           message: description
-        ]
+        callback? [ err ] #TODO deprecate
+        return [ err ]
+      else
+        callback?() #TODO deprecate
+        return null

   find: ({ row, column }) ->
     pos = 0


I write that shit like this.

I write that shit like this.

--- lib/routes.coffee
+++ lib/routes.coffee
@@ -35,7 +35,7 @@
   # We fetch one more than we ask for, so that we can know if we
   # have a potential next page
   Comic.top (perPage + 1), skipTotal, (err, comics) =>
-    return response.redirect("/") if err
+    return failHome() if err
     response.render 'top',
       top: comics[0...perPage]
       page: page


Return null instead of throwing an exception

--- src/natives/classes/sun.net.spi.DefaultProxySelector.coffee
+++ src/natives/classes/sun.net.spi.DefaultProxySelector.coffee
@@ -1,5 +1,4 @@
 native_methods.sun.net.spi.DefaultProxySelector = [
   o 'init()Z', (rs) -> true
-  o 'getSystemProxy(Ljava/lang/String;Ljava/lang/String;)Ljava/net/Proxy;', (rs) ->
-    rs.java_throw rs.get_bs_class('Ljava/io/IOException;'), 'proxy'
+  o 'getSystemProxy(Ljava/lang/String;Ljava/lang/String;)Ljava/net/Proxy;', (rs) -> null
 ]


Don't parse json you don't have

Don't parse json you don't have

--- app/assets/javascripts/collection.js.coffee
+++ app/assets/javascripts/collection.js.coffee
@@ -6,8 +6,8 @@
     $('.collection-num').html localStorage.num
     $('.collection-total').html localStorage.total
     $('#results').html ''
-    releases = JSON.parse(localStorage.releases)
-    if releases.length > 0
+    if localStorage.releases?.length > 0
+      releases = JSON.parse(localStorage.releases)
       releaseStrings = $.map releases, (r, i) ->
         "#{r.artists} - #{r.title}"
       $.each releaseStrings.sort(), (i, r) ->