[Gutter] Fix custom decorations tests in TextEditorPresenter specs

--- spec/text-editor-presenter-spec.coffee
+++ spec/text-editor-presenter-spec.coffee
@@ -2156,7 +2156,7 @@
           gutter = description.gutter
           return description if gutter.name is gutterName

-      describe "the array itself", ->
+      describe "the array itself, an array of gutter descriptions", ->
         it "updates when gutters are added to the editor model, and keeps the gutters sorted by priority", ->
           presenter = buildPresenter()
           gutter1 = editor.addGutter({name: 'test-gutter-1', priority: -100, visible: true})
@@ -2180,8 +2180,238 @@
           gutter.destroy()
           expect(getStateForGutterWithName(presenter, 'test-gutter')).toBeUndefined()

-      describe "when gutter description corresponds to a custom gutter", ->
-        # TODO
+      describe "for a gutter description that corresponds to a custom gutter", ->
+        describe ".content", ->
+          getContentForGutterWithName = (presenter, gutterName) ->
+            fullState = getStateForGutterWithName(presenter, gutterName)
+            return fullState.content if fullState
+
+          [presenter, gutter, decorationItem, decorationParams] = []
+          [marker1, decoration1, marker2, decoration2, marker3, decoration3] = []
+
+          # Set the scrollTop to 0 to show the very top of the file.
+          # Set the explicitHeight to make 10 lines visible.
+          scrollTop = 0
+          lineHeight = 10
+          explicitHeight = lineHeight * 10
+          lineOverdrawMargin = 1
+
+          beforeEach ->
+            # At the beginning of each test, decoration1 and decoration2 are in visible range,
+            # but not decoration3.
+            presenter = buildPresenter({explicitHeight, scrollTop, lineHeight, lineOverdrawMargin})
+            gutter = editor.addGutter({name: 'test-gutter', visible: true})
+            decorationItem = document.createElement('div')
+            decorationItem.class = 'decoration-item'
+            decorationParams =
+              type: 'gutter'
+              gutterName: 'test-gutter'
+              class: 'test-class'
+              item: decorationItem
+            marker1 = editor.markBufferRange([[0,0],[1,0]])
+            decoration1 = editor.decorateMarker(marker1, decorationParams)
+            marker2 = editor.markBufferRange([[9,0],[12,0]])
+            decoration2 = editor.decorateMarker(marker2, decorationParams)
+            marker3 = editor.markBufferRange([[13,0],[14,0]])
+            decoration3 = editor.decorateMarker(marker3, decorationParams)
+
+            # Clear any batched state updates.
+            presenter.getState()
+
+          it "contains all decorations within the visible buffer range", ->
+            decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+            expect(decorationState[decoration1.id].top).toBe lineHeight * marker1.getScreenRange().start.row
+            expect(decorationState[decoration1.id].height).toBe lineHeight * marker1.getScreenRange().getRowCount()
+            expect(decorationState[decoration1.id].item).toBe decorationItem
+            expect(decorationState[decoration1.id].class).toBe 'test-class'
+
+            expect(decorationState[decoration2.id].top).toBe lineHeight * marker2.getScreenRange().start.row
+            expect(decorationState[decoration2.id].height).toBe lineHeight * marker2.getScreenRange().getRowCount()
+            expect(decorationState[decoration2.id].item).toBe decorationItem
+            expect(decorationState[decoration2.id].class).toBe 'test-class'
+
+            expect(decorationState[decoration3.id]).toBeUndefined()
+
+          it "updates when ::scrollTop changes", ->
+            # This update will scroll decoration1 out of view, and decoration3 into view.
+            expectStateUpdate presenter, -> presenter.setScrollTop(scrollTop + lineHeight * 5)
+
+            decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+            expect(decorationState[decoration1.id]).toBeUndefined()
+            expect(decorationState[decoration2.id].top).toBeDefined()
+            expect(decorationState[decoration3.id].top).toBeDefined()
+
+          it "updates when ::explicitHeight changes", ->
+            # This update will make all three decorations visible.
+            expectStateUpdate presenter, -> presenter.setExplicitHeight(explicitHeight + lineHeight * 5)
+
+            decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+            expect(decorationState[decoration1.id].top).toBeDefined()
+            expect(decorationState[decoration2.id].top).toBeDefined()
+            expect(decorationState[decoration3.id].top).toBeDefined()
+
+          it "updates when ::lineHeight changes", ->
+            # This update will make all three decorations visible.
+            expectStateUpdate presenter, -> presenter.setLineHeight(Math.ceil(1.0 * explicitHeight / marker3.getBufferRange().end.row))
+
+            decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+            expect(decorationState[decoration1.id].top).toBeDefined()
+            expect(decorationState[decoration2.id].top).toBeDefined()
+            expect(decorationState[decoration3.id].top).toBeDefined()
+
+          it "updates when the editor's content changes", ->
+            # This update will add enough lines to push decoration2 out of view.
+            expectStateUpdate presenter, -> editor.setTextInBufferRange([[8,0],[9,0]],'\n\n\n\n\n')
+
+            decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+            expect(decorationState[decoration1.id].top).toBeDefined()
+            expect(decorationState[decoration2.id]).toBeUndefined()
+            expect(decorationState[decoration3.id]).toBeUndefined()
+
+          it "updates when a decoration's marker is modified", ->
+            # This update will move decoration1 out of view.
+            expectStateUpdate presenter, ->
+              newRange = new Range([13,0],[14,0])
+              marker1.setBufferRange(newRange)
+
+            decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+            expect(decorationState[decoration1.id]).toBeUndefined()
+            expect(decorationState[decoration2.id].top).toBeDefined()
+            expect(decorationState[decoration3.id]).toBeUndefined()
+
+          describe "when a decoration's properties are modified", ->
+            it "updates the item applied to the decoration, if the decoration item is changed", ->
+              # This changes the decoration class. The visibility of the decoration should not be affected.
+              newItem = document.createElement('div')
+              newItem.class = 'new-decoration-item'
+              newDecorationParams =
+                type: 'gutter'
+                gutterName: 'test-gutter'
+                class: 'test-class'
+                item: newItem
+              expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams)
+
+              decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+              expect(decorationState[decoration1.id].item).toBe newItem
+              expect(decorationState[decoration2.id].item).toBe decorationItem
+              expect(decorationState[decoration3.id]).toBeUndefined()
+
+            it "updates the class applied to the decoration, if the decoration class is changed", ->
+              # This changes the decoration item. The visibility of the decoration should not be affected.
+              newDecorationParams =
+                type: 'gutter'
+                gutterName: 'test-gutter'
+                class: 'new-test-class'
+                item: decorationItem
+              expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams)
+
+              decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+              expect(decorationState[decoration1.id].class).toBe 'new-test-class'
+              expect(decorationState[decoration2.id].class).toBe 'test-class'
+              expect(decorationState[decoration3.id]).toBeUndefined()
+
+            it "updates the type of the decoration, if the decoration type is changed", ->
+              # This changes the type of the decoration. This should remove the decoration from the gutter.
+              newDecorationParams =
+                type: 'line'
+                gutterName: 'test-gutter' # This is an invalid/meaningless option here, but it shouldn't matter.
+                class: 'test-class'
+                item: decorationItem
+              expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams)
+
+              decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+              expect(decorationState[decoration1.id]).toBeUndefined()
+              expect(decorationState[decoration2.id].top).toBeDefined()
+              expect(decorationState[decoration3.id]).toBeUndefined()
+
+            it "updates the gutter the decoration targets, if the decoration gutterName is changed", ->
+              # This changes which gutter this decoration applies to. Since this gutter does not exist,
+              # the decoration should not appear in the customDecorations state.
+              newDecorationParams =
+                type: 'gutter'
+                gutterName: 'test-gutter-2'
+                class: 'new-test-class'
+                item: decorationItem
+              expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams)
+
+              decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+              expect(decorationState[decoration1.id]).toBeUndefined()
+              expect(decorationState[decoration2.id].top).toBeDefined()
+              expect(decorationState[decoration3.id]).toBeUndefined()
+
+              # After adding the targeted gutter, the decoration will appear in the state for that gutter,
+              # since it should be visible.
+              expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'})
+              newGutterDecorationState = getContentForGutterWithName(presenter, 'test-gutter-2')
+              expect(newGutterDecorationState[decoration1.id].top).toBeDefined()
+              expect(newGutterDecorationState[decoration2.id]).toBeUndefined()
+              expect(newGutterDecorationState[decoration3.id]).toBeUndefined()
+              oldGutterDecorationState = getContentForGutterWithName(presenter, 'test-gutter')
+              expect(oldGutterDecorationState[decoration1.id]).toBeUndefined()
+              expect(oldGutterDecorationState[decoration2.id].top).toBeDefined()
+              expect(oldGutterDecorationState[decoration3.id]).toBeUndefined()
+
+          it "updates when the editor's mini state changes, and is cleared when the editor is mini", ->
+            expectStateUpdate presenter, -> editor.setMini(true)
+            decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+            expect(decorationState).toBeUndefined()
+
+            # The decorations should return to the original state.
+            expectStateUpdate presenter, -> editor.setMini(false)
+            decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+            expect(decorationState[decoration1.id].top).toBeDefined()
+            expect(decorationState[decoration2.id].top).toBeDefined()
+            expect(decorationState[decoration3.id]).toBeUndefined()
+
+          it "updates when a gutter's visibility changes, and is cleared when the gutter is not visible", ->
+            expectStateUpdate presenter, -> gutter.hide()
+            decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+            expect(decorationState[decoration1.id]).toBeUndefined()
+            expect(decorationState[decoration2.id]).toBeUndefined()
+            expect(decorationState[decoration3.id]).toBeUndefined()
+
+            # The decorations should return to the original state.
+            expectStateUpdate presenter, -> gutter.show()
+            decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+            expect(decorationState[decoration1.id].top).toBeDefined()
+            expect(decorationState[decoration2.id].top).toBeDefined()
+            expect(decorationState[decoration3.id]).toBeUndefined()
+
+          it "updates when a gutter is added to the editor", ->
+            decorationParams =
+              type: 'gutter'
+              gutterName: 'test-gutter-2'
+              class: 'test-class'
+            marker4 = editor.markBufferRange([[0,0],[1,0]])
+            decoration4 = editor.decorateMarker(marker4, decorationParams)
+            expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'})
+
+            decorationState = getContentForGutterWithName(presenter, 'test-gutter-2')
+            expect(decorationState[decoration1.id]).toBeUndefined()
+            expect(decorationState[decoration2.id]).toBeUndefined()
+            expect(decorationState[decoration3.id]).toBeUndefined()
+            expect(decorationState[decoration4.id].top).toBeDefined()
+
+          it "updates when editor lines are folded", ->
+            oldDimensionsForDecoration1 =
+              top: lineHeight * marker1.getScreenRange().start.row
+              height: lineHeight * marker1.getScreenRange().getRowCount()
+            oldDimensionsForDecoration2 =
+              top: lineHeight * marker2.getScreenRange().start.row
+              height: lineHeight * marker2.getScreenRange().getRowCount()
+
+            # Based on the contents of sample.js, this should affect all but the top
+            # part of decoration1.
+            expectStateUpdate presenter, -> editor.foldBufferRow(0)
+
+            decorationState = getContentForGutterWithName(presenter, 'test-gutter')
+            expect(decorationState[decoration1.id].top).toBe oldDimensionsForDecoration1.top
+            expect(decorationState[decoration1.id].height).not.toBe oldDimensionsForDecoration1.height
+            # Due to the issue described here: https://github.com/atom/atom/issues/6454, decoration2
+            # will be bumped up to the row that was folded and still made visible, instead of being
+            # entirely collapsed. (The same thing will happen to decoration3.)
+            expect(decorationState[decoration2.id].top).not.toBe oldDimensionsForDecoration2.top
+            expect(decorationState[decoration2.id].height).not.toBe oldDimensionsForDecoration2.height

       describe "regardless of what kind of gutter a gutter description corresponds to", ->
         [customGutter] = []
@@ -2309,238 +2539,6 @@
             expectStateUpdate presenter, -> presenter.setBackgroundColor("rgba(0, 0, 255, 0)")
             expect(getStylesForGutterWithName(presenter, 'line-number').backgroundColor).toBe "rgba(0, 0, 255, 0)"
             expect(getStylesForGutterWithName(presenter, 'test-gutter').backgroundColor).toBe "rgba(0, 0, 255, 0)"
-
-      # TODO
-      describe ".customDecorations", ->
-        [presenter, gutter, decorationItem, decorationParams] = []
-        [marker1, decoration1, marker2, decoration2, marker3, decoration3] = []
-
-        # Set the scrollTop to 0 to show the very top of the file.
-        # Set the explicitHeight to make 10 lines visible.
-        scrollTop = 0
-        lineHeight = 10
-        explicitHeight = lineHeight * 10
-        lineOverdrawMargin = 1
-
-        decorationStateForGutterName = (presenter, gutterName) ->
-          presenter.getState().gutters.customDecorations[gutterName]
-
-        beforeEach ->
-          # At the beginning of each test, decoration1 and decoration2 are in visible range,
-          # but not decoration3.
-          presenter = buildPresenter({explicitHeight, scrollTop, lineHeight, lineOverdrawMargin})
-          gutter = editor.addGutter({name: 'test-gutter', visible: true})
-          decorationItem = document.createElement('div')
-          decorationItem.class = 'decoration-item'
-          decorationParams =
-            type: 'gutter'
-            gutterName: 'test-gutter'
-            class: 'test-class'
-            item: decorationItem
-          marker1 = editor.markBufferRange([[0,0],[1,0]])
-          decoration1 = editor.decorateMarker(marker1, decorationParams)
-          marker2 = editor.markBufferRange([[9,0],[12,0]])
-          decoration2 = editor.decorateMarker(marker2, decorationParams)
-          marker3 = editor.markBufferRange([[13,0],[14,0]])
-          decoration3 = editor.decorateMarker(marker3, decorationParams)
-
-          # Clear any batched state updates.
-          presenter.getState()
-
-        it "contains all decorations within the visible buffer range", ->
-          decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-          expect(decorationState[decoration1.id].top).toBe lineHeight * marker1.getScreenRange().start.row
-          expect(decorationState[decoration1.id].height).toBe lineHeight * marker1.getScreenRange().getRowCount()
-          expect(decorationState[decoration1.id].item).toBe decorationItem
-          expect(decorationState[decoration1.id].class).toBe 'test-class'
-
-          expect(decorationState[decoration2.id].top).toBe lineHeight * marker2.getScreenRange().start.row
-          expect(decorationState[decoration2.id].height).toBe lineHeight * marker2.getScreenRange().getRowCount()
-          expect(decorationState[decoration2.id].item).toBe decorationItem
-          expect(decorationState[decoration2.id].class).toBe 'test-class'
-
-          expect(decorationState[decoration3.id]).toBeUndefined()
-
-        it "updates when ::scrollTop changes", ->
-          # This update will scroll decoration1 out of view, and decoration3 into view.
-          expectStateUpdate presenter, -> presenter.setScrollTop(scrollTop + lineHeight * 5)
-
-          decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-          expect(decorationState[decoration1.id]).toBeUndefined()
-          expect(decorationState[decoration2.id].top).toBeDefined()
-          expect(decorationState[decoration3.id].top).toBeDefined()
-
-        it "updates when ::explicitHeight changes", ->
-          # This update will make all three decorations visible.
-          expectStateUpdate presenter, -> presenter.setExplicitHeight(explicitHeight + lineHeight * 5)
-
-          decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-          expect(decorationState[decoration1.id].top).toBeDefined()
-          expect(decorationState[decoration2.id].top).toBeDefined()
-          expect(decorationState[decoration3.id].top).toBeDefined()
-
-        it "updates when ::lineHeight changes", ->
-          # This update will make all three decorations visible.
-          expectStateUpdate presenter, -> presenter.setLineHeight(Math.ceil(1.0 * explicitHeight / marker3.getBufferRange().end.row))
-
-          decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-          expect(decorationState[decoration1.id].top).toBeDefined()
-          expect(decorationState[decoration2.id].top).toBeDefined()
-          expect(decorationState[decoration3.id].top).toBeDefined()
-
-        it "updates when the editor's content changes", ->
-          # This update will add enough lines to push decoration2 out of view.
-          expectStateUpdate presenter, -> editor.setTextInBufferRange([[8,0],[9,0]],'\n\n\n\n\n')
-
-          decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-          expect(decorationState[decoration1.id].top).toBeDefined()
-          expect(decorationState[decoration2.id]).toBeUndefined()
-          expect(decorationState[decoration3.id]).toBeUndefined()
-
-        it "updates when a decoration's marker is modified", ->
-          # This update will move decoration1 out of view.
-          expectStateUpdate presenter, ->
-            newRange = new Range([13,0],[14,0])
-            marker1.setBufferRange(newRange)
-
-          decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-          expect(decorationState[decoration1.id]).toBeUndefined()
-          expect(decorationState[decoration2.id].top).toBeDefined()
-          expect(decorationState[decoration3.id]).toBeUndefined()
-
-        describe "when a decoration's properties are modified", ->
-          it "updates the item applied to the decoration, if the decoration item is changed", ->
-            # This changes the decoration class. The visibility of the decoration should not be affected.
-            newItem = document.createElement('div')
-            newItem.class = 'new-decoration-item'
-            newDecorationParams =
-              type: 'gutter'
-              gutterName: 'test-gutter'
-              class: 'test-class'
-              item: newItem
-            expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams)
-
-            decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-            expect(decorationState[decoration1.id].item).toBe newItem
-            expect(decorationState[decoration2.id].item).toBe decorationItem
-            expect(decorationState[decoration3.id]).toBeUndefined()
-
-          it "updates the class applied to the decoration, if the decoration class is changed", ->
-            # This changes the decoration item. The visibility of the decoration should not be affected.
-            newDecorationParams =
-              type: 'gutter'
-              gutterName: 'test-gutter'
-              class: 'new-test-class'
-              item: decorationItem
-            expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams)
-
-            decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-            expect(decorationState[decoration1.id].class).toBe 'new-test-class'
-            expect(decorationState[decoration2.id].class).toBe 'test-class'
-            expect(decorationState[decoration3.id]).toBeUndefined()
-
-          it "updates the type of the decoration, if the decoration type is changed", ->
-            # This changes the type of the decoration. This should remove the decoration from the gutter.
-            newDecorationParams =
-              type: 'line'
-              gutterName: 'test-gutter' # This is an invalid/meaningless option here, but it shouldn't matter.
-              class: 'test-class'
-              item: decorationItem
-            expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams)
-
-            decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-            expect(decorationState[decoration1.id]).toBeUndefined()
-            expect(decorationState[decoration2.id].top).toBeDefined()
-            expect(decorationState[decoration3.id]).toBeUndefined()
-
-          it "updates the gutter the decoration targets, if the decoration gutterName is changed", ->
-            # This changes which gutter this decoration applies to. Since this gutter does not exist,
-            # the decoration should not appear in the customDecorations state.
-            newDecorationParams =
-              type: 'gutter'
-              gutterName: 'test-gutter-2'
-              class: 'new-test-class'
-              item: decorationItem
-            expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams)
-
-            decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-            expect(decorationState[decoration1.id]).toBeUndefined()
-            expect(decorationState[decoration2.id].top).toBeDefined()
-            expect(decorationState[decoration3.id]).toBeUndefined()
-
-            # After adding the targeted gutter, the decoration will appear in the state for that gutter,
-            # since it should be visible.
-            expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'})
-            newGutterDecorationState = decorationStateForGutterName(presenter, 'test-gutter-2')
-            expect(newGutterDecorationState[decoration1.id].top).toBeDefined()
-            expect(newGutterDecorationState[decoration2.id]).toBeUndefined()
-            expect(newGutterDecorationState[decoration3.id]).toBeUndefined()
-            oldGutterDecorationState = decorationStateForGutterName(presenter, 'test-gutter')
-            expect(oldGutterDecorationState[decoration1.id]).toBeUndefined()
-            expect(oldGutterDecorationState[decoration2.id].top).toBeDefined()
-            expect(oldGutterDecorationState[decoration3.id]).toBeUndefined()
-
-        it "updates when the editor's mini state changes, and is cleared when the editor is mini", ->
-          expectStateUpdate presenter, -> editor.setMini(true)
-          decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-          expect(decorationState).toBeUndefined()
-
-          # The decorations should return to the original state.
-          expectStateUpdate presenter, -> editor.setMini(false)
-          decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-          expect(decorationState[decoration1.id].top).toBeDefined()
-          expect(decorationState[decoration2.id].top).toBeDefined()
-          expect(decorationState[decoration3.id]).toBeUndefined()
-
-        it "updates when a gutter's visibility changes, and is cleared when the gutter is not visible", ->
-          expectStateUpdate presenter, -> gutter.hide()
-          decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-          expect(decorationState[decoration1.id]).toBeUndefined()
-          expect(decorationState[decoration2.id]).toBeUndefined()
-          expect(decorationState[decoration3.id]).toBeUndefined()
-
-          # The decorations should return to the original state.
-          expectStateUpdate presenter, -> gutter.show()
-          decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-          expect(decorationState[decoration1.id].top).toBeDefined()
-          expect(decorationState[decoration2.id].top).toBeDefined()
-          expect(decorationState[decoration3.id]).toBeUndefined()
-
-        it "updates when a gutter is added to the editor", ->
-          decorationParams =
-            type: 'gutter'
-            gutterName: 'test-gutter-2'
-            class: 'test-class'
-          marker4 = editor.markBufferRange([[0,0],[1,0]])
-          decoration4 = editor.decorateMarker(marker4, decorationParams)
-          expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'})
-
-          decorationState = decorationStateForGutterName(presenter, 'test-gutter-2')
-          expect(decorationState[decoration1.id]).toBeUndefined()
-          expect(decorationState[decoration2.id]).toBeUndefined()
-          expect(decorationState[decoration3.id]).toBeUndefined()
-          expect(decorationState[decoration4.id].top).toBeDefined()
-
-        it "updates when editor lines are folded", ->
-          oldDimensionsForDecoration1 =
-            top: lineHeight * marker1.getScreenRange().start.row
-            height: lineHeight * marker1.getScreenRange().getRowCount()
-          oldDimensionsForDecoration2 =
-            top: lineHeight * marker2.getScreenRange().start.row
-            height: lineHeight * marker2.getScreenRange().getRowCount()
-
-          # Based on the contents of sample.js, this should affect all but the top
-          # part of decoration1.
-          expectStateUpdate presenter, -> editor.foldBufferRow(0)
-
-          decorationState = decorationStateForGutterName(presenter, 'test-gutter')
-          expect(decorationState[decoration1.id].top).toBe oldDimensionsForDecoration1.top
-          expect(decorationState[decoration1.id].height).not.toBe oldDimensionsForDecoration1.height
-          # Due to the issue described here: https://github.com/atom/atom/issues/6454, decoration2
-          # will be bumped up to the row that was folded and still made visible, instead of being
-          # entirely collapsed. (The same thing will happen to decoration3.)
-          expect(decorationState[decoration2.id].top).not.toBe oldDimensionsForDecoration2.top
-          expect(decorationState[decoration2.id].height).not.toBe oldDimensionsForDecoration2.height

   # disabled until we fix an issue with display buffer markers not updating when
   # they are moved on screen but not in the buffer


Remove stray `)`

Remove stray `)`

--- test-integration/task-plan/cleanup.coffee
+++ test-integration/task-plan/cleanup.coffee
@@ -27,7 +27,7 @@

     , (plans) ->
       console.log "Deleting #{plans.length} Drafts..." if plans.length
-    )
+

     # Delete published but not opened plans
     @calendar.el.unopenPlan.forEach (plan, index, total) =>
@@ -41,4 +41,3 @@

     , (plans) ->
       console.log "Deleting #{plans.length} Unopened..." if plans.length
-    )


Tweak

Tweak

--- test/generators/relational/has_one.coffee
+++ test/generators/relational/has_one.coffee
@@ -422,22 +422,21 @@
               require('../../../lib/cache').reset() # reset cache
               owner = new Owner({id: owner.id})
             owner.patchRemove 'reverse', destroyed_model, (err) ->
-            assert.ok(!err, "No errors: #{err}")
-
-            assert.ok(!owner.get('reverse'), "destroyed in memory relationship.")
-
-            owner.get 'reverse', (err, reverse) ->
-              assert.ok(!err, "No errors: #{err}")
-              assert.ok(!reverse, "loaded correct models.")
-
-              Owner.findOne owner.id, (err, owner) ->
-                assert.ok(!err, "No errors: #{err}")
-                assert.ok(owner, 'found owners')
-
-                owner.get 'reverse', (err, reverse) ->
-                  assert.ok(!err, "No errors: #{err}")
-                  assert.ok(!reverse, "loaded correct models.")
-                  done()
+              assert.ok(!err, "No errors: #{err}")
+              assert.ok(!owner.get('reverse'), "destroyed in memory relationship.")
+
+              owner.get 'reverse', (err, reverse) ->
+                assert.ok(!err, "No errors: #{err}")
+                assert.ok(!reverse, "loaded correct models.")
+
+                Owner.findOne owner.id, (err, owner) ->
+                  assert.ok(!err, "No errors: #{err}")
+                  assert.ok(owner, 'found owners')
+
+                  owner.get 'reverse', (err, reverse) ->
+                    assert.ok(!err, "No errors: #{err}")
+                    assert.ok(!reverse, "loaded correct models.")
+                    done()

       it "Can manually delete a relationship by array related of model (hasOne) #{if unload then 'with unloaded model' else ''}", (done) ->
         # TODO: implement embedded find
@@ -457,7 +456,6 @@
               owner = new Owner({id: owner.id})
             owner.patchRemove 'reverse', [destroyed_model], (err) ->
               assert.ok(!err, "No errors: #{err}")
-
               assert.ok(!owner.get('reverse'), "destroyed in memory relationship.")

               owner.get 'reverse', (err, reverse) ->


Added assembly grammar

Added assembly grammar

Signed-off-by: ysgard <125fa00567d70184e4d0dc6a16b66d7bf20d7d44@gmail.com>

--- grammars/d.cson
+++ grammars/d.cson
@@ -339,7 +339,7 @@
           },
           'end': '\\)',
           'endCaptures': {
-            '0': { 'name': 'keyword.other.version.identifer.end.d'}
+            '0': { 'name': 'keyword.other.version.identifer.end.d' }
           }
           'patterns': [
             { 'include': '#integer-literal' },
@@ -537,27 +537,27 @@
     #
     'asm-statement': {
       'patterns': [
-        'begin': 'asm\\s*{',
+        'begin': 'asm\\s*\\{',
+        'end': '\\}',
         'beginCaptures': {
           '0': { 'name': 'keyword.control.asm.begin.d' },
         },
-        'end': '}',
         'endCaptures': {
           '0': { 'name': 'keyword.control.asm.end.d' },
         },
         'patterns': [
-          { '#include': '#asm-instruction' }
-        ]
+          { 'include': '#asm-instruction' },
+        ],
       ]
     },
     'asm-instruction': {
       'patterns': [
         {
           'match': 'align|even|naked|db|ds|di|dl|df|dd|de|:',
-          'name': 'keyword.other.asm-instruction.d',
-        },
+          'name': 'keyword.asm-instruction.d',
+        },
+        { 'include': '#operands' },
         { 'include': '#integer-expression' }
-        { 'include': '#operands' }
       ]
     },
     'operands': {
@@ -566,7 +566,7 @@
           'match': ',',
           'name': 'keyword.operator.comma.assembly.d'
         }
-        { 'include': 'asm-exp' },
+        { 'include': '#asm-exp' },
       ]
     },
     'asm-exp': {
@@ -580,7 +580,7 @@
           'name': 'keyword.operator.bracket.assembly.d',
         },
         {
-          'match': '>>>|\\|\\||&&|==|!=|<=|>=|<<|>>|\\||\\^|&|<|>|\\+|-|\\*|/|%',
+          'match': '>>>|\\|\\||&&|==|!=|<=|>=|<<|>>|\\||\\^|&|<|>|\\+|-|\\*|/|%|~|!',
           'name': 'keyword.operator.assembly.d'
         },
         { 'include': '#asm-una-exp' },
@@ -596,11 +596,10 @@
       'patterns': [
         { 'include': '#asm-type-prefix' },
         {
-          'match': 'offsetof|seg|\\+|-|!|~',
-          'name': 'entity.name.tag.assembly.asm-una-exp'
+          'match': 'offsetof|seg',
+          'name': 'support.type.assembly.d'
         },
         { 'include': '#asm-primary-expression' },
-        { 'include': '$base' },
       ]
     },
     'asm-primary-expression': {
@@ -609,7 +608,7 @@
         { 'include': '#float-literal' },
         {
           'match': '__LOCAL_SIZE',
-          'name': 'entity.name.tag.assembly.primary-expression.d'
+          'name': 'constant.language.assembly.d'
         },
         { 'include': '#register' },
         { 'include': '#register-64' },
@@ -619,19 +618,19 @@
     'asm-type-prefix': {
       'patterns': [
         'match': '(near\\s+ptr)|(far\\s+ptr)|(byte\\s+ptr)|(short\\s+ptr)|(int\\s+ptr)|(word\\s+ptr)|(dword\\s+ptr)|(qword\\s+ptr)|(float\\s+ptr)|(double\\s+ptr)|(real\\s+ptr)',
-        'name': 'entity.name.tag.assembly.type-prefix.d',
+        'name': 'support.type.asm-type-prefix.d',
       ]
     },
     'register': {
       'patterns': [
         'match': 'XMM0|XMM1|XMM2|XMM3|XMM4|XMM5|XMM6|XMM7|MM0|MM1|MM2|MM3|MM4|MM5|MM6|MM7|ST\\(0\\)|ST\\(1\\)|ST\\(2\\)|ST\\(3\\)|ST\\(4\\)|ST\\(5\\)|ST\\(6\\)|ST\\(7\\)|ST|TR1|TR2|TR3|TR4|TR5|TR6|TR7|DR0|DR1|DR2|DR3|DR4|DR5|DR6|DR7|CR0|CR2|CR3|CR4|EAX|EBX|ECX|EDX|EBP|ESP|EDI|ESI|AL|AH|AX|BL|BH|BX|CL|CH|CX|DL|DH|DX|BP|SP|DI|SI|ES|CS|SS|DS|GS|FS',
-        'name': 'entity.name.tag.assembly.register.d',
+        'name': 'storage.type.assembly.register.d',
       ]
     },
     'register-64': {
       'patterns': [
         'match': 'RAX|RBX|RCX|RDX|BPL|RBP|SPL|RSP|DIL|RDI|SIL|RSI|R8B|R8W|R8D|R8|R9B|R9W|R9D|R9|R10B|R10W|R10D|R10|R11B|R11W|R11D|R11|R12B|R12W|R12D|R12|R13B|R13W|R13D|R13|R14B|R14W|R14D|R14|R15B|R15W|R15D|R15|XMM8|XMM9|XMM10|XMM11|XMM12|XMM13|XMM14|XMM15|YMM0|YMM1|YMM2|YMM3|YMM4|YMM5|YMM6|YMM7|YMM8|YMM9|YMM10|YMM11|YMM12|YMM13|YMM14|YMM15',
-        'name': 'entity.name.tag.assembly.register-64.d'
+        'name': 'storage.type.assembly.register-64.d'
       ]
     },


Minor fix to the instructions

Minor fix to the instructions

--- programs/image2code/src/html-ui.coffee
+++ programs/image2code/src/html-ui.coffee
@@ -35,12 +35,12 @@
       computer to convert into code.

-      The identifier is a string that will be used
-      in the generated code to refer to a function that
-      will give you your image. For example, if you use
+      The identifier is a string that will be used in the
+      generated code, as part of the name of a function that
+      will load your image. For example, if you use
       MyImage as the identifier, then the
       generated code will include a function called
-      getMyImage which will return your image.
+      loadMyImage.

       It is recommended that you use an image in JPG, PNG or


Add test for php hook

Add test for php hook

--- test/unit/hooks-worker-client-test.coffee
+++ test/unit/hooks-worker-client-test.coffee
@@ -175,6 +175,60 @@
             assert.include logs.join(", "), "pip install dredd_hooks"
             done()
           , 2200
+
+    describe 'when --language php option is given', () ->
+      beforeEach ->
+        sinon.stub childProcessStub, 'spawn', () ->
+          emitter = new EventEmitter
+          emitter.stdout = new EventEmitter
+          emitter.stderr = new EventEmitter
+          emitter
+
+        hooks['configuration'] =
+          options:
+            language: 'php'
+            hookfiles: "somefile.php"
+
+      afterEach ->
+        childProcessStub.spawn.restore()
+        hooks['configuration'] = undefined
+
+      it 'should spawn the server process with command "dredd-hooks-php"', () ->
+        loadWorkerClient()
+        assert.isTrue childProcessStub.spawn.called
+        assert.equal childProcessStub.spawn.getCall(0).args[0], 'dredd-hooks-php'
+
+      it 'should pass --hookfiles option as a array of arguments', () ->
+        loadWorkerClient()
+        assert.equal childProcessStub.spawn.getCall(0).args[1][0], 'somefile.php'
+
+      describe 'when the worker is not installed', () ->
+        it 'should exit with 1', (done) ->
+          hooks.processExit.reset()
+          sinon.stub whichStub, 'which', (command) -> false
+          loadWorkerClient()
+          setTimeout () ->
+            whichStub.which.restore()
+            assert.isTrue hooks.processExit.calledWith(1)
+            done()
+          , 2200
+
+        it 'should write a hint how to install', (done) ->
+          sinon.stub consoleStub, 'log'
+          sinon.stub whichStub, 'which', (command) -> false
+          loadWorkerClient()
+          setTimeout () ->
+            logs = []
+            for args in consoleStub.log.args
+              logs.push args.join(" ")
+
+            consoleStub.log.restore()
+            whichStub.which.restore()
+
+            assert.include logs.join(", "), "composer require ddelnano/dredd-hooks-php"
+            done()
+          , 2200
+

     describe 'when --language ./any/other-command is given', () ->
       beforeEach ->


disable progressJS

disable progressJS

--- client/assets/scripts/application.coffee
+++ client/assets/scripts/application.coffee
@@ -261,13 +261,13 @@
 		if typeof params == "function"
 			callback = params
 			params = {}
-		progressJs().start().autoIncrease(4, 500)
+		# progressJs().start().autoIncrease(4, 500)
 		$.ajax
 			url: url,
 			xhrFields: { withCredentials: true },
 			data: params
-		.done -> progressJs().end()
 		.done callback
+		# .done -> progressJs().end()

 	nowPlaying: ->
 		method = 'nowPlaying'


don't send password w/ player data

don't send password w/ player data

--- shared/idle/IdleServer.coffee
+++ shared/idle/IdleServer.coffee
@@ -22,7 +22,7 @@
     IdlePlayerEvents.find {player: playerName}, {limit: 7, sort: {createdAt: -1}}

   Meteor.publish 'singlePlayer', (playerName) ->
-    IdlePlayers.find {name: playerName}, {fields: {pushbulletApiKey: 0}}
+    IdlePlayers.find {name: playerName}, {fields: {pushbulletApiKey: 0, password: 0}}

   Meteor.publish 'analytics', ->
     IdleAnalytics.find {}, {fields: analyticsFields}


Social: return if there is an error

Social: return if there is an error

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -34,6 +34,7 @@

   messageApiMessageResFunc = (name, rest..., callback)->
     KD.remote.api.SocialMessage[name] rest..., (err, res)->
+      return callback err if err
       return callback null, mapActivity res

   channelApiActivitiesResFunc = (name, rest..., callback)->


Revert "terminal: fetch account's group associated VMs in "VM selection" modal"

This reverts commit e0d8381d3efe193093c86cf6f23ee399c2e109f2.

--- client/Terminal/vmselectionview.coffee
+++ client/Terminal/vmselectionview.coffee
@@ -45,6 +45,4 @@
       @emit "VMSelected", vm
       @destroy()

-    KD.singleton("vmController").fetchGroupVMs no, (err, vms) =>
-      return  if KD.showError err
-      @listController.instantiateListItems vms
+    @listController.instantiateListItems KD.getSingleton('vmController').vms


tell app to handle query immediately if it's created, otherwise delay query handling once app is created

--- client/app/MainApp/kodingrouter.coffee
+++ client/app/MainApp/kodingrouter.coffee
@@ -82,12 +82,12 @@
         appManager  = KD.getSingleton "appManager"
         handleQuery = appManager.tell.bind appManager, app, "handleQuery", query

-        appManager.once "AppCreated", handleQuery  unless appManager.get app
+        appManager.once "AppCreated", handleQuery  unless appWasOpen = appManager.get app

         appManager.open app, (appInstance)=>
           appInstance.setOption "initialRoute", @getCurrentPath()

-        handleQuery()  if appManager.get app
+        handleQuery()  if appWasOpen

   handleNotFound:(route)->


Delete archetype from toolbar after it has been installed

--- client/code/view/tool/menu-item.coffee
+++ client/code/view/tool/menu-item.coffee
@@ -66,6 +66,7 @@
     if app.tools().length
       html = JST['toolbar-tile']
         manifest: @options.archetype.get 'manifest'
+        toolName: @options.archetype.get 'name'
       @$el.html html
     @

@@ -85,13 +86,15 @@

     dataset.fetch
       success: (dataset, resp, options) =>
-        dataset.installPlugin @options.archetype.get('name'), (err, view) =>
+        toolName = @options.archetype.get 'name'
+        dataset.installPlugin toolName, (err, view) =>
           console.warn 'Error', err if err?
           v = new Cu.View.ToolMenuItem model: view
           el = v.render().el
           $('a', el).addClass('active')
           $('#toolbar .tool.active').removeClass("active")
           $('#toolbar .tools').append el
+          $("ul.archetypes a[data-toolname='#{toolName}']").parent().remove()
           window.app.navigate "/dataset/#{dataset.id}/view/#{view.id}", trigger: true
       error: (model, xhr, options) ->
         @active = false


Use local variables.

Use local variables.

please.

--- client/ide/finder/AppController.coffee
+++ client/ide/finder/AppController.coffee
@@ -6,11 +6,14 @@
     options.useStorage       ?= yes
     options.addOrphansToRoot ?= no
     options.delegate         ?= this
+
     @controller = new NFinderController options
+    finderView  = @controller.getView()

-    @controller.getView().addSubView @getAppTitleView()
-    @controller.getView().addSubView @getUploader()
-    @controller.getView().addSubView @getMountVMButton()
+    finderView.addSubView @getAppTitleView()
+    finderView.addSubView @getUploader()
+    finderView.addSubView @getMountVMButton()
+
     return @controller

   getAppTitleView: ->


css clean should happen at end in gulp preview

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -43,9 +43,10 @@
                        cb

 gulp.task "preview", (cb)->
-  return runSequence  ['build:clean', 'css:clean', 'js:clean'],
+  return runSequence  ['build:clean', 'js:clean'],
                       ['css:build','js:build'],
                       'build_wintersmith',
+                      'css:clean',
                       cb


fix repository url

fix repository url

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -244,7 +244,7 @@
       value: 'm@bouzuya.net'
     ]
     directory: 'dist'
-    repository: 'https://github.com/bouzuya/bbn-react'
+    repository: 'https://github.com/bouzuya/blog.bouzuya.net'

 gulp.task 'test', ['build-test'], ->
   gulp.src './.tmp/test/**/*.js'


fewer and shorter compass config variables

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -21,21 +21,20 @@
   next()

 gulp.task "compass", ->
-  compile = (config, path) ->
-    exec "compass compile -c #{config}", {cwd: path}, (error, stdout, stderr) ->
-      console.log("[compass] compiling #{path}")
+  compile = (config) ->
+    proj = path.dirname(config.path)
+    exec "compass compile -c #{config.path}", {cwd: proj}, (error, stdout, stderr) ->
+      console.log("[compass] compiling #{proj}")
       process.stdout.write(stdout)
       console.log("[compass] error(s) above") if error isnt null
   gulp.src("harp/**/+(config|compass).rb", read: false)
-    .pipe tap (configFile) ->
-      configFull = configFile.path
-      configPath = path.dirname(configFile.path)
-      configName = path.basename(configFile.path)
-      gutil.log "Compass config #{configName} for #{configPath}"
-      compile(configFull, configPath)
-      gulp.watch "#{configPath}/**/*.+(sass|scss)", (event) ->
+    .pipe tap (config) ->
+      proj = path.dirname(config.path)
+      gutil.log "Compass #{path.basename(config.path)} in #{proj}"
+      compile(config)
+      gulp.watch "#{proj}/**/*.+(sass|scss)", (event) ->
         if event.type is "changed"
-          compile(configFull, configPath)
+          compile(config)

 gulp.task "default", ["server"], ->
   reload = livereload()


add handlers for error events so gulp doesnt't crash every time an error event is emitted

watchify will probably need some special treatment but this will do for now

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -38,11 +38,17 @@
     watch: './src/assets/**/*.*'
     destination: './public/'

+handleError = (err) ->
+  gutil.log err
+  gutil.beep()
+  this.emit 'end'
+
 gulp.task 'scripts', ->

   bundle = browserify paths.scripts.source

   build = bundle.bundle(debug: not production)
+    .on 'error', handleError
     .pipe source paths.scripts.filename

   build.pipe(streamify(uglify())) if production
@@ -54,6 +60,7 @@
   gulp
     .src paths.templates.source
     .pipe(jade(pretty: not production))
+    .on 'error', handleError
     .pipe gulp.dest paths.templates.destination
     .pipe livereload(reloadServer)

@@ -61,6 +68,7 @@
   styles = gulp
     .src paths.styles.source
     .pipe(stylus({set: ['include css']}))
+    .on 'error', handleError
     .pipe prefix 'last 2 versions', 'Chrome 34', 'Firefox 28', 'iOS 7'

   styles = styles.pipe(CSSmin()) if production
@@ -92,6 +100,8 @@

   bundle.on 'update', ->
     build = bundle.bundle(debug: not production)
+      .on 'error', handleError
+
       .pipe source paths.scripts.filename

     build


save_on_enter event

save_on_enter event

--- client/src/views/text_edit_view.coffee
+++ client/src/views/text_edit_view.coffee
@@ -15,6 +15,7 @@
     "drop .content-text-field": "delayedResize"
     "keydown .content-text-field": "delayedResize"
     "blur :input": "addPlaceholder"
+    "keyup .content-text-field"  : "saveOnEnter"

   initialize: (options) ->
     @type   = options.type
@@ -72,5 +73,9 @@
   startEdit: =>
     @render(edit: true)

+  saveOnEnter: (e) =>
+    if e.keyCode == 13 && @type == 'input'
+      @saveContent()
+
   onClose: ->


do not mess with history when changing videos

--- src/scripts/controllers/subreddit.coffee
+++ src/scripts/controllers/subreddit.coffee
@@ -104,7 +104,7 @@
             r: subreddit
             v: post.id
         }, {
-            location: true
+            location: false
             reload: false
             notify: false
         }


Fix #48 copy size classes to no button for inlineConfirm widget

--- src/scripts/widgets/inlineConfirm.coffee
+++ src/scripts/widgets/inlineConfirm.coffee
@@ -34,10 +34,14 @@
     float = $btn.css("float");
     $btn.attr("class").split(" ").forEach (c) ->
       if c.indexOf("btn") != 0
-          $container.addClass(c);
-          $btn.removeClass(c);
+          $container.addClass c
+          $btn.removeClass c
       else
-        $yesBtn.addClass(c);
+        $yesBtn.addClass c
+        if c == 'btn-lg' or c == 'btn-sm' or c == 'btn-mini'
+          $noBtn.addClass c
+
+

     $btn.css("float", float);
     $btn.addClass("original-button");


Make error use the standard addError function.

--- app/assets/service/upload.coffee
+++ app/assets/service/upload.coffee
@@ -1,8 +1,8 @@
 'use strict'

 app.factory('uploadService', [
-  'routerService',
-  (router) ->
+  'routerService', 'messageService'
+  (router, messages) ->
     removedAsset: undefined

     # callbacks for ng-flow:
@@ -14,7 +14,10 @@

     fileError: (file, message, flow) ->
       file.abort()
-      alert("There was an error uploading the file")
+      messages.addError
+        type: 'red'
+        body: message
+        owner: this

     fileProgress: (file) ->
       file.store.progress = file.progress()


bootstrap needs jquery

bootstrap needs jquery

--- app/client/scripts/client.coffee
+++ app/client/scripts/client.coffee
@@ -1,2 +1,3 @@
 _ = require('lodash')
+window.jQuery = require('jquery')
 require('bootstrap')


change logging message to be different from LatexRunner

--- app/coffee/CompileManager.coffee
+++ app/coffee/CompileManager.coffee
@@ -28,7 +28,7 @@
 		compileDir = getCompileDir(request.project_id, request.user_id)

 		timer = new Metrics.Timer("write-to-disk")
-		logger.log project_id: request.project_id, user_id: request.user_id, "starting compile"
+		logger.log project_id: request.project_id, user_id: request.user_id, "syncing resources to disk"
 		ResourceWriter.syncResourcesToDisk request.project_id, request.resources, compileDir, (error) ->
 			if error?
 				logger.err err:error, project_id: request.project_id, user_id: request.user_id, "error writing resources to disk"


remove unused dryRun option

remove unused dryRun option

Co-Authored-By: Jakob Ackermann <caa3e4f01d56b797520cbfed54bdab4c5e3b0ebb@outlook.com>

--- app/coffee/HttpController.coffee
+++ app/coffee/HttpController.coffee
@@ -219,7 +219,6 @@
 		options =
 			limit : req.query.limit || 1000
 			timeout: 5 * 60 * 1000
-			dryRun : req.query.dryRun || false
 			min_delete_age: req.query.min_delete_age || 5 * 60 * 1000
 		res.send 204
 		# run the flush in the background


set up key events for multi-select and opening local network

--- app/coffeescripts/AppView.coffee
+++ app/coffeescripts/AppView.coffee
@@ -18,8 +18,7 @@
     lmenu.createHTML()
     lmenu.attachEvents()
     self = @
-    google.maps.event.addDomListener(window, 'keydown', (event) -> self._setShift(event))
-    google.maps.event.addDomListener(window, 'keyup', (event) -> self._setShift(event))
+    google.maps.event.addDomListener(window, 'keydown', (event) -> self._setKeyEvents(event))
     $a.broker.on('map:upload_complete', @_displayMap, @)
     @

@@ -62,7 +61,10 @@
     new $a.MapNetworkModel()
     @mapView = new $a.MapNetworkView $a.models

-  _setShift: (e) ->
+  _setKeyEvents: (e) ->
+    # Open Local Network SHIFT-A
+    $("#uploadField").click() if e.type == 'keydown' and $a.SHIFT_DOWN and e.keyCode == 65
+
+    # Set multi-select of map elements with the shift key
     $a.SHIFT_DOWN = false
     $a.SHIFT_DOWN = true if e.type == 'keydown' and e.keyCode == 16
-


Use property vs state to determine toggled status

--- src/components/reference-book/teacher-content-toggle.cjsx
+++ src/components/reference-book/teacher-content-toggle.cjsx
@@ -3,25 +3,21 @@

 TeacherContentToggle = React.createClass

-  getInitialState: ->
-    isShowing: false
-
   propTypes:
     onChange: React.PropTypes.func.isRequired
+    isShowing: React.PropTypes.bool.isRequired

   onClick: ->
-    isShowing = not @state.isShowing
-    @setState({isShowing})
-    @props.onChange(isShowing)
+    @props.onChange(not @props.isShowing)

   render: ->
-    teacherLinkText = if @state.isShowing
+    teacherLinkText = if @props.isShowing
       'Hide Teacher Edition'
     else
       'Show Teacher Edition'

-    <BS.Button className='btn-sm teacher-edition' onClick={@onClick}>
+    <BS.NavItem className='teacher-edition' onClick={@onClick}>
       {teacherLinkText}
-    </BS.Button>
+    </BS.NavItem>

 module.exports = TeacherContentToggle


better cpp documents (#6474)

better cpp documents (#6474)

* better cpp documents

* no translateJS base on cpp code blocks

Co-authored-by: smallst_wsl <6c42689ff77371401bf43b7d56b93ac1ca3e92d3@163.com>

--- app/lib/aether_utils.coco.coffee
+++ app/lib/aether_utils.coco.coffee
@@ -191,7 +191,7 @@
     # TODO: figure out how we are going to handle array methods in Java

     # Don't substitute these within comments
-    noComment = '^ *([^/\\r\\n]*?)'
+    noComment = '^( *[^/\\r\\n]*?)' # keep leading whitespace
     if language is 'cpp'
       newRegex = new RegExp(noComment + '([^*])new ', 'gm')
       while newRegex.test(s)
@@ -418,7 +418,10 @@
 module.exports.filterMarkdownCodeLanguages = (text, language) ->
   return '' unless text
   currentLanguage = language or me.get('aceConfig')?.language or 'python'
-  excludedLanguages = _.without ['javascript', 'python', 'coffeescript', 'lua', 'java', 'cpp', 'html'], if currentLanguage == 'cpp' then 'javascript' else currentLanguage
+  excludeCpp = 'cpp'
+  unless /```cpp\n[^`]+```\n?/.test text
+    excludeCpp = 'javascript'
+  excludedLanguages = _.without ['javascript', 'python', 'coffeescript', 'lua', 'java', 'cpp', 'html'], if currentLanguage == 'cpp' then excludeCpp else currentLanguage
   # Exclude language-specific code blocks like ```python (... code ...)``
   # ` for each non-target language.
   codeBlockExclusionRegex = new RegExp "```(#{excludedLanguages.join('|')})\n[^`]+```\n?", 'gm'
@@ -446,7 +449,7 @@
       text = text.replace ///(\ a|A)n(\ `#{to}`)///g, "$1$2"
     if not startsWithVowel(from) and startsWithVowel(to)
       text = text.replace ///(\ a|A)(\ `#{to}`)///g, "$1n$2"
-  if currentLanguage == 'cpp'
+  if currentLanguage == 'cpp' and excludeCpp == 'javascript'
     jsRegex = new RegExp "```javascript\n([^`]+)```", 'gm'
     text = text.replace jsRegex, (a, l) =>
       """```cpp


:lipstick:

:lipstick:

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -535,8 +535,7 @@
   getSelectedEntries: ->
     @list.element.querySelectorAll('.selected')

-  deselect: (elementsToDeselect) ->
-    elementsToDeselect ?= @getSelectedEntries()
+  deselect: (elementsToDeselect=@getSelectedEntries()) ->
     selected.classList.remove('selected') for selected in elementsToDeselect
     undefined


:memo:

:memo:

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -351,6 +351,7 @@
     if @entryForPath(activeFilePath)
       @selectEntryForPath(activeFilePath)
     else
+      # If the active file is not part of the project, deselect all entries
       @deselect()

   revealActiveFile: (options = {}) ->


Fix issue when scrolling to the first item in dir

When arrowing through the files with a directory at the bottom of the
tree view, it would not scroll to the first nested file.

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -174,7 +174,9 @@
     selectedEntry = @selectedEntry()
     if selectedEntry
       if selectedEntry.is('.expanded.directory')
-        return if @selectEntry(selectedEntry.find('.entry:first'))
+        if @selectEntry(selectedEntry.find('.entry:first'))
+          @scrollToEntry(@selectedEntry())
+          return
       until @selectEntry(selectedEntry.next('.entry'))
         selectedEntry = selectedEntry.parents('.entry:first')
         break unless selectedEntry.length
@@ -294,9 +296,9 @@

     dialog.miniEditor.getBuffer().on 'changed', =>
       if /\/$/.test(dialog.miniEditor.getText())
-        dialog.prompt.removeClass('icon-file-add').addClass('icon-file-directory-create')
-      else
-        dialog.prompt.removeClass('icon-file-directory-create').addClass('icon-file-add')
+        dialog.promptText.removeClass('icon-file-add').addClass('icon-file-directory-create')
+      else
+        dialog.promptText.removeClass('icon-file-directory-create').addClass('icon-file-add')

     rootView.append(dialog)


Fix routing issue

Fix routing issue

- Remove static routing as it was too greedy and not yet needed.
- Fix some variable names as well.

--- lib/webserver.coffee
+++ lib/webserver.coffee
@@ -28,23 +28,15 @@
 # Root
 app.get '/', (req, res) -> res.render(path.join(generatedPath, 'index.html'))

-# Static Routing
-# Take a standard route that may or may not have a file extension
-# if there is no extension, return the path with a .html extension
-app.get /^\/(\w+)(?:\.)?(\w+)?/, (req, res) ->
-  path = req.params[0]
-  ext  = req.params[1] ? "html"
-  res.render(path.join(basePath, ".generated", "#{path}.#{ext}"))
-
 # Avatars: Basic Route
 app.get '/avatar/:name', (req, res) ->
   id = a.convert(req.params.name)
-  res.sendfile( path.join(staticPath, "img", "avatar#{id}.png") )
+  res.sendfile( path.join(generatedPath, "img", "avatar#{id}.png") )

 # Avatars: Route with custom Size
 app.get '/avatar/:size/:name', (req, res, next) ->
   id = a.convert(req.params.name)
-  imgPath = path.join(staticPath, "img", "avatar#{id}.png")
+  imgPath = path.join(generatedPath, "img", "avatar#{id}.png")
   imager.resize(imgPath, req.params.size, req, res, next)


Docs: add mediaqueries.

Docs: add mediaqueries.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -143,6 +143,7 @@
 			'layout',
 			'links',
 			'images',
+			'mediaqueries',
 			'misc',
 			'functions',
 			'classes'


Copy de-refed schemas to dist

Copy de-refed schemas to dist

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -67,6 +67,10 @@
         files: [
           expand: true, cwd: 'code-examples', src: ['designsystems/*.js'], dest: 'dist/'
         ]
+      fullschemas:
+        files: [
+          expand: true, src: ['full-schema/*.json'], dest: 'dist/', filter: 'isFile'
+        ]

     'gh-pages':
       options:
@@ -124,9 +128,9 @@
   @registerTask 'build', 'Build', (target = 'all') =>
     @task.run 'yaml'
     @file.mkdir 'dist'
-    @task.run 'copy'
     @task.run 'build_examples'
     @task.run 'build_fullschemas'
+    @task.run 'copy'
     @task.run 'aglio'

   @registerTask 'test', 'Build and run tests', (target = 'all') =>


debug( travis ): update mocha test target

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -229,7 +229,7 @@
           mocha:
             ignoreLeaks: true

-          urls: ['http://localhost:<%= connect.test.options.port %>/']
+          urls: ['http://localhost:<%= connect.test.options.port %>/index.html']
           run: false
           reporter: 'mocha-phantom-coverage-reporter'
           timeout: 60000


add the migrate task into grunt

add the migrate task into grunt

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -165,7 +165,7 @@
 		Helpers.buildUpstartScripts()

-	#grunt.registerTask 'migrate', "compile migrations and run them", ['coffee:migrate', 'shell:migrate']
+	grunt.registerTask 'migrate', "compile migrations and run them", ['coffee:migrate', 'shell:migrate']

 	Helpers =


Banana

Banana

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -12,7 +12,6 @@
           "src/_defaults.scss"
           "src/functions/*.scss"
           "src/mixins/*.scss"
-          "src/deprecated/*.scss"
         ]
         dest: "_sunglass.scss"

@@ -33,9 +32,9 @@

         files: [
           expand: true,
-          cwd: "test/",
+          cwd: "tests/",
           src: ["**/*.scss"],
-          dest: "test/",
+          dest: "tests/",
           ext: ".css"
         ]

@@ -45,8 +44,8 @@
           livereload: true
           spawn: false
         files: [
-          "test/**/*.scss"
-          "test/**/index.html"
+          "tests/**/*.scss"
+          "tests/**/index.html"
           "src/**/*.scss"
         ]
         tasks: ["sass"]
@@ -68,7 +67,7 @@
     "copy"
   ]

-  grunt.registerTask "test", [
+  grunt.registerTask "default", [
     "connect"
     "watch"
   ]


Remove unecessary uglify task

Remove unecessary uglify task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -101,14 +101,6 @@
         ext:        '.min.css'

-    # uglify:
-    #   app:
-    #     expand:     true
-    #     cwd:        '<%= dirs.dist %>'
-    #     src:        ['<%= files.js %>', '!**/components/<%= files.js %>']
-    #     dest:       '<%= dirs.dist %>'
-    #     ext:        '.min.js'
-
     useminPrepare:
       html:         '<%= dirs.dist %>/index.html'


Release tasks updated, coffeescript migration issues fixed.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -41,7 +41,7 @@
               latestVersion = data.version
               grunt.config.set 'update.version', latestVersion
               grunt.config.set 'update.themes', data.themes
-              grunt.log.writeln 'We have ', grunt.config.get('bowerConf.version') + ', latest version: ' + latestVersion
+              grunt.log.writeln "We have #{grunt.config.get 'bowerConf.version'}, latest version: #{latestVersion}"
               if latestVersion > grunt.config.get 'bowerConf.version'
                 grunt.log.writeln 'New version is available. Updating to: ' + latestVersion
                 # fetch themes
@@ -59,7 +59,6 @@
               else
                 grunt.task.run 'checkThemes'
                 grunt.task.run 'releaseNewThemes'
-                grunt.task.run 'updateJsonConf'

     clean:
       dist:
@@ -181,13 +180,14 @@

   grunt.registerTask 'releaseNewThemes', () ->
-    grunt.log.writeln 'themes to release', grunt.config.get('update.newThemes').length
     if grunt.config.get('update.newThemes').length > 0
+      grunt.log.writeln 'Themes to release', grunt.config.get('update.newThemes').length
       grunt.config.set 'update.themes', grunt.config.get 'update.newThemes'
       grunt.task.run 'fetchBootstrapFiles'
       grunt.task.run 'release'
+      grunt.task.run 'updateJsonConf'
     else
-      grunt.log.write 'All themes up to date. Version: ' + grunt.config.get 'pkg.bootswatch.version'
+      grunt.log.write "All is well! Themes are up to date. Version: #{grunt.config.get 'bowerConf.version'}."

   grunt.registerTask 'checkThemes', () ->
@@ -212,7 +212,7 @@
         else
           grunt.log.writeln theme.name + ' up to date. (' + data.version + ')'

-        if release?
+        if release is true
           # new theme release
           grunt.config.set 'http.fetch' + theme.name + '.options.url', theme.css
           grunt.config.set 'http.fetch' + theme.name + '.dest', 'update/' + theme.name.toLowerCase() + '/bootstrap.css'


Fixed wrong path

Fixed wrong path

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -54,7 +54,7 @@
 					'bower_components/js-md5/js/md5.min.js'
 					'bower_components/mousetrap/mousetrap.min.js'
 					'bower_components/mousetrap/plugins/global-bind/mousetrap-global-bind.min.js'
-					'bower_components/jQuery/dist/basicModal.min.js'
+					'bower_components/basicModal/dist/basicModal.min.js'
 					'assets/js/*.js'
 					'cache/.temp/main.js'
 				]


Improved setup command

Improved setup command

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -53,7 +53,11 @@
         ].join(' && ')

       setup:
-        command: "git clone -b gh-pages `git config --get remote.origin.url` gh-pages"
+        command: [
+          "if [ ! -d gh-pages ]; then git clone -b gh-pages `git config --get remote.origin.url` gh-pages; fi"
+          "mkdir -p build"
+          "npm install -g coffee-script jison jison-lex simple-http-server"
+        ].join(' && ')

       test:
         command: "node build/demo.js"


Fixed wrap by inlining into core

Fixed wrap by inlining into core

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -30,24 +30,20 @@
       src: 'dist/server/factlink.core.min.js'
       dest: 'dist/js/loader/loader_common.js'
     concat:
-      wrap:
-        src: [
-          'dist/js/wrap/first.js',
-          'dist/js/plugins/*.js',
-          'dist/js/models/*.js',
-          'dist/js/views/*.js',
-          'dist/js/util/*.js',
-          'dist/js/initializers/*.js',
-          'dist/js/wrap/last.js'
-        ]
-        dest: 'dist/wrapped.js'
       core:
         options:
           banner: banner_template
         src: [
-          'dist/js/libs/jquery-1.7.2.js',
-          'dist/js/core.js',
-          'dist/wrapped.js'
+          'dist/js/libs/jquery-1.7.2.js'
+          'dist/js/core.js'
+
+          'dist/js/wrap/first.js'
+          'dist/js/plugins/*.js'
+          'dist/js/models/*.js'
+          'dist/js/views/*.js'
+          'dist/js/util/*.js'
+          'dist/js/initializers/*.js'
+          'dist/js/wrap/last.js'
         ]
         dest: 'dist/factlink.core.js'
       loader_DEPRECATED:


(minor) whitespace

(minor) whitespace

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -145,8 +145,6 @@
       'build/factlink_loader_bookmarklet.js'
       'build/factlink_loader_publishers.js'
     ]
-
-
     file_variant_funcs.forEach (file_variant_func) ->
         replacements.forEach (replacement) ->
           input_filename = file_variant_func(replacement.content_file)


options to keep results for local environments

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -71,6 +71,7 @@
 		connect:
 			server:
 				options:
+					# keepalive: true
 					port: 9001
 					hostname: '*'
 					base: '.'
@@ -83,6 +84,7 @@
 						middlewares
 		jasmine:
 			options:
+				# keepRunner: true
 				vendor: [
 					'test/vendor/*.js'
 				]


fixing source map path for less

fixing source map path for less

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -36,7 +36,7 @@
       options:
         sourceMap: true
         sourceMapBasepath: "app"
-        sourceMapRootpath: "/"
+        sourceMapRootpath: "/user/"
       app:
         files:
           "app/_compiled/app.ltr.css": "app/_less/app.less"


Fix inconsistent indentation

Fix inconsistent indentation

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -120,8 +120,8 @@
         command: "npm publish"

   grunt.registerTask "release", "Release a new version, push it and publish it", (target) ->
-      target = "patch" unless target
-      grunt.task.run "bump-only:#{target}", "compile", "bump-commit", "shell:publish"
+    target = "patch" unless target
+    grunt.task.run "bump-only:#{target}", "compile", "bump-commit", "shell:publish"

   # tasks.
   grunt.registerTask 'compile', [


Update gruntfile to compile coffeescript when releasing.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -102,7 +102,18 @@
   # Load all our grunt tasks
   require('load-grunt-tasks') grunt;

+  # We rename the release task so we can use it in our custom task (below)
+  grunt.renameTask 'release', 'publish'
+
   # Our default grunt task compiles our coffeescript lib then runs our server
   grunt.registerTask 'default', ['concurrent:dev']
+  # Our test task sets the environment to be test, and then runs our unit tests
   grunt.registerTask 'test', ['env:test', 'mochaTest']

+  # This is our custom release task that ensures coffeescript compiles first
+  grunt.registerTask 'release', "compile coffeescript, bump version, git tag,
+    git push, npm publish", (target) ->
+    target ?= 'patch'
+    grunt.task.run ['coffee', "publish:#{target}"]
+
+


remove este-library/node_modules/**/*.* from coffeeScripts gruntfile option

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -8,6 +8,7 @@
   coffeeScripts = [
     'bower_components/este-library/**/*.coffee'
     '!bower_components/este-library/Gruntfile.coffee'
+    '!bower_components/este-library/node_modules/**/*.*'
     'client/app/js/**/*.coffee'
     'server/**/*.coffee'
   ]


Compile the RequestQueueJob

Compile the RequestQueueJob

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -23,6 +23,7 @@
         "src/request.coffee"
         "src/response.coffee"
         "src/request-queue.coffee"
+        "src/request-queue-job.coffee"
       ]
       spec: ["spec/*.coffee"]


search bar

search bar

--- components/search_bar/client/view.coffee
+++ components/search_bar/client/view.coffee
@@ -4,6 +4,7 @@
 sd = require("sharify").data
 mediator = require '../../../lib/mediator.coffee'
 SearchBlocks = require '../../../collections/search_blocks.coffee'
+analytics = require '../../../lib/analytics.coffee'

 resultsTemplate = -> require('../templates/results.jade') arguments...

@@ -59,7 +60,9 @@
       data:
         q: query
         per: 4
-      success: => @searchLoaded()
+      success: =>
+        analytics.track.click "Search query"
+        @searchLoaded()

   getQuery: ->
     query = @$input.val()?.trim()


Explicitly set radix to avoid problems with old ECMAScript4 browsers

Older browsers use octal(radix=8) for strings starting with 0 so, even
though I doubt we would run into this issue given our versioning
strategy, it's better to be safe than sorry here.

--- coffee/cilantro/models/exporter.coffee
+++ coffee/cilantro/models/exporter.coffee
@@ -36,9 +36,9 @@
             if attrs? and attrs._links?
                 if attrs.version? and attrs.version.split(".").length == 3
                     versionFields = attrs.version.split(".")
-                    @version = [parseInt(versionFields[0]),
-                                parseInt(versionFields[1]),
-                                parseInt(versionFields[2])]
+                    @version = [parseInt(versionFields[0], 10),
+                                parseInt(versionFields[1], 10),
+                                parseInt(versionFields[2], 10)]

                 for key in Object.keys(attrs._links)
                     # Ignore the exporter endpoint itself


simplify

simplify

--- src/main.coffee
+++ src/main.coffee
@@ -27,12 +27,8 @@
 	then: (onFulfilled, onRejected) ->
 		self = @

-		offset = @_thenCount * 4
-
-		addHandler self, offset, onFulfilled, onRejected
-
 		new Promise (resolve, reject) ->
-			chainHandlers self, offset, resolve, reject
+			addHandler self, onFulfilled, onRejected, resolve, reject

 # ********************** Private **********************

@@ -77,43 +73,30 @@
 		return

 	# Push new handler to current promise.
-	addHandler = (self, offset, onFulfilled, onRejected) ->
+	addHandler = (self, onFulfilled, onRejected, resolve, reject) ->
+		offset = self._thenCount * 4
+
 		switch self._state
 			when $pending
 				self._handlers[offset] = onFulfilled
 				self._handlers[offset + 1] = onRejected
+				self._handlers[offset + 2] = resolve
+				self._handlers[offset + 3] = reject
 				self._thenCount++

 			when $resolved
-				self._handlers[offset] = onFulfilled self._value
+				chainHandler self._value, onFulfilled, resolve

 			when $rejected
-				self._handlers[offset + 1] = onRejected self._value
-		return
-
-	# A new promsie created by then will use it to chain the current promise
-	# to the new promise.
-	chainHandlers = (self, offset, resolve, reject) ->
-		switch self._state
-			when $pending
-				self._handlers[offset + 2] = resolve
-				self._handlers[offset + 3] = reject
-
-			when $resolved
-				chainHandler self._handlers[offset], 0, resolve
-
-			when $rejected
-				# TODO: Decide when to use reject of chained promise.
-				chainHandler self._handlers[offset + 1], 0, resolve
-
+				chainHandler self._value, onRejected, reject
 		return

 	# Chain value to a handler, then handler may be a promise or a function.
-	chainHandler = (value, handler) ->
+	chainHandler = (value, onFulfilled, onRejected) ->
 		if value and typeof value.then == 'function'
-			value.then handler
+			value.then onFulfilled, onRejected
 		else
-			handler value
+			onFulfilled value
 		return

 	# It will produce a trigger function to user.


fi: test case

fi: test case

--- tests/integration/components/pricing-plan-test.coffee
+++ tests/integration/components/pricing-plan-test.coffee
@@ -13,4 +13,4 @@

   @render hbs """{{pricing-plan}}"""

-  assert.equal @$().text().trim(), 'Pay $undefined USDEnter the number of appsPlease Enter the number of apps you would like to scan below:Pay $NaN USD'
+  assert.equal @$().text().trim(), '1app(s)Pay $NaN USD'


Removing useless require

Removing useless require

--- tests/www/tests/functional/routing.coffee
+++ tests/www/tests/functional/routing.coffee
@@ -2,7 +2,6 @@
 colors = require 'colors'

 conds = require '../../utils/conditions'
-quit = require '../../utils/quit'
 Hook = require '../../utils/hooks'


fix command panel specs that I broke

fix command panel specs that I broke

We're now refocusing the preview list instead of the editor

--- src/packages/command-panel/spec/command-panel-spec.coffee
+++ src/packages/command-panel/spec/command-panel-spec.coffee
@@ -438,13 +438,13 @@
         expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[0]

     describe "when core:confirm is triggered on the preview list", ->
-      it "opens the operation's buffer, selects and scrolls to the search result, and focuses the active editor", ->
+      it "opens the operation's buffer, selects and scrolls to the search result, and refocuses the preview list", ->
         rootView.height(200)
         rootView.attachToDom()

         waitsForPromise -> commandPanel.execute('X x/apply/') # use apply because it is at the end of the file
         runs ->
-          spyOn(rootView, 'focus')
+          spyOn(previewList, 'focus')
           executeHandler = jasmine.createSpy('executeHandler')
           commandPanel.on 'core:confirm', executeHandler

@@ -458,13 +458,13 @@
           expect(editSession.getSelectedBufferRange()).toEqual operation.getBufferRange()
           expect(editSession.getSelectedBufferRange()).toEqual operation.getBufferRange()
           expect(editor.isScreenRowVisible(editor.getCursorScreenRow())).toBeTruthy()
-          expect(rootView.focus).toHaveBeenCalled()
+          expect(previewList.focus).toHaveBeenCalled()

           expect(executeHandler).not.toHaveBeenCalled()

     describe "when an operation in the preview list is clicked", ->
-      it "opens the operation's buffer, selects the search result, and focuses the active editor", ->
-        spyOn(rootView, 'focus')
+      it "opens the operation's buffer, selects the search result, and refocuses the preview list", ->
+        spyOn(previewList, 'focus')
         operation = previewList.getOperations()[4]

         previewList.find('li.operation:eq(4) span').mousedown()
@@ -473,4 +473,4 @@
         editSession = rootView.getActiveEditSession()
         expect(editSession.buffer.getPath()).toBe project.resolve(operation.getPath())
         expect(editSession.getSelectedBufferRange()).toEqual operation.getBufferRange()
-        expect(rootView.focus).toHaveBeenCalled()
+        expect(previewList.focus).toHaveBeenCalled()


In-code docs fix

In-code docs fix

--- files.coffee
+++ files.coffee
@@ -1619,7 +1619,6 @@
 @name formatFleURL
 @param {Object} fileRef - File reference object
 @param {String} version - [Optional] Version of file you would like build URL for
-@param {Boolean}  pub   - [Optional] is file located in publicity available folder?
 @summary Returns formatted URL for file
 @returns {String} Downloadable link
 ###


Adjusted formatting to break chain into seperate lines for better legibility

--- test/integration/controllers/v1/ApiController.test.coffee
+++ test/integration/controllers/v1/ApiController.test.coffee
@@ -10,20 +10,39 @@
 ###
 describe 'Requests to the root (/api) path', ->
   it 'GET: Returns a 200 status code', (done) ->
-    request(sails.hooks.http.app).get('/api').expect 200, done
+    request(sails.hooks.http.app)
+      .get('/api')
+      .expect 200, done
     return
   return
   it 'GET: Returns JSON format', (done) ->
-    request(sails.hooks.http.app).get('/api').expect 'content-type', /json/, done
+    request(sails.hooks.http.app)
+      .get('/api')
+      .expect 'content-type', /json/, done
     return
   return
   it 'GET: Redirects to v1 path and returns v1 route details', (done) ->
-    request(sails.hooks.http.app).get('/api').expect('content-type', /json/).end (err, res) ->
-      if err
-        throw err
-      res.body.should.have.property 'v1'
-      res.body.v1.should.endWith '/api/v1'
-      done()
-      return
+    request(sails.hooks.http.app)
+      .get('/api')
+      .expect('content-type', /json/)
+      .end (err, res) ->
+        if err
+          throw err
+        res.body.should.have.property 'v1'
+        res.body.v1.should.endWith '/api/v1'
+        done()
+        return
     return
   return
+
+###*
+# Test API V1 information / status endpoint.
+###
+
+describe 'Requests to v1 root (/api/v1) path', ->
+  it 'GET: Returns a 200 status code', (done) ->
+    request(sails.hooks.http.app)
+      .get('/api/v1')
+      .expect 200, done
+    return
+  return


fix migration phase check

fix migration phase check

--- services/document-updater/app/coffee/RedisMigrationManager.coffee
+++ services/document-updater/app/coffee/RedisMigrationManager.coffee
@@ -114,7 +114,7 @@
 		@rclient_new = redis.createClient(new_settings)
 		@new_key_schema = new_settings.key_schema
 		@migration_phase = new_settings.migration_phase
-		throw new Error("invalid migration phase") unless @migration_phase in ['prepare', 'start', 'switch', 'complete']
+		throw new Error("invalid migration phase") unless @migration_phase in ['prepare', 'switch', 'rollback']

 	getMigrationStatus: (key, migrationKey, callback) ->
 		async.series [
@@ -132,7 +132,7 @@
 		project_id = getProjectId(key)
 		migrationKey = @new_key_schema.projectHistoryMigrationKey({project_id})

-		@getMigrationStatus key, migrationKey, (err, migrationKeyExists, newQueueExists, oldQueueExists) ->
+		@getMigrationStatus key, migrationKey, (err, migrationKeyExists, newQueueExists, oldQueueExists) =>
 			return callback(err) if err?
 			# In all cases, if the migration key exists we must always write to the
 			# new redis, unless we are rolling back.
@@ -189,7 +189,7 @@
 					logger.debug {project_id}, "using old client because migration key does not exist"
 					return callback(null, @rclient_old)
 			else
-				logger.error {key: key}, "unknown migration phase"
+				logger.error {key: key, migration_phase: @migration_phase}, "unknown migration phase"
 				callback(new Error('invalid migration phase'))
 	multi: () ->
 		new Multi(@)


update a data.coffee after cake build-all

--- Src/coffeescript/cql-execution/test/elm/interval/data.coffee
+++ Src/coffeescript/cql-execution/test/elm/interval/data.coffee
@@ -56592,147 +56592,6 @@
                      }
                   },
                   "high" : {
-                     "type" : "DateTime",
-                     "year" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "2013",
-                        "type" : "Literal"
-                     },
-                     "month" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "1",
-                        "type" : "Literal"
-                     },
-                     "day" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "1",
-                        "type" : "Literal"
-                     },
-                     "hour" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "0",
-                        "type" : "Literal"
-                     },
-                     "minute" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "0",
-                        "type" : "Literal"
-                     },
-                     "second" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "0",
-                        "type" : "Literal"
-                     },
-                     "millisecond" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "0",
-                        "type" : "Literal"
-                     }
-                  }
-               }, {
-                  "lowClosed" : true,
-                  "highClosed" : true,
-                  "type" : "Interval",
-                  "low" : {
-                     "type" : "DateTime",
-                     "year" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "2013",
-                        "type" : "Literal"
-                     },
-                     "month" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "1",
-                        "type" : "Literal"
-                     },
-                     "day" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "1",
-                        "type" : "Literal"
-                     },
-                     "hour" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "0",
-                        "type" : "Literal"
-                     },
-                     "minute" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "0",
-                        "type" : "Literal"
-                     },
-                     "second" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "0",
-                        "type" : "Literal"
-                     },
-                     "millisecond" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "0",
-                        "type" : "Literal"
-                     }
-                  },
-                  "high" : {
-                     "type" : "DateTime",
-                     "year" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "2030",
-                        "type" : "Literal"
-                     },
-                     "month" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "1",
-                        "type" : "Literal"
-                     },
-                     "day" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "1",
-                        "type" : "Literal"
-                     },
-                     "hour" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "0",
-                        "type" : "Literal"
-                     },
-                     "minute" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "0",
-                        "type" : "Literal"
-                     },
-                     "second" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "0",
-                        "type" : "Literal"
-                     },
-                     "millisecond" : {
-                        "valueType" : "{urn:hl7-org:elm-types:r1}Integer",
-                        "value" : "0",
-                        "type" : "Literal"
-                     }
-                  }
-               } ]
-            }
-         }, {
-            "name" : "UnknownBegMayMeetAfterDateIvl",
-            "context" : "Patient",
-            "accessLevel" : "Public",
-            "expression" : {
-               "type" : "MeetsBefore",
-               "operand" : [ {
-                  "lowClosed" : false,
-                  "highClosed" : false,
-                  "type" : "Interval",
-                  "low" : {
-                     "asType" : "{urn:hl7-org:elm-types:r1}DateTime",
-                     "type" : "As",
-                     "operand" : {
-                        "type" : "Null"
-                     },
-                     "asTypeSpecifier" : {
-                        "name" : "{urn:hl7-org:elm-types:r1}DateTime",
-                        "type" : "NamedTypeSpecifier"
-                     }
-                  },
-                  "high" : {
                      "localId" : "542",
                      "type" : "DateTime",
                      "year" : {


minor commenting out debug

minor commenting out debug

--- fizzylogo_tokenizer.coffee
+++ fizzylogo_tokenizer.coffee
@@ -108,8 +108,7 @@
   codeWithoutStrings = code.replace(/"((?:[^"\\\n]|\\.)*)"/g, (all, quoted) ->
     if DEBUG_STRINGIFICATION_CHECKS
       stringsTable_TO_CHECK_CONVERTIONS[ValidIDfromString quoted] = quoted
-    console.log "$STRING_TOKEN_" + (ValidIDfromString quoted)
-    console.log "i.e." + quoted
+    #console.log "$STRING_TOKEN_" + (ValidIDfromString quoted) + " i.e." + quoted
     return "$STRING_TOKEN_" + (ValidIDfromString quoted)
   )
   return codeWithoutStrings


fix collection.add

fix collection.add

--- model.coffee
+++ model.coffee
@@ -53,7 +53,7 @@
 				else
 					# merge input item into existing model
 					model = _.find @models, (model) =>
-						model[@idAttribute] == item[@idAttribute]
+						model[@$idAttribute] == item[@$idAttribute]
 					_.extend model, _.omit(item, '$$hashKey')

 		remove: (models, opts = {}) ->


Always allow full-excerpting on unpositioned asset

Even if there is currently a selection

--- app/assets/volume/slot.coffee
+++ app/assets/volume/slot.coffee
@@ -220,7 +220,10 @@
       return

     getSelection = ->
-      if ruler.selection.empty then new Segment(ruler.position) else ruler.selection
+      if ruler.selection.empty
+        new Segment(ruler.position)
+      else
+        ruler.selection

     $scope.addBlank = () ->
       unless blank
@@ -314,6 +317,7 @@
             updateRange()
             sortTracks()
             @finishPosition()
+            @updateExcerpt()
           , (res) =>
             @finishPosition()
             messages.addError
@@ -334,8 +338,8 @@

       updateExcerpt: () ->
         @excerpt = undefined
-        seg = getSelection()
-        return if !@asset || !seg || (@segment.full && !seg.full) || !@segment.overlaps(seg)
+        seg = if @segment.full then @segment else getSelection()
+        return if !@asset || !seg || !@segment.overlaps(seg)
         excerpt = @excerpts.find((e) -> seg.overlaps(e.segment))
         @excerpt =
           if !excerpt


Fixed a bug introduced with last feature

--- app/coffee/controllers.coffee
+++ app/coffee/controllers.coffee
@@ -55,7 +55,7 @@

     $scope.checkPaths = ->
       @paths = @domSearcher.getAllPaths()
-      @selectedPath = @paths[0]
+      @selectedPath = @paths[0].path
       @canSearch = true

     $scope.checkPathsDelayed = ->


solve tsProduction browserify error halting compilation

--- gulp/ts.coffee
+++ gulp/ts.coffee
@@ -31,10 +31,11 @@
 gulp.task 'ts', client

 gulp.task 'tsProduction', ->
-  browserify('./src/app.ts')
+  browserify './src/app.ts'
     .plugin tsify
     .transform babelify
     .bundle()
+    .on 'error', gutil.log
     .pipe source('app.js')
     .pipe buffer()
     .pipe gulp.dest("#{config.path}/scripts")


Check that index page is legitimate

Check that index page is legitimate

--- frontend/view_index.coffee
+++ frontend/view_index.coffee
@@ -4,7 +4,10 @@
 base = global.wolisconfig.test_url

 casper.start base, ->
-  this.test.assertHttpStatus 200
+  @test.assertHttpStatus 200
+
+  # check that the index page has expected text
+  @test.assertTextExists 'Your first forum'

 casper.run ->
   @test.done()


base relations specs

base relations specs

--- test/specs/source/base_model_spec.coffee
+++ test/specs/source/base_model_spec.coffee
@@ -66,8 +66,35 @@

   describe "Relations", ->

+    p0 = new Person({id: 1})
+    p1 = new Person({id: 2})
+    n0 = new Name({name: "abc"})
+    n1 = new Name({name: "qwe"})
+    g0 = new Group({name: "group-0"})
+    g1 = new Group({name: "group-1"})
+    g2 = new Group({name: "group-2"})
+
+    p0.add_group(g0)
+    p0.add_group(g1)
+    p1.add_group(g2)
+    p0.add_name(n0)
+
     it "has_*", ->
+      expect(p0.attributes.length).toEqual(3)
+      expect(p0.group().length).toEqual(2)

+      expect(p1.group().length).toEqual(1)
+
+      # feedback
+      expect(g0.attributes.length).toEqual(2)
+
+      expect(g0.get('person_id')).toEqual(1)
+      expect(g1.get('person_id')).toEqual(1)
+      expect(g2.get('person_id')).toEqual(2)
+
+      expect(() ->
+        p0.add_name(n1)
+      ).toThrowError()

     describe "JSON", ->


InputView: autogrow resize deferred for it to get the correct value of the input

--- src/components/inputs/inputview.coffee
+++ src/components/inputs/inputview.coffee
@@ -460,9 +460,11 @@
       @_clone.detach()
       @$()[0].style.height = 'none' # hack to set to initial

-    @on "keyup", (event) => @resize()
-
-  resize: ->
+    @on 'keydown', => KD.utils.defer @bound 'resize'
+
+
+  resize: (event) ->
+
     return  unless @_clone

     @_clone.appendTo 'body' unless document.body.contains @_clone[0]


'fix' for slug problem. needs further investigation

--- client/landing/app/Applications/Apps.kdapplication/Views/AppsListItemView.coffee
+++ client/landing/app/Applications/Apps.kdapplication/Views/AppsListItemView.coffee
@@ -82,6 +82,6 @@

{a[href="#"]{#(title)}}

{{@utils.shortenText #(body)}}

-      Application Page →
+      Application Page →

     """


No longer using args...

No longer using args...

--- game/lib/pinManager.coffee
+++ game/lib/pinManager.coffee
@@ -9,7 +9,7 @@
   openPin: (pin, val)->
     taskRunner = taskRunner.then ->
       return new Promise (resolve, reject)->
-        console.log 'Open Pin', args
+        console.log 'Open Pin', pin, val
         Promise.promisify(gpio.open)(pin, val).then (->
           openedPins[pin] = true
           resolve()
@@ -24,28 +24,28 @@

       return new Promise (resolve, reject)->
         setTimeout ->
-          console.log 'Close Pin', args
-          Promise.promisify(gpio.close)(args...).then (->
+          console.log 'Close Pin', pin
+          Promise.promisify(gpio.close)(pin).then (->
             delete openedPins[pin]
             resolve()
           ), (err)->
-            console.log 'Error opening pin ', args
+            console.log 'Error opening pin ', pin
             resolve(err)
         , 100

-  writePin: (args...)->
+  writePin: (pin, val)->
     taskRunner = taskRunner.then ->
       return new Promise (resolve, reject)->
-        console.log 'Write Pin', args
-        Promise.promisify(gpio.write)(args...).then resolve, (err)->
-          console.log 'Error writing pin ', args
+        console.log 'Write Pin', pin, val
+        Promise.promisify(gpio.write)(pin, val).then resolve, (err)->
+          console.log 'Error writing pin ', pin, val
           reject(err)

-  readPin: (args...)->
+  readPin: (pin)->
     taskRunner = taskRunner.then ->
       return new Promise (resolve, reject)->
-        Promise.promisify(gpio.read)(args...).then resolve, (err)->
-          console.log 'Error reading pin ', args
+        Promise.promisify(gpio.read)(pin).then resolve, (err)->
+          console.log 'Error reading pin ', pin
           reject(err)

   cleanup: ->


Avoid redirect by adding www to the URL

Avoid redirect by adding www to the URL

--- src/scripts/clojure.coffee
+++ src/scripts/clojure.coffee
@@ -19,7 +19,7 @@
   robot.respond /(clojure|clj)\s+(.*)/i, (msg)->
     script = msg.match[2]

-    msg.http("http://tryclj.com/eval.json")
+    msg.http("http://www.tryclj.com/eval.json")
       .query(expr: script)
       .headers(Cookie: "ring-session=#{ringSessionID}")
       .get() (err, res, body) ->


Removed one of the rimshot videos due to YouTube take down

The ~1 second video was presumably a grave threat to the media industry

--- src/scripts/rimshot.coffee
+++ src/scripts/rimshot.coffee
@@ -14,7 +14,6 @@
 #   mrtazz

 shots = [
-    'http://www.youtube.com/watch?v=DH5p5iMEbrE',
     'http://www.youtube.com/watch?v=6zXDo4dL7SU',
     'http://www.youtube.com/watch?v=GnOl4VcV5ng',
     'http://www.youtube.com/watch?v=3gPV2wzNNyo'


Making the regular expression a little more fool proof.

--- src/scripts/shorten.coffee
+++ src/scripts/shorten.coffee
@@ -1,9 +1,9 @@
 # Shorten URLs with bit.ly
 #
-# (bitly|shorten) me <url> - Shorten the URL using bit.ly
+# (bitly|shorten) (me) <url> - Shorten the URL using bit.ly

 module.exports = (robot) ->
-  robot.respond /(bitly|shorten) (me)? (.+)$/, (msg) ->
+  robot.respond /(bitly|shorten)\s?(me)?\s?(.+)$/, (msg) ->
     msg
       .http("http://api.bitly.com/v3/shorten")
       .query


support links from play.spotify.com

support links from play.spotify.com

--- src/scripts/spotify.coffee
+++ src/scripts/spotify.coffee
@@ -22,7 +22,7 @@

 spotify =
   link: /// (
-    ?: http://open.spotify.com/(track|album|artist)/
+    ?: (http|https)://(open|play).spotify.com/(track|album|artist)/
      | spotify:(track|album|artist):
     ) \S+ ///


info instead of log and use #{}

info instead of log and use #{}

--- lib/paas.coffee
+++ lib/paas.coffee
@@ -35,8 +35,7 @@
     # 2. Send a request to proxy to add a new route
     installApp: (app, callback) ->
         console.info "Request haibu for spawning #{app.name}..."
-
-        console.log "haibu descriptor : "+JSON.stringify(app.getHaibuDescriptor())
+        console.info "haibu descriptor : #{JSON.stringify(app.getHaibuDescriptor())}"

         @client.start app.getHaibuDescriptor(), (err, result) =>
             if err


Sort out ES6 import confusion; add POST to /api/me to record user visit

--- app/routes/application.coffee
+++ app/routes/application.coffee
@@ -1,23 +1,30 @@
 `import BaseRoute from './_base'`
+`import ajax from 'ic-ajax'`

 Route = BaseRoute.extend
   model: ->
+    mePath = '/api/me'
+
     # return the current user as the model if authenticated, otherwise a blank object
     new Ember.RSVP.Promise (resolve, reject) =>
-      promise = $.ajax
-        url: '/api/me'
-        dataType: 'json'
-      promise.done (result) =>
-        user = Ember.Object.create(result)
-        if result.isAnon
-          @set 'session.isAnon', true
-          @set 'session.currentUser', null
-        else
-          @set 'session.isAnon', false
-          @set 'session.currentUser', user
-        resolve(user)
-      promise.fail (result) ->
-        reject()
+      ajax
+          url: mePath
+          dataType: 'json'
+        .then (result) =>
+            user = Ember.Object.create(result)
+            if result.isAnon
+              @set 'session.isAnon', true
+              @set 'session.currentUser', null
+            else
+              @set 'session.isAnon', false
+              @set 'session.currentUser', user
+            resolve(user)
+            ajax
+              url: mePath
+              method: 'post'
+          ,
+            (result) ->
+              reject()
   clearSession: ->
     @set 'session.currentUser', undefined
     @set 'session.isAnon', true


hub/monitoring: db counting concurrent problems

--- src/smc-hub/rethink.coffee
+++ src/smc-hub/rethink.coffee
@@ -215,6 +215,7 @@
                                                                        "Total number of 'create_project' calls", ['account_id'])
         @_concurrent_gauge              = MetricsRecorder.new_gauge(   'db_concurrent',         'Number of concurrent db queries')
         @_concurrent_quantile           = MetricsRecorder.new_quantile('db_concurrent_summary', 'Distribution of concurrent db queries')
+        @_concurrent_problems_counter   = MetricsRecorder.new_counter( 'db_concurrent_problems_total', 'counts the number of concurrent errors', ['type', 'reason'])
         @_modified_counter              = MetricsRecorder.new_counter( 'db_modified_total',  "Number of modified documents", ['type'])
         @_query_time_quantile           = MetricsRecorder.new_quantile('db_query_summary',   'Quantile summary of query times')
         # TODO add unique error codes to the reported errors
@@ -386,13 +387,16 @@
                         winston.debug("[#{that._concurrent_queries} concurrent]  rethink: query -- '#{query_string}'")
                         if that._concurrent_queries > that._concurrent_warn
                             winston.debug("rethink: *** concurrent_warn *** CONCURRENT WARN THRESHOLD EXCEEDED!")
+                            that._concurrent_problems_counter.inc(type: 'warn', reason: 'too_many')
                         if that._concurrent_queries > that._concurrent_kill
                             winston.debug("rethink: *** concurrent_kill *** CONCURRENT KILL THRESHOLD EXCEEDED!")
+                            that._concurrent_problems_counter.inc(type: 'exit', reason: 'too_many') # will never show up in the metrics, though ...
                             process.exit(1)

                         if that._concurrent_error and that._concurrent_queries > that._concurrent_error
                             winston.debug("rethink: *** concurrent_error *** CONCURRENT ERROR THRESHOLD #{that._concurrent_error} EXCEEDED -- FAILING QUERY")
                             that._concurrent_queries -= 1
+                            that._concurrent_problems_counter.inc(type: 'error', reason: 'too_many')
                             setTimeout((()=>cb("concurrent_error")), 5*Math.random())
                             return

@@ -403,9 +407,11 @@
                         warning_too_long = ->
                             # if a connection is slow, display a warning
                             winston.debug("[#{that._concurrent_queries} concurrent]  rethink: query '#{query_string}' is taking over #{that._warning_thresh}s!")
+                            that._concurrent_problems_counter.inc(type: 'warn', reason: 'too_long')

                         error_too_long = ->
                             winston.debug("[#{that._concurrent_queries} concurrent]  rethink: query '#{query_string}' is taking over #{that._error_thresh}s so we kill it!")
+                            that._concurrent_problems_counter.inc(type: 'error', reason: 'too_long')
                             winston.debug("rethink: query -- close existing connection")
                             # this close will cause g below to get called with a RqlRuntimeError
                             conn.close (err) ->


DE20883 - Remove invalid RPB test around app height

--- test/spec/roadmapplanningboard/RoadmapPlanningBoardAppSpec.coffee
+++ test/spec/roadmapplanningboard/RoadmapPlanningBoardAppSpec.coffee
@@ -83,13 +83,6 @@
   it 'should render a planning board with a roadmap', ->
     @createApp().then =>
       expect(@planningBoard.timeline.getId()).toBe @timelineStore.first().getId()
-
-  it 'should define board height based on app height', ->
-    @createApp(false, {height: 1000}).then =>
-      #test range as jasmine does not like to render html the same with local and test server
-      boardHeight = @planningBoard.getHeight()
-      expect(boardHeight >= 950).toBe true
-      expect(boardHeight <= 1000).toBe true

   it 'should notify of error if the timeline store fails to load', ->
     @stub @timelineStore, 'load', ->


ng-mfb update

ng-mfb update

--- app/js/app.coffee
+++ app/js/app.coffee
@@ -17,7 +17,7 @@
     ["ngResource", "ui.router", "ngIdle", "app.services", "app.directives", "ui.bootstrap",
      "ui.validate", "xeditable", "pascalprecht.translate", "pageslide-directive", "ui.grid",
      "ngMaterial", "utils.autofocus", "ngMessages", "ui.grid.autoResize", "ngAnimate",
-    "ng-mfb", "angular-carousel"])
+    "angular-carousel", "ng-mfb", "angular-growl"])

 app.run ($rootScope, $location, Idle, $state, $interval, $window, $templateCache, $translate, editableOptions, editableThemes) ->