Use document.scrollingElement for scrolling

This fixes Vimium with the 'Experimental Web Platform Features' flag
enabled, and ensures compatibility across quirks mode scrolling (via
document.body) and standards-compliant scrolling (via
document.documentElement).

--- content_scripts/scroller.coffee
+++ content_scripts/scroller.coffee
@@ -3,6 +3,9 @@
 # input elements. This mechanism allows us to decide whether to scroll a div or to scroll the whole document.
 #
 activatedElement = null
+
+if !('scrollingElement' of document)
+  Object.defineProperty(document, 'scrollingElement', { get: () -> document.body })

 # Return 0, -1 or 1: the sign of the argument.
 # NOTE(smblott; 2014/12/17) We would like to use Math.sign().  However, according to this site
@@ -37,7 +40,7 @@
     name = amount
     # the clientSizes of the body are the dimensions of the entire page, but the viewport should only be the
     # part visible through the window
-    if name is 'viewSize' and el is document.body
+    if name is 'viewSize' and el is document.scrollingElement
       # TODO(smblott) Should we not be returning the width/height of element, here?
       if direction is 'x' then window.innerWidth else window.innerHeight
     else
@@ -82,13 +85,13 @@

 # From element and its parents, find the first which we should scroll and which does scroll.
 findScrollableElement = (element, direction, amount, factor) ->
-  while element != document.body and not isScrollableElement element, direction, amount, factor
-    element = DomUtils.getContainingElement(element) ? document.body
+  while element != document.scrollingElement and not isScrollableElement element, direction, amount, factor
+    element = DomUtils.getContainingElement(element) ? document.scrollingElement
   element

-# On some pages, document.body is not scrollable.  Here, we search the document for the largest visible
+# On some pages, document.scrollingElement is not scrollable.  Here, we search the document for the largest visible
 # element which does scroll vertically. This is used to initialize activatedElement. See #1358.
-firstScrollableElement = (element=document.body) ->
+firstScrollableElement = (element=document.scrollingElement) ->
   if doesScroll(element, "y", 1, 1) or doesScroll(element, "y", -1, 1)
     element
   else
@@ -234,14 +237,14 @@
   # :factor is needed because :amount can take on string values, which scrollBy converts to element dimensions.
   scrollBy: (direction, amount, factor = 1, continuous = true) ->
     # if this is called before domReady, just use the window scroll function
-    if (!document.body and amount instanceof Number)
+    if (!document.scrollingElement and amount instanceof Number)
       if (direction == "x")
         window.scrollBy(amount, 0)
       else
         window.scrollBy(0, amount)
       return

-    activatedElement ||= (document.body and firstScrollableElement()) or document.body
+    activatedElement ||= (document.scrollingElement and firstScrollableElement()) or document.scrollingElement
     return unless activatedElement

     # Avoid the expensive scroll calculation if it will not be used.  This reduces costs during smooth,
@@ -252,7 +255,7 @@
       CoreScroller.scroll element, direction, elementAmount, continuous

   scrollTo: (direction, pos) ->
-    activatedElement ||= (document.body and firstScrollableElement()) or document.body
+    activatedElement ||= (document.scrollingElement and firstScrollableElement()) or document.scrollingElement
     return unless activatedElement

     element = findScrollableElement activatedElement, direction, pos, 1
@@ -261,13 +264,13 @@

   # Is element scrollable and not the activated element?
   isScrollableElement: (element) ->
-    activatedElement ||= (document.body and firstScrollableElement()) or document.body
+    activatedElement ||= (document.scrollingElement and firstScrollableElement()) or document.scrollingElement
     element != activatedElement and isScrollableElement element

   # Scroll the top, bottom, left and right of element into view.  The is used by visual mode to ensure the
   # focus remains visible.
   scrollIntoView: (element) ->
-    activatedElement ||= document.body and firstScrollableElement()
+    activatedElement ||= document.scrollingElement and firstScrollableElement()
     rect = element. getClientRects()?[0]
     if rect?
       # Scroll y axis.


Needed to split up the wp-install task to have it run sequentially as well as remove repeated code

--- gulp-tasks/wordpress.coffee
+++ gulp-tasks/wordpress.coffee
@@ -1,48 +1,73 @@
+
+getWordPressFileSrcName = ->
+  if config.wordpress.version == "latest"
+    "latest.zip"
+  else
+    "wordpress-#{config.wordpress.version}.zip"
+
+
+gulp.task "wp-init", (cb) ->
+  runSequence(
+    "wp-install"
+    "wp-remove-defaults"
+    "wp-install-plugins"
+    cb
+  )
+
+
 # Install the latest instance of WordPress or the version specified
 gulp.task "wp-install", (cb) ->
   helper.out "Installing WordPress"
-
-  if config.wordpress.version == "latest"
-    fileName = "latest.zip"
-  else
-    fileName = "wordpress-#{config.wordpress.version}.zip"

   # Remove WordPress folder
   if fs.existsSync "#{config.wordpress.folder}"
     del.sync(["#{config.wordpress.folder}"], force: true)

   # Get WordPress from the cache folder if a copy is available
-  if fs.existsSync "#{config.base}/.cache/#{fileName}"
-
+  if fs.existsSync "#{config.base}/.cache/#{getWordPressFileSrcName()}"
     helper.out "Using cached source of WordPress"

-    gulp.src "#{config.base}/.cache/#{fileName}"
-      .pipe unzip()
-      .pipe gulp.dest("#{config.base}/.cache")
-
+    runSequence(
+      "wp-extract-from-cache"
+      "wp-copy-from-cache"
+      cb
+    )

   else
     # Download the WordPress instance from wordpress.org
-    helper.out "Downloading WordPress"
+    runSequence(
+      "wp-download"
+      "wp-extract-from-cache"
+      "wp-copy-from-cache"
+      cb
+    )

-    url = "https://wordpress.org/#{fileName}"

-    download(url)
-      # Make a copy to the cache folder
-      .pipe gulp.dest("#{config.base}/.cache")
-      .pipe unzip()
+# Download the WordPress source into the cache folder
+gulp.task "wp-download", ->
+  url = "https://wordpress.org/#{getWordPressFileSrcName()}"

-    # TODO: Not working due to the sequencing need to separate into tasks and runSequence
-    gulp.src "#{config.base}/.cache/wordpress/**/*", base: "wordpress"
-      .pipe gulp.dest(config.wordpress.folder)
+  # Download to the cache folder
+  download(url)
+    .pipe gulp.dest("#{config.base}/.cache")

-    # TODO: Delete folder in the cache

-  cb
+# Extract the WordPress source in the cache directory
+gulp.task "wp-extract-from-cache", ->
+  gulp.src "#{config.base}/.cache/#{getWordPressFileSrcName()}"
+    .pipe unzip()
+    .pipe gulp.dest("#{config.base}/.cache")
+
+
+# Copy the WordPress source from the cache folder
+gulp.task "wp-copy-from-cache", ->
+  gulp.src "#{config.base}/.cache/wordpress/**/*"
+    .pipe gulp.dest(config.wordpress.folder)
+

 # Remove all default plugins and themes
-gulp.task "wp-remove-themes-and-plugins", ->
+gulp.task "wp-remove-defaults", ->
   helper.out "Removing default themes and plugins"

   # Remove default themes and plugins


fix(containers): removed default datahandler, as it caused some nasty sideeffects

--- src/lib/containable.coffee
+++ src/lib/containable.coffee
@@ -11,8 +11,10 @@
     field = name.toLowerCase()
     field = settings.field if settings.field?

+    if typeof settings.datahandler != 'object'
+      throw new Error 'Invalid or no datahandler provided for: "' + name + '"', typeof settings.datahandler
+
     datahandler = settings.datahandler if settings.datahandler?
-    datahandler = new JSONHandler()    unless settings.datahandler?

     first_class = false
     first_class = settings.first_class if settings.first_class?


chat/collabparticipants: add & use push/remove helpers

--- client/activity/lib/models/collaborationchannelparticipants.coffee
+++ client/activity/lib/models/collaborationchannelparticipants.coffee
@@ -65,11 +65,8 @@
   ###
   addVideoParticipant: (nickname, emitEvent = yes) ->

-    index = @state.videoParticipants.indexOf nickname
-
-    if index is -1
-      @state.videoParticipants.push nickname
-      @emitChange()  if emitEvent
+    pushToCollection @state.videoParticipants, nickname, =>
+      @emitEvent()  if emitEvent

   ###*
@@ -80,11 +77,8 @@
   ###
   removeVideoParticipant: (nickname, emitEvent = yes) ->

-    index = @state.videoParticipants.indexOf nickname
-
-    unless index is -1
-      @state.videoParticipants.splice index, 1
-      @emitChange()  if emitEvent
+    removeFromCollection @state.videoParticipants, nickname, =>
+      @emitEvent()  if emitEvent

   ###*
@@ -110,10 +104,7 @@
   ###
   addTalkingParticipant: (nickname) ->

-    index = @state.talkingParticipants.indexOf nickname
-
-    if index is -1
-      @state.talkingParticipants.push nickname
+    pushToCollection @state.talkingParticipants, nickname, =>
       @emitChange()

@@ -124,10 +115,7 @@
   ###
   removeTalkingParticipant: (nickname) ->

-    index = @state.talkingParticipants.indexOf nickname
-
-    unless index is -1
-      @state.talkingParticipants.splice index, 1
+    removeFromCollection @state.talkingParticipants, nickname, =>
       @emitChange()

@@ -138,10 +126,7 @@
   ###
   addVideoActiveParticipant: (nickname) ->

-    index = @state.videoParticipants.indexOf nickname
-
-    if index is -1
-      @state.videoParticipants.push nickname
+    pushToCollection @state.videoParticipants, nickname, =>
       @emitChange()

@@ -152,10 +137,7 @@
   ###
   removeVideoActiveParticipant: (nickname) ->

-    index = @state.videoParticipants.indexOf nickname
-
-    unless index is -1
-      @state.videoParticipants.splice index, 1
+    removeFromCollection @state.videoParticipants, nickname, =>
       @emitChange()

@@ -187,3 +169,43 @@
     @emitChange()

+###*
+ * Pushes to collection with an existential check first.
+ * If item is not there already it will push it and will call the callback. If
+ * item is already there, this is basically a noop. It won't call the callback
+ * and so on. If this is really weird, we can change the accepted callback into
+ * a callback that accepts an `err` object (just like the other ones) to make
+ * it more familiar.
+ *
+ * @param {array} collection
+ * @param {(string|number)} item
+ * @param {function} callback
+###
+pushToCollection = (collection, item, callback) ->
+
+  # if item is not in collection
+  if collection.indexOf(item) is -1
+    collection.push item
+    callback?()
+
+
+###*
+ * Removes from collection with an existential check first.
+ * If item is there already it remove it and will call the callback. If
+ * item is not already there, this is basically a noop. It won't call the callback
+ * and so on. If this is really weird, we can change the accepted callback into
+ * a callback that accepts an `err` object (just like the other ones) to make
+ * it more familiar.
+ *
+ * @param {array} collection
+ * @param {(string|number)} item
+ * @param {function} callback
+###
+removeFromCollection = (collection, item, callback) ->
+
+  # if item is in collection
+  unless index = collection.indexOf(item) is -1
+    collection.splice index, 1
+    callback?()
+
+


force display the prompt and don't let it be hidden when the question changes

--- common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee
+++ common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee
@@ -152,10 +152,13 @@

     @out_of_sync_message = 'The problem state got out of sync.  Try reloading the page.'

-    if @task_number>1
-      @prompt_hide()
-    else if @task_number==1 and @child_state!='initial'
-      @prompt_hide()
+#   force show the prompt
+    @prompt_show()
+
+#    if @task_number>1
+#      @prompt_hide()
+#    else if @task_number==1 and @child_state!='initial'
+#      @prompt_hide()

     @find_assessment_elements()
     @find_hint_elements()


let's just make the dev menu invisible. go to the corner and it'll open

Former-commit-id: 2d479e45d22c77287a952b5bede0bf15d787cda4 [formerly eb026094c7fe4c28cd3718732189d01831bd100f] [formerly 869fe51513b4d9d59820a184ddfa460408008e97 [formerly 11f2f12897ac4fc38bdc98c427607281f4e18ff8]] [formerly b4ab9b5ff77bf5de8cb7b6230653b050241ed898 [formerly c70ab0d57d322f4869c4c7384058276a35bc3f0b] [formerly e80768eb7f782c70538d16a78dabdaee9f9d1f9b [formerly e80768eb7f782c70538d16a78dabdaee9f9d1f9b [formerly aba0c4b3aff9cb6d942bf88c70930f3400cf0182]]]]
Former-commit-id: 912c7c35b06b64d11bd82ec6d8b96098874373be [formerly c8db794b099bc9ca2738895e91e312761085f04d] [formerly 3a5f00945e129868216f8ff8d424f1d22877875f [formerly afede89bff563b010cb6a2f639a2f22d99e6e179]]
Former-commit-id: 2d55bf5661c25e79f66dcda5eb2cdaca7ba1e161 [formerly a9dfe5bb3e9e297f5403608fef8ed3e35904aaf6]
Former-commit-id: 59086e085ab365770515b5d0d3a0f88dfde2a042
Former-commit-id: 153354c0de5309422a474c9d168a70f9ed3c33ab

--- @client/development.coffee
+++ @client/development.coffee
@@ -16,15 +16,13 @@
         zIndex: 9999
         left: 0
         top: 0
-        opacity: if @local.hover_top then 1 else .05
+        opacity: if @local.hover_top then 1 else 0
         backgroundColor: 'black'
         color: 'white'
-        padding: '4px 2px'
+        padding: 10

       onMouseEnter: (e) => @local.hover_top = true; save @local
       onMouseLeave: (e) => @local.hover_top = @local.hover_second = false; save @local
-
-      "D"

       if @local.hover_top
         SPAN null,


remove console log

remove console log

--- BackofficeBundle/Resources/public/coffee/formBehavior/TagCreator.coffee
+++ BackofficeBundle/Resources/public/coffee/formBehavior/TagCreator.coffee
@@ -20,7 +20,6 @@
       element = $(element)
       tags = elements.data('tags')
       url = elements.data('check')
-      console.log url
       grantedNew = elements.data('authorize-new')
       elements.select2(
         tags: tags


LMS: resolving incorrect icon on Instructor Dashboard admin list mgmt

UX-1466

--- lms/static/coffee/src/instructor_dashboard/membership.coffee
+++ lms/static/coffee/src/instructor_dashboard/membership.coffee
@@ -117,7 +117,7 @@

         # create revoke button and insert it into the row
         label_trans = gettext("Revoke access")
-        $revoke_btn = $ _.template('
 <%= label %>
', {label: label_trans}),
+        $revoke_btn = $ _.template('
 <%= label %>
', {label: label_trans}),
           class: 'revoke'
         $revoke_btn.click =>
             @modify_member_access member.email, 'revoke', (error) =>


Update specs

Update specs

--- spec/markdown-preview-spec.coffee
+++ spec/markdown-preview-spec.coffee
@@ -9,155 +9,120 @@
     atom.packages.activatePackage("markdown-preview", immediate: true)
     spyOn(MarkdownPreviewView.prototype, 'renderMarkdown')

-  describe "markdown-preview:show", ->
-    beforeEach ->
-      atom.workspaceView.openSync("file.markdown")
+  describe "when the active item can't be rendered as markdown", ->
+    describe "when the editor does not use the GFM grammar", ->
+      it "does not show a markdown preview", ->
+        spyOn(console, 'warn')

-    describe "when the active item is an editor", ->
-      beforeEach ->
-        atom.workspaceView.attachToDom()
+        waitsForPromise ->
+          atom.workspaceView.open()

-      describe "when the editor does not use the GFM grammar", ->
-        it "does not show a markdown preview", ->
-          spyOn(console, 'warn')
-          atom.workspaceView.openSync()
+        runs ->
           expect(atom.workspaceView.getPanes()).toHaveLength(1)
           atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
           expect(atom.workspaceView.getPanes()).toHaveLength(1)
           expect(console.warn).toHaveBeenCalled()

-      describe "when a preview item has not been created for the editor's uri", ->
-        describe "when there is more than one pane", ->
-          it "shows a markdown preview for the current buffer on the next pane", ->
-            atom.workspaceView.getActivePane().splitRight()
-            [pane1, pane2] = atom.workspaceView.getPanes()
+  describe "when a preview has not been created for the buffer", ->
+    beforeEach ->
+      atom.workspaceView.attachToDom()
+
+      waitsForPromise ->
+        atom.workspaceView.open("file.markdown")
+
+    it "splits the current pane to the right with a markdown preview for the current buffer", ->
+      preview = null
+      [pane1, pane2] = []
+
+      atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
+
+      waitsFor ->
+        MarkdownPreviewView.prototype.renderMarkdown.callCount > 0
+
+      runs ->
+        expect(atom.workspaceView.getPanes()).toHaveLength 2
+        [pane1, pane2] = atom.workspaceView.getPanes()
+
+        expect(pane1.items).toHaveLength 1
+        preview = pane2.activeItem
+        expect(preview).toBeInstanceOf(MarkdownPreviewView)
+        expect(preview.buffer).toBe atom.workspaceView.getActivePaneItem().buffer
+        expect(pane1).toHaveFocus()
+
+  describe "when a preview has been created for the buffer", ->
+    [pane1, pane2, preview] = []
+
+    beforeEach ->
+      atom.workspaceView.attachToDom()
+
+      waitsForPromise ->
+        atom.workspaceView.open("file.markdown")
+
+      runs ->
+        atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
+
+      waitsFor ->
+        MarkdownPreviewView.prototype.renderMarkdown.callCount > 0
+
+      runs ->
+        [pane1, pane2] = atom.workspaceView.getPanes()
+        preview = pane2.activeItem
+        MarkdownPreviewView.prototype.renderMarkdown.reset()
+
+    it "re-renders and shows the existing preview", ->
+      waitsForPromise ->
+        pane2.focus()
+        atom.workspaceView.open()
+
+      runs ->
+        pane1.focus()
+        atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
+
+      waitsFor ->
+        MarkdownPreviewView.prototype.renderMarkdown.callCount > 0
+
+      runs ->
+        expect(pane2.activeItem).toBe preview
+        expect(pane1).toHaveFocus()
+
+    describe "when the buffer is saved", ->
+      describe "when the preview is in the active pane", ->
+        it "re-renders the preview but does not make it active", ->
+          waitsForPromise ->
+            pane2.focus()
+            atom.workspaceView.open(preview.getPath())
+
+          runs ->
+            pane2.activeItem.buffer.emit 'saved'
+
+          waitsFor ->
+            MarkdownPreviewView.prototype.renderMarkdown.callCount > 0
+
+          runs ->
+            expect(pane2).toHaveFocus()
+            expect(pane2.activeItem).not.toBe preview
+
+      describe "when the preview is not in the active pane", ->
+        it "re-renders the preview and makes it active", ->
+          waitsForPromise ->
+            pane2.focus()
+            atom.workspaceView.open(preview.getPath())
+
+          runs ->
             pane1.focus()
+            pane1.activeItem.buffer.emit 'saved'

-            atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
+          waitsFor ->
+            MarkdownPreviewView.prototype.renderMarkdown.callCount > 0

-            preview = pane2.activeItem
-            expect(preview).toBeInstanceOf(MarkdownPreviewView)
+          runs ->
+            expect(pane1).toHaveFocus()
+            expect(pane2.activeItem).toBe preview

-            waitsFor ->
-              preview.buffer
+    describe "when a new grammar is loaded", ->
+      it "re-renders the preview", ->
+        jasmine.unspy(window, 'setTimeout')
+        atom.packages.activatePackage('language-javascript', sync: true)

-            runs ->
-              expect(preview.buffer).toBe atom.workspaceView.getActivePaneItem().buffer
-              expect(pane1).toMatchSelector(':has(:focus)')
-
-        describe "when there is only one pane", ->
-          it "splits the current pane to the right with a markdown preview for the current buffer", ->
-            expect(atom.workspaceView.getPanes()).toHaveLength 1
-
-            atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
-
-            expect(atom.workspaceView.getPanes()).toHaveLength 2
-            [pane1, pane2] = atom.workspaceView.getPanes()
-
-            expect(pane2.items).toHaveLength 1
-            preview = pane2.activeItem
-            expect(preview).toBeInstanceOf(MarkdownPreviewView)
-
-            waitsFor ->
-              preview.buffer
-
-            runs ->
-              expect(preview.buffer).toBe atom.workspaceView.getActivePaneItem().buffer
-              expect(pane1).toMatchSelector(':has(:focus)')
-
-        describe "when a buffer is saved", ->
-          it "does not show the markdown preview", ->
-            [pane] = atom.workspaceView.getPanes()
-            pane.focus()
-
-            MarkdownPreviewView.prototype.renderMarkdown.reset()
-            pane.activeItem.buffer.emit 'saved'
-            expect(MarkdownPreviewView.prototype.renderMarkdown).not.toHaveBeenCalled()
-
-        describe "when a buffer is reloaded", ->
-          it "does not show the markdown preview", ->
-            [pane] = atom.workspaceView.getPanes()
-            pane.focus()
-
-            MarkdownPreviewView.prototype.renderMarkdown.reset()
-            pane.activeItem.buffer.emit 'reloaded'
-            expect(MarkdownPreviewView.prototype.renderMarkdown).not.toHaveBeenCalled()
-
-      describe "when a preview item has already been created for the editor's uri", ->
-        it "updates and shows the existing preview item if it isn't displayed", ->
-          atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
-          [pane1, pane2] = atom.workspaceView.getPanes()
-          pane2.focus()
-          expect(atom.workspaceView.getActivePane()).toBe pane2
-          preview = pane2.activeItem
-          expect(preview).toBeInstanceOf(MarkdownPreviewView)
-          atom.workspaceView.openSync()
-          expect(pane2.activeItem).not.toBe preview
-          pane1.focus()
-
-          preview.renderMarkdown.reset()
-          atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
-          expect(preview.renderMarkdown).toHaveBeenCalled()
-          expect(atom.workspaceView.getPanes()).toHaveLength 2
-          expect(pane2.getItems()).toHaveLength 2
-          expect(pane2.activeItem).toBe preview
-          expect(pane1).toMatchSelector(':has(:focus)')
-
-        describe "when a buffer is saved", ->
-          describe "when the preview is in the same pane", ->
-            it "updates the preview but does not make it active", ->
-              atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
-              [pane1, pane2] = atom.workspaceView.getPanes()
-              preview = pane2.activeItem
-              pane2.moveItemToPane(preview, pane1, 1)
-
-              pane1.showItemAtIndex(1)
-              pane1.showItemAtIndex(0)
-              preview = pane1.itemAtIndex(1)
-
-              waitsFor ->
-                preview.buffer
-
-              runs ->
-                preview.renderMarkdown.reset()
-                pane1.activeItem.buffer.emit 'saved'
-                expect(preview.renderMarkdown).toHaveBeenCalled()
-                expect(pane1.activeItem).not.toBe preview
-
-          describe "when the preview is not in the same pane", ->
-            it "updates the preview and makes it active", ->
-              atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
-              [pane1, pane2] = atom.workspaceView.getPanes()
-              preview = pane2.activeItem
-              pane2.showItem($$ -> @div id: 'view', tabindex: -1, 'View')
-              expect(pane2.activeItem).not.toBe preview
-              pane1.focus()
-
-              waitsFor ->
-                preview.buffer
-
-              runs ->
-                preview.renderMarkdown.reset()
-                pane1.activeItem.buffer.emit 'saved'
-                expect(preview.renderMarkdown).toHaveBeenCalled()
-                expect(pane2.activeItem).toBe preview
-
-      describe "when a new grammar is loaded", ->
-        it "reloads the view to colorize any fenced code blocks matching the newly loaded grammar", ->
-          atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
-          [pane1, pane2] = atom.workspaceView.getPanes()
-          preview = pane2.activeItem
-          preview.renderMarkdown.reset()
-          jasmine.unspy(window, 'setTimeout')
-
-          atom.packages.activatePackage('language-javascript', sync: true)
-          waitsFor -> preview.renderMarkdown.callCount > 0
-
-      describe "when pane:reopen-closed-item is triggered", ->
-        it "reopens the markdown preview view", ->
-          atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
-          [pane1, pane2] = atom.workspaceView.getPanes()
-          preview = pane2.activeItem
-          pane2.destroyActiveItem()
-          atom.workspaceView.trigger 'pane:reopen-closed-item'
-          expect(atom.workspaceView.getActivePaneItem().constructor.name).toBe 'MarkdownPreviewView'
+        waitsFor ->
+          MarkdownPreviewView.prototype.renderMarkdown.callCount > 0


[metrics] fixed an errant console error when no data present

--- app/assets/javascripts/states/monitoring/views/_host_overview.js.coffee
+++ app/assets/javascripts/states/monitoring/views/_host_overview.js.coffee
@@ -9,7 +9,7 @@

     events:
       "mousemove td.sparkline": (e) ->
-        return if !$(e.target).hasClass("sparkline")
+        return if !$(e.target).hasClass("sparkline") || !@metrics[0].graph.dygraph

         x = e.clientX - @$("div.graph").first().offset().left


set uom

set uom

--- spec/skr/models/SoLineSpec.coffee
+++ spec/skr/models/SoLineSpec.coffee
@@ -2,6 +2,6 @@

     it "can sets properties from the uom", ->
         model = new Skr.Models.SoLine()
-        model.uom = new Skr.Models.Uom(size: 10, code: 'CS')
+        model.set(uom: new Skr.Models.Uom(size: 10, code: 'CS'))
         expect(model.uom_size).toEqual(10)
         expect(model.uom_code).toEqual('CS')


[client] bugfix "form" participants cannot be added

--- client/app/components/message-list.coffee
+++ client/app/components/message-list.coffee
@@ -352,7 +352,7 @@
         from = message.get 'from'
         to   = message.get('to').concat(message.get('cc'))
         span null,
-            Participants participants: from, onAdd: @addAddres
+            Participants participants: from, onAdd: @addAddress
             span null, ', '
             Participants participants: to, onAdd: @addAddress


collaboration: replace all KD.showError calls

--- client/ide/collaborationcontroller.coffee
+++ client/ide/collaborationcontroller.coffee
@@ -58,16 +58,16 @@

   fetchSocialChannel: (callback) ->

-    return callback @socialChannel  if @socialChannel
+    return callback null, @socialChannel  if @socialChannel

     id = @channelId or @workspaceData.channelId

     KD.singletons.socialapi.cacheable 'channel', id, (err, channel) =>
-      return KD.showError err  if err
+      return callback err  if err

       @setSocialChannel channel

-      callback @socialChannel
+      callback null, @socialChannel

   deletePrivateMessage: (callback = noop) ->
@@ -78,18 +78,17 @@
     options = channelId: @socialChannel.getId()
     channel.delete options, (err) =>

-      return KD.showError err  if err
+      return callback err  if err

       @channelId = @socialChannel = null

       options = $unset: channelId: 1
       JWorkspace.update @workspaceData._id, options, (err) =>

-        return KD.showError err  if err
+        return callback err  if err

         @workspaceData.channelId = null
-
-        callback()
+        callback null

   # FIXME: This method is called more than once. It should cache the result and
@@ -116,7 +115,7 @@
       @listChatParticipants (accounts) =>
         @statusBar.emit 'ShowAvatars', accounts, @participants.asArray()

-      callback()
+      callback null

   startChatSession: (callback) ->
@@ -132,12 +131,11 @@

     if channelId

-      @fetchSocialChannel (channel) =>
+      @fetchSocialChannel (err, channel) =>
+        return callback err  if err

         @createChatPaneView channel
-
         @isRealtimeSessionActive channelId, (isActive, file) =>
-
           if isActive
             @loadCollaborationFile file.result.items[0].id
             return @continuePrivateMessage callback
@@ -652,7 +650,8 @@
         IDE.Metrics.collect 'StatusBar.collaboration_button', 'shown'
         return @statusBar.share.show()

-      @fetchSocialChannel (channel) =>
+      @fetchSocialChannel (err, channel) =>
+        return throwError "no social channel"  if err
         @isRealtimeSessionActive channelId, (isActive) =>
           if isActive or @isInSession
             @startChatSession => @chat.showChatPane()
@@ -772,7 +771,7 @@
     method   = if share then 'share' else 'unshare'
     jMachine[method] usernames, (err) =>

-      return KD.showError err  if err
+      return callback err  if err

       kite   = @mountedMachine.getBaseKite()
       method = if share then 'klientShare' else 'klientUnshare'
@@ -790,8 +789,9 @@
               ]

               action = if share then 'added' else 'removed'
-              KD.showError "#{username} couldn't be #{action} as an user"
-              console.error err
+              message = "#{username} couldn't be #{action} as an user"
+              callback { message }
+              console.error message

       Bongo.dash queue, callback


CollectionView: clear up grammar in itemsResetted --> itemsReset

--- src/chaplin/views/collection_view.coffee
+++ src/chaplin/views/collection_view.coffee
@@ -114,7 +114,7 @@
   addCollectionListeners: ->
     @listenTo @collection, 'add', @itemAdded
     @listenTo @collection, 'remove', @itemRemoved
-    @listenTo @collection, 'reset sort', @itemsResetted
+    @listenTo @collection, 'reset sort', @itemsReset

   # Rendering
   # ---------
@@ -162,7 +162,7 @@
     @removeViewForItem item

   # When all items are resetted, render all anew
-  itemsResetted: =>
+  itemsReset: =>
     @renderAllItems()

   # Fallback message when the collection is empty


Add ngDown tests

Add ngDown tests

--- client/test/spec/directives/ngdown.coffee
+++ client/test/spec/directives/ngdown.coffee
@@ -1,16 +1,43 @@
 'use strict'

 describe 'Directive: ngDown', ->
-
-  # load the directive's module
   beforeEach module 'blicblockApp'

   scope = {}
+  compile = {}

-  beforeEach inject ($controller, $rootScope) ->
+  beforeEach inject ($controller, $compile, $rootScope) ->
     scope = $rootScope.$new()
+    compile = $compile

-  it 'should make hidden element visible', inject ($compile) ->
-    element = angular.element '<ng-down></ng-down>'
-    element = $compile(element) scope
-    expect(element.text()).toBe 'this is the ngDown directive'
+  it 'is triggered on down arrow keydown', ->
+    element = compile('

')(scope)
+    expect(scope.touched).toBeFalsy()
+    e = $.Event('keydown')
+    e.which = 40
+    $(element).trigger(e)
+    expect(scope.touched).toEqual(true)
+
+  it 'is triggered on down arrow keypress', ->
+    element = compile('

')(scope)
+    expect(scope.touched).toBeFalsy()
+    e = $.Event('keypress')
+    e.which = 40
+    $(element).trigger(e)
+    expect(scope.touched).toEqual(true)
+
+  it 'is triggered on letter s keydown', ->
+    element = compile('

')(scope)
+    expect(scope.touched).toBeFalsy()
+    e = $.Event('keydown')
+    e.which = 115
+    $(element).trigger(e)
+    expect(scope.touched).toEqual(true)
+
+  it 'is triggered on letter s keypress', ->
+    element = compile('

')(scope)
+    expect(scope.touched).toBeFalsy()
+    e = $.Event('keypress')
+    e.which = 115
+    $(element).trigger(e)
+    expect(scope.touched).toEqual(true)


activity: use constant instead of hardcoded string

--- client/activity/lib/flux/actions/message.coffee
+++ client/activity/lib/flux/actions/message.coffee
@@ -72,7 +72,7 @@
   dispatch actionTypes.LOAD_MESSAGE_SUCCESS, { channelId, channel, message }

   message.replies.forEach (comment) ->
-    dispatch 'LOAD_COMMENT_SUCCESS', {messageId: message.id, comment}
+    dispatch actionTypes.LOAD_COMMENT_SUCCESS, {messageId: message.id, comment}
     realtimeActionCreators.bindMessageEvents comment


Client:Activity: Updated editMessage method from message.coffee

--- client/activity/lib/flux/actions/message.coffee
+++ client/activity/lib/flux/actions/message.coffee
@@ -165,16 +165,14 @@
  *
  * @param {string} messageId
  * @param {string} body
- * @param {string} createdAt
  * @param {object=} payload
 ###
 editMessage = (messageId, body, payload = {}) ->

-  {socialapi} = kd.singletons
+  { socialapi } = kd.singletons
   { EDIT_MESSAGE_BEGIN
     EDIT_MESSAGE_SUCCESS
-    EDIT_MESSAGE_FAIL
-  } = actionTypes
+    EDIT_MESSAGE_FAIL } = actionTypes

   dispatch EDIT_MESSAGE_BEGIN, { messageId, body, payload }

@@ -183,12 +181,7 @@
       dispatch EDIT_MESSAGE_FAIL, { err, messageId }
       return

-    kd.utils.wait 673, ->
-      socialapi.message.byId {id: messageId}, (err, message) ->
-        message.body = body
-        dispatch EDIT_MESSAGE_SUCCESS, {
-          message, messageId
-        }
+    dispatch EDIT_MESSAGE_SUCCESS, { message, messageId }

 ###*


Client: Activity: Feed: Comment-out function calls to prevent load comments

--- client/activity/lib/flux/actions/message.coffee
+++ client/activity/lib/flux/actions/message.coffee
@@ -109,7 +109,7 @@

     dispatchLoadMessageSuccess message.initialChannelId, message
     # unmark this message for being fetched.
-    loadComments message.id
+    # loadComments message.id
     fetchingMap[messageId] = no

     return { message }
@@ -206,7 +206,7 @@
       return

     dispatchLoadMessageSuccess message.initialChannelId, message
-    loadComments message.id
+    # loadComments message.id

 ###*


CLI fix for the 'install' command to make it copy fonts as well

--- cli/repository.coffee
+++ cli/repository.coffee
@@ -46,7 +46,7 @@
       fs = require('fs')

       fs.mkdirSync("#{location}/images", 0o0755)
-      build = build.replace /('|")([^'"]+\.(gif|png|jpg|jpeg|svg|swf))\1/g,
+      build = build.replace /('|")([^'"]+\.(gif|png|jpg|jpeg|svg|swf|eot|ttf|woff))\1/g,
       (match, q, url)->
         if url.indexOf('http://') > -1
           url = url.replace(/http:\/\/[^\/]+/, '')


Use name of patient when allowed instead of type of appointment.

--- app/assets/javascripts/appointments/appointment_sync.service.js.coffee
+++ app/assets/javascripts/appointments/appointment_sync.service.js.coffee
@@ -63,7 +63,10 @@
     type = _.findWhere types, id: appointment.appointmentTypeId
     if !$rootScope.user? || ($rootScope.user.isPatient() && $rootScope.user.id != (appointment.userId || appointment.user?.id))
       type = angular.extend(type, {name: 'Slot Taken', colorClass: 'black', textColor: 'white'})
-    angular.extend(existing, {title: type.name, color: type.colorClass, textColor: type.textColor, allDay: false, appointment: appointment})
+      angular.extend(existing, {title: type.name})
+    else
+      angular.extend(existing, {title: appointment.user.display})
+    angular.extend(existing, {color: type.colorClass, textColor: type.textColor, allDay: false, appointment: appointment})
     if $rootScope.user? && ($rootScope.user.isStaffOrAdmin() || ($rootScope.user.isPatient() && $rootScope.user.id != appointment.user?.id))
       existing.editable = true
     start = moment(appointment.start)


collection is not needed in CommentEntry view

--- app/assets/javascripts/backbone/views/comments/comment_entry.js.coffee
+++ app/assets/javascripts/backbone/views/comments/comment_entry.js.coffee
@@ -6,9 +6,6 @@

   events:
     'click .remove': 'removeComment'
-
-  initialize:(options) ->
-    @collection = options.collection

   render: ->
     extra =


Added debug for production.

Added debug for production.

--- client/app/app.coffee
+++ client/app/app.coffee
@@ -15,5 +15,5 @@
   $urlRouterProvider
   .otherwise '/'
   $locationProvider.html5Mode true
-
+  $stickyStateProvider.enableDebug(true)
 .run ($rootScope, $location) ->


Fix MessageTags

Fix MessageTags

--- client/message.coffee
+++ client/message.coffee
@@ -54,12 +54,12 @@
   sortTags @tags

 Template.registerHelper 'linkToTag', ->
-  linkToTag Template.parentData().group
-linkToTag = (group) ->
+  linkToTag @, Template.parentData().group
+linkToTag = (tag, group) ->
   #pathFor 'tag',
   #  group: group
-  #  tag: @key
-  search = @key
+  #  tag: tag.key
+  search = tag.key
   if 0 <= search.indexOf ' '
     if 0 <= search.indexOf '"'
       if 0 <= search.indexOf "'"
@@ -1069,14 +1069,14 @@
 MessageTags = React.memo ({message}) ->

     {for tag in sortTags message.tags
-      <>
-
+      <React.Fragment key={tag.key}>
+

             {tag.key}

-        ' '
-      </>
+        {' '}
+      </React.Fragment>
     }

 MessageTags.displayName = 'MessageTags'


Expose highcharts

Expose highcharts

--- client/src/app.coffee
+++ client/src/app.coffee
@@ -7,6 +7,7 @@
 window.d3 = require 'd3'
 window.showdown = require 'showdown'
 window.pleaseWait = require('please-wait').pleaseWait
+window.Highcharts = require 'highcharts'

 require 'angular-material/angular-material.min.css'
 require 'angular-material-data-table/dist/md-data-table.min.css'
@@ -36,7 +37,6 @@
 require 'd3-bdc'
 require 'c3'
 require 'MimeJS/dist/mime-js.js'
-require 'highcharts'

 require './models/0-module.coffee'


start adding individual player info

start adding individual player info

--- coffee/players.coffee
+++ coffee/players.coffee
@@ -14,7 +14,7 @@
       "name":"Cesc Fabregas"
       "position": "Attacking Midfielder"
       "number": "4"
-      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
+      "copy" :"Thibaut Nicolas Marc Courtois is a Belgian professional footballer who plays for English club Chelsea and the Belgium national team as a goalkeeper. Despite his young age, Courtois is widely regarded as one of the best goalkeepers in the world, and he won the 2012–13 and 2013–14 Ricardo Zamora Trophies while on loan at Atlético Madrid."
     },
     "drogba": {
       "name":"Didier Drogba"


Wait for crash reporter spec longer.

Wait for crash reporter spec longer.

--- spec/api-crash-reporter-spec.coffee
+++ spec/api-crash-reporter-spec.coffee
@@ -15,7 +15,7 @@
   afterEach -> w.destroy()

   it 'should send minidump when renderer crashes', (done) ->
-    @timeout 5000
+    @timeout 60000
     server = http.createServer (req, res) ->
       form = new formidable.IncomingForm()
       process.throwDeprecation = false


Change timeout for pusher fallback to 30s

We have a bug that results in sending unneeded ajax queries on missing
log parts. I'm working on a fix, but it may take a while, because I want
to refactor a few parts of the app instead of putting more hacks on
legacy code. To help with the situation I'm increasing timeout for
pusher fallback.

--- assets/scripts/lib/travis/log_chunks.coffee
+++ assets/scripts/lib/travis/log_chunks.coffee
@@ -1,5 +1,5 @@
 Travis.LogChunks = Em.ArrayProxy.extend
-  timeout: 10000
+  timeout: 30000

   init: ->
     @setTimeout()


Fix emit warning calculation.

Fix emit warning calculation.

--- compiler/index.coffee
+++ compiler/index.coffee
@@ -117,7 +117,7 @@
                 SDKError.warn('files', 'Projects SHOULD have a /index.html')

             num_indexed = Object.keys(_emitFile.files_emitted_indexed).length
-            num_emitted = _emitFile.files_emitted_indexed
+            num_emitted = _emitFile.files_emitted_indexed.length
             if num_indexed isnt num_emitted
                 SDKError.warn('files', "#{ num_emitted - num_indexed } too many emits. Check for multiple emits of the same file.")
             onCompile?(_emitFile.files_emitted, compileAssets.files_emitted, project_package, project_config)


modal view is less quirky now

modal view is less quirky now

--- components/inputs/KDInputViewWithPreview.coffee
+++ components/inputs/KDInputViewWithPreview.coffee
@@ -137,7 +137,7 @@

           @utils.wait =>

-            modal.$(".kdmodal-content").height modal.$(".kdmodal-inner").height()-modal.$(".kdmodal-buttons").height()-modal.$(".kdmodal-title").height()+10 # minus the margin, border pixels too..
+            modal.$(".kdmodal-content").height modal.$(".kdmodal-inner").height()-modal.$(".kdmodal-buttons").height()-modal.$(".kdmodal-title").height() # minus the margin, border pixels too..
             modal.$(".fullscreen-data").height modal.$(".kdmodal-content").height()-30-23+10
             modal.$(".input_preview").height   modal.$(".kdmodal-content").height()-0-21+10
             modal.$(".input_preview").css maxHeight:  modal.$(".kdmodal-content").height()-0-21+10


Clear out loaded packages in PackageManager.prototype.reset

This way, when the AtomEnvironment is reset, packages will have .load() called
on them again, so their config schemas will be registered again.

--- src/package-manager.coffee
+++ src/package-manager.coffee
@@ -62,6 +62,7 @@
   reset: ->
     @serviceHub.clear()
     @deactivatePackages()
+    @loadedPackages = {}
     @packageStates = {}

   ###


test error-handling

test error-handling

--- src/protomixin.spec.coffee
+++ src/protomixin.spec.coffee
@@ -156,6 +156,21 @@
         Example.mixinto_proto @mixin, omits: ['non_mixin_key']
       ).toThrow new MixinUtils.ArgumentError "Some omit keys aren't in mixin object: non_mixin_key"

+    it 'should throw an error when omitting a non-Array or empty Array', ->
+      bad_omits_values = [
+        []
+        {}
+        null
+        1
+        'String'
+      ]
+
+      for bad_omits_value in bad_omits_values
+        expect(=>
+          class Example
+          Example.mixinto_proto @mixin, omits: bad_omits_value
+        ).toThrow new MixinUtils.ArgumentError "Expected omits option to be a nonempty Array"
+
     it 'should not mangle the class hierarchy when omitting keys', ->
       class Super
         bar: ->
@@ -175,10 +190,23 @@
         Example.mixinto_proto @mixin, omits: ['bar', 'baz']
       ).toThrow new MixinUtils.ArgumentError "Found nothing to mix in!"

-    it 'should throw an error when supplying a mixin-method hook'
-
-    it 'should support pre-mixin hooks', ->
+    it 'should throw an error when supplying a non object-literal hooks option', ->
+      non_obj_literals = [
+        []
+        1
+        'String'
+        false
+        null
+      ]
+
+      for non_obj_literal in non_obj_literals
+        expect(=>
+          class Example
+          Example.mixinto_proto @mixin, hooks: non_obj_literal
+        ).toThrow new MixinUtils.ArgumentError "Expected object literal for hooks option"
+
+    xit 'should support pre-mixin hooks', ->
       throw new Error 'Write me!'

-    it 'should support post-mixin hooks', ->
+    xit 'should support post-mixin hooks', ->
       throw new Error 'Write me!'


Allow for parallel check/lint

Allow for parallel check/lint

--- lib/ghc-modi-process.coffee
+++ lib/ghc-modi-process.coffee
@@ -38,7 +38,7 @@

   createQueues: =>
     @commandQueues =
-      checklint: new Queue(1)
+      checklint: new Queue(2)
       browse: null
       typeinfo: new Queue(1)
       find: new Queue(1)
@@ -217,9 +217,8 @@
     @doCheckOrLintBuffer "lint", buffer, fast

   doCheckAndLint: (buffer, fast) =>
-    @doCheckBuffer(buffer, fast).then (resCheck) =>
-      @doLintBuffer(buffer, fast).then (resLint) ->
-        return resCheck.concat resLint
+    Promise.all [ @doCheckBuffer(buffer, fast), @doLintBuffer(buffer, fast) ]
+    .then (resArr) -> [].concat resArr...

   getRootDir: (buffer) ->
     @backend?.getRootDir?(buffer) ? Util.getRootDir(buffer)


Fixes handling of the pull response stream

In newer versions of Docker we can get more than one JSON object in the
same stream. As a stop-gap, fix the parsing and pick one to report on.

--- lib/lib/docker_utils.coffee
+++ lib/lib/docker_utils.coffee
@@ -109,10 +109,17 @@
         # Docker sends along a nice summary of the download progress, including an ASCII progress
         # bar and estimation of time remaining for the current download. Send that along to our
         # progress callback.
-        resp = JSON.parse byteBuffer.toString()
-        reject(resp.error) if resp.error?
+        #
+        # At least with Docker 1.11 running in Docker for Mac, the byteBuffer can contain more than
+        # one newline-separated JSON object, so we split, look for errors across all of them, and
+        # report on the first one.
+        #
+        # TODO(finneganh): Do better about multiple simultaneous statuses
+        statusArr = byteBuffer.toString().split('\n').filter((str) -> str.length).map((json) -> JSON.parse(json))
+        statusArr.forEach (status) ->
+          reject(status.error) if status.error?

-        progressCb resp.progress or resp.status
+        progressCb statusArr[0].progress or statusArr[0].status

       stream.on 'end', -> resolve()


fixes #7

fixes #7

--- lib/line-diff-worker.coffee
+++ lib/line-diff-worker.coffee
@@ -63,7 +63,10 @@

     findFileDiffs: ->
         activePath = @editor.getPath()
-        repo = r for r in atom.project.getRepositories() when activePath.indexOf(r?.repo.workingDirectory) != -1
+        if process.platform is 'win32'
+            repo = r for r in atom.project.getRepositories() when activePath.indexOf(r?.repo.workingDirectory.replace(/\//g, "\\")) != -1
+        else
+            repo = r for r in atom.project.getRepositories() when activePath.indexOf(r?.repo.workingDirectory) != -1
         return [] if not repo?
         fileRepo = repo.getRepo(activePath)
         activeEditorText = @editor.getBuffer().getText()


Only position misspellings when they are visible

This lifts code from the find-and-replace package to only
render misspellings when they are visible in the editor.

--- lib/misspelling-view.coffee
+++ lib/misspelling-view.coffee
@@ -9,22 +9,24 @@
   initialize: (range, @editorView) ->
     @editor = @editorView.getEditor()
     @misspellingValid = true
+    @updateDisplayPosition = true

     range = @editor.screenRangeForBufferRange(Range.fromObject(range))
     @startPosition = range.start
     @endPosition = range.end
+    @oldScreenRange = @getScreenRange()

     @createMarker()

     @subscribe @editorView, 'editor:display-updated', =>
-      @updatePosition() if @updateDisplayPosition
+      @updatePosition() if @isUpdateNeeded()

     @subscribeToCommand @editorView, 'spell-check:correct-misspelling', =>
       if @misspellingValid and @containsCursor()
         @correctionsView?.remove()
         @correctionsView = new CorrectionsView(@editorView, @getCorrections(), @getScreenRange())

-    @updatePosition()
+    @updatePosition() if @isUpdateNeeded()

   createMarker: ->
     @marker = @editor.markScreenRange(@getScreenRange(), invalidation: 'inside', replicate: false, persistent: false)
@@ -34,6 +36,17 @@
       @updateDisplayPosition = isValid
       @misspellingValid = isValid
       @hide() unless isValid
+
+  isUpdateNeeded: ->
+    return false unless @updateDisplayPosition
+
+    oldScreenRange = @oldScreenRange
+    newScreenRange = @getScreenRange()
+    @oldScreenRange = newScreenRange
+    @intersectsRenderedScreenRows(oldScreenRange) or @intersectsRenderedScreenRows(newScreenRange)
+
+  intersectsRenderedScreenRows: (range) ->
+    range.intersectsRowRange(@editorView.firstRenderedScreenRow, @editorView.lastRenderedScreenRow)

   getScreenRange: ->
     new Range(@startPosition, @endPosition)


Simplified toJSON

Simplified toJSON

--- lib/model_extensions.coffee
+++ lib/model_extensions.coffee
@@ -225,17 +225,14 @@
     json = {}
     attributes = if @whitelist then _.pick(@attributes, @whitelist) else @attributes
     for key, value of attributes
-      if value instanceof Backbone.Collection
-        if not options.relations and schema and (relation = schema.relation(key))
-          relation.appendJSON(json, @, key)
-        else
-          json[key] = _.map(value.models, (model) -> if model then model.toJSON(options) else null)
+      if schema and (relation = schema.relation(key))
+        relation.appendJSON(json, @, key)
+
+      else if value instanceof Backbone.Collection
+        json[key] = _.map(value.models, (model) -> if model then model.toJSON(options) else null)

       else if value instanceof Backbone.Model
-        if not options.relations and schema and (relation = schema.relation(key))
-          relation.appendJSON(json, @, key)
-        else
-          json[key] = value.toJSON(options)
+        json[key] = value.toJSON(options)

       else
         json[key] = value


Refactor event handling

Refactor event handling

--- lib/models/synced-fs.coffee
+++ lib/models/synced-fs.coffee
@@ -30,59 +30,79 @@

   handleEvents: ->
     atom.workspace.observeTextEditors (editor) =>
-      project = editor.project
-      buffer = editor.buffer
-      file = buffer.file
-      editorElement = atom.views.getView(editor)
+      editor.onDidSave => @onSave()
+      editor.onDidChangePath -> console.log 'PATH CHANGED'

-      editor.onDidSave =>
-        atom.commands.dispatch(editorElement, 'line-ending-selector:convert-to-LF')
-        console.log 'Saving: Path - ' + this.formatFilePath(buffer.file.path) + ' Matches? - ' + !!this.formatFilePath(buffer.file.path).match(/\.atom\/code/)
-        if @formatFilePath(buffer.file.path).match(/\.atom\/code/)
-          if @connectionState == 'closed'
-            @popupNoConnectionWarning()
+    atom.commands.onDidDispatch ({type}) =>
+      console.log type
+      @onCancel() if type is 'core:cancel'
+      @onConfirm(e) if type is 'core:confirm'

-          @sendSave(editor.project, buffer.file, buffer)
+    atom.commands.add atom.views.getView(atom.workspace),
+      'tree-view:remove': ({target}) => @onRemove(target)
+      'tree-view:add-file': (e) => @queueTreeViewEvent(e)
+      'tree-view:move': (e) => @queueTreeViewEvent(e)

-      editor.onDidChangePath =>
-        console.log('PATH CHANGED')
+  sendSave: (project, file, buffer) ->
+    ipc.send 'fs-local-save', JSON.stringify(
+      action: 'local_save'
+      project:
+        path: this.formatFilePath(project.getPaths()[0])
+      file:
+        path: this.formatFilePath(file.path)
+        digest: file.digest,
+      buffer:
+        content: window.btoa(unescape(encodeURIComponent(buffer.getText())))
+      )

-    atom.commands.onDidDispatch (e) =>
-      if e.type == 'tree-view:remove'
-        if e.target.attributes['data-path']
-          path = e.target.attributes['data-path'].nodeValue
-        else
-          path = e.target.file.path
+  formatFilePath: (path) ->
+    return path.replace(/(.*:\\)/, '/').replace(/\\/g, '/') if path.match(/:\\/)
+    path

-        ipc.send 'fs-local-delete', JSON.stringify({
-          action: 'local_delete',
-          project: {
-            path: this.formatFilePath(atom.project.getPaths()[0])
-          },
-          file: {
-            path: this.formatFilePath(path)
-          }
-        })
-      else if e.type == 'tree-view:add-file' || e.type == 'tree-view:move'
-        @treeViewEventQueue.push
-          type: e.type
-          event: e
-      else if e.type == 'core:cancel'
-        @treeViewEventQueue = []
-      else if e.type == 'core:confirm'
-        confirmedEvent = @treeViewEventQueue.shift()
-        if confirmedEvent
-          event = confirmedEvent.event
+  onRemove: (target) ->
+    if target.attributes['data-path']
+      path = target.attributes['data-path'].nodeValue
+    else
+      path = target.file.path

-          switch confirmedEvent.type
-            when 'tree-view:move'
-              from = event.target.getAttribute('data-name')
-              fromPath = event.target.getAttribute('data-path')
-            when 'tree-view:add-file'
-              window.confirmedEvent = event
-              true
-      else
-        console.log(e.type)
+    ipc.send 'fs-local-delete', JSON.stringify(
+      action: 'local_delete'
+      project:
+        path: this.formatFilePath atom.project.getPaths()[0]
+      file:
+        path: this.formatFilePath path
+      )
+
+  onConfirm: (e) =>
+    confirmedEvent = @treeViewEventQueue.shift()
+    return unless confirmedEvent?
+
+    {event, type} = confirmedEvent
+    if type is 'tree-view:move'
+      from = event.target.getAttribute('data-name')
+      fromPath = event.target.getAttribute('data-path')
+    if type is 'tree-view:add-file'
+      window.confirmedEvent = event
+      true
+
+  onCancel: =>
+    @treeViewEventQueue = []
+
+  onSave: (editor) =>
+    editorElement = atom.views.getView(editor)
+    atom.commands.dispatch(editorElement, 'line-ending-selector:convert-to-LF')
+
+    {project, buffer} = editor
+    {file} = buffer
+    inCodeDir = !!@formatFilePath(buffer.file.path).match(/\.atom\/code/)
+    console.log 'Saving: Path - ' + @formatFilePath buffer.file.path + ' Matches? - ' + inCodeDir
+    return unless inCodeDir
+
+    @popupNoConnectionWarning() if @connectionState is 'closed'
+    @sendSave(editor.project, buffer.file, buffer)
+
+  queueTreeViewEvent: (event) =>
+    @treeViewEventQueue.push(type: event.type, event: event)

   popupNoConnectionWarning: ->
     noConnectionPopup = document.createElement 'div'
@@ -104,30 +124,3 @@

     noConnectionButton.addEventListener 'click', (e) =>
       panel.destroy()
-
-  sendSave: (project, file, buffer) ->
-    ipc.send 'fs-local-save', JSON.stringify({
-      action: 'local_save',
-      project: {
-        path: this.formatFilePath(project.getPaths()[0])
-      },
-      file: {
-        path: this.formatFilePath(file.path)
-        digest: file.digest,
-      },
-      buffer: {
-        content: window.btoa(unescape(encodeURIComponent(buffer.getText())))
-      }
-    })
-
-  #formattedText: (text) ->
-    #try
-      #window.btoa(text)
-    #catch
-      #window.btoa(unescape(encodeURIComponent(text)))
-
-  formatFilePath: (path) ->
-    if path.match(/:\\/)
-      return path.replace(/(.*:\\)/, '/').replace(/\\/g, '/')
-    else
-      return path


Send local open to remote

Send local open to remote

--- lib/models/synced-fs.coffee
+++ lib/models/synced-fs.coffee
@@ -22,7 +22,6 @@
       @connectionState = state

     @handleEvents()
-    @treeViewEventQueue = []

   expandTreeView: ->
     workspaceView = atom.views.getView(atom.workspace)
@@ -33,31 +32,33 @@
       editor.onDidSave => @onSave()
       editor.onDidChangePath -> console.log 'PATH CHANGED'

+    atom.workspace.onDidOpen ({uri, item, pane, index}) =>
+      console.log 'OPENED ' + uri
+      @onOpen(uri)
+
     atom.commands.onDidDispatch ({type}) =>
       console.log type
-      @onCancel() if type is 'core:cancel'
-      @onConfirm(e) if type is 'core:confirm'

     atom.commands.add atom.views.getView(atom.workspace),
       'tree-view:remove': ({target}) => @onRemove(target)
-      'tree-view:add-file': (e) => @queueTreeViewEvent(e)
-      'tree-view:move': (e) => @queueTreeViewEvent(e)

   sendSave: (project, file, buffer) ->
     ipc.send 'fs-local-save', JSON.stringify(
       action: 'local_save'
       project:
-        path: this.formatFilePath(project.getPaths()[0])
+        path: @formatFilePath(project.getPaths()[0])
       file:
-        path: this.formatFilePath(file.path)
+        path: @formatFilePath(file.path)
         digest: file.digest,
       buffer:
         content: window.btoa(unescape(encodeURIComponent(buffer.getText())))
       )

   formatFilePath: (path) ->
-    return path.replace(/(.*:\\)/, '/').replace(/\\/g, '/') if path.match(/:\\/)
-    path
+    if path.match(/:\\/)
+      path.replace(/(.*:\\)/, '/').replace(/\\/g, '/')
+    else
+      path

   onRemove: (target) ->
     if target.attributes['data-path']
@@ -68,25 +69,10 @@
     ipc.send 'fs-local-delete', JSON.stringify(
       action: 'local_delete'
       project:
-        path: this.formatFilePath atom.project.getPaths()[0]
+        path: @formatFilePath(atom.project.getPaths()[0])
       file:
-        path: this.formatFilePath path
+        path: @formatFilePath(path)
       )
-
-  onConfirm: (e) =>
-    confirmedEvent = @treeViewEventQueue.shift()
-    return unless confirmedEvent?
-
-    {event, type} = confirmedEvent
-    if type is 'tree-view:move'
-      from = event.target.getAttribute('data-name')
-      fromPath = event.target.getAttribute('data-path')
-    if type is 'tree-view:add-file'
-      window.confirmedEvent = event
-      true
-
-  onCancel: =>
-    @treeViewEventQueue = []

   onSave: (editor) =>
     editorElement = atom.views.getView(editor)
@@ -101,8 +87,14 @@
     @popupNoConnectionWarning() if @connectionState is 'closed'
     @sendSave(editor.project, buffer.file, buffer)

-  queueTreeViewEvent: (event) =>
-    @treeViewEventQueue.push(type: event.type, event: event)
+  onOpen: (filePath) =>
+    ipc.send 'fs-local-open', JSON.stringify(
+      action: 'local_open'
+      project:
+        path: @formatFilePath(atom.project.getPaths()[0])
+      file:
+        path: @formatFilePath(filePath)
+      )

   popupNoConnectionWarning: ->
     noConnectionPopup = document.createElement 'div'


Don't limit volume

Don't limit volume

--- lib/player_internals.coffee
+++ lib/player_internals.coffee
@@ -96,14 +96,12 @@
     @_worker.requestFrame()
     @_audioSource = globalAudioContext.createScriptProcessor(AUDIO_BUFFER_SIZE, 0, 2)
     @_audioSource.onaudioprocess = (event) =>
-      volume = 0.2
       left  = event.outputBuffer.getChannelData(0)
       right = event.outputBuffer.getChannelData(1)
       nextBuffer = @_worker.pop()
       for i in [0...left.length]
-        # Clip! – TODO add warning when sample > 1 || sample < -1
-        left[i]  = Math.min(Math.max(volume * nextBuffer[ i*2 ], -1), 1)
-        right[i] = Math.min(Math.max(volume * nextBuffer[ i*2+1 ], -1), 1)
+        left[i]  = nextBuffer[ i*2 ]
+        right[i] = nextBuffer[ i*2+1 ]
       @_elapsedTime += AUDIO_BUFFER_SIZE / SAMPLE_RATE
       if @_elapsedTime > duration
         @guardedNext()


SignalListView: Don't switch to running kernel

--- lib/signal-list-view.coffee
+++ lib/signal-list-view.coffee
@@ -65,6 +65,7 @@
         else
             # add commands to switch to other kernels
             @kernelManager.getAllKernelSpecsFor language, (kernelSpecs) =>
+                _.pull kernelSpecs, kernel.kernelSpec
                 switchCommands = kernelSpecs.map (spec) ->
                     return {
                         name: 'Switch to ' + spec.display_name


:fire: leftover

:fire: leftover

--- lib/visual-blockwise.coffee
+++ lib/visual-blockwise.coffee
@@ -29,7 +29,6 @@

   constructor: ->
     super
-    console.log @getKind()
     if @editor.getCursors().length is 1
       @reset()
     @currentRow  = @editor.getLastCursor()?.getBufferRow()


treeviewcontroller keyboard scroll node fix

--- components/tree/treeviewcontroller.coffee
+++ components/tree/treeviewcontroller.coffee
@@ -648,8 +648,8 @@
     return unless nodeView

     switch key
-      when "down"      then @performDownKey nodeView, event
-      when "up"        then @performUpKey nodeView, event
+      when "down"      then nextNode = @performDownKey nodeView, event
+      when "up"        then nextNode = @performUpKey nodeView, event
       when "left"      then @performLeftKey nodeView, event
       when "right"     then @performRightKey nodeView, event
       when "backspace" then @performBackspaceKey nodeView, event
@@ -660,7 +660,7 @@
     switch key
       when "down", "up"
         event.preventDefault()
-        @getView().scrollToSubView? nodeView
+        @getView().scrollToSubView? nextNode

   performDownKey:(nodeView, event)->


Hide shows section in partner overview if no shows.

--- apps/partner/client/shows_grid.coffee
+++ apps/partner/client/shows_grid.coffee
@@ -10,6 +10,7 @@
 # upcoming, and past) in a grid layout.
 # Used in overview and shows tab of partner galleries, overview tab
 # of nonpartner galleries, and shows tab of institutions.
+# Will hide itself if there is no shows.
 module.exports = class PartnerShowsGridView extends Backbone.View

   defaults:
@@ -24,6 +25,8 @@
     @initializeShows()

   renderShows: (featured=[], current=[], upcoming=[], past=[]) ->
+    return @$el.hide() if _.union(featured, current, upcoming, upcoming).length is 0
+
     @ensurePosterImages featured.concat current, upcoming, past
     @$el.html template
       partner: @partner
@@ -62,7 +65,7 @@
       success: =>
         if @numberOfFeatured - featured.length > 0
           f = partnerShows.featured()
-          featured.push f
+          featured.push f if f? # only add it if it's really something

         exclude = if f then [f] else []


Use BufferedProcess instead of BufferedNodeProcess

--- lib/package-generator-view.coffee
+++ lib/package-generator-view.coffee
@@ -1,6 +1,6 @@
 path = require 'path'
 _ = require 'underscore-plus'
-{$, BufferedNodeProcess, EditorView, View} = require 'atom'
+{$, BufferedProcess, EditorView, View} = require 'atom'
 fs = require 'fs-plus'

 module.exports =
@@ -84,4 +84,4 @@
       @initPackage packagePath, => @linkPackage(packagePath, callback)

   runCommand: (command, args, exit) ->
-    new BufferedNodeProcess({command, args, exit})
+    new BufferedProcess({command, args, exit})


Rename path variables to prevent collisions

--- lib/package-generator-view.coffee
+++ lib/package-generator-view.coffee
@@ -60,18 +60,18 @@
     templatePath = fsUtils.resolveOnLoadPath(path.join("package-generator", "template"))
     packageName = path.basename(@getPackagePath())

-    for path in fsUtils.listTree(templatePath)
-      relativePath = path.replace(templatePath, "")
+    for templateChildPath in fsUtils.listTree(templatePath)
+      relativePath = templateChildPath.replace(templatePath, "")
       relativePath = relativePath.replace(/^\//, '')
       relativePath = relativePath.replace(/\.template$/, '')
       relativePath = @replacePackageNamePlaceholders(relativePath, packageName)

       sourcePath = path.join(@getPackagePath(), relativePath)
-      if fsUtils.isDirectory(path)
+      if fsUtils.isDirectory(templateChildPath)
         fsUtils.makeTree(sourcePath)
-      if fsUtils.isFile(path)
+      if fsUtils.isFile(templateChildPath)
         fsUtils.makeTree(path.dirname(sourcePath))
-        content = @replacePackageNamePlaceholders(fsUtils.read(path), packageName)
+        content = @replacePackageNamePlaceholders(fsUtils.read(templateChildPath), packageName)
         fsUtils.write(sourcePath, content)

   replacePackageNamePlaceholders: (string, packageName) ->


Indent error

Indent error

--- src/scripts/modules/media/nav/nav.coffee
+++ src/scripts/modules/media/nav/nav.coffee
@@ -88,7 +88,7 @@
       if window.innerWidth < 640
         if @tocIsOpen
           @toggleContents()
-      @mediaParent.scrollToTop()
+        @mediaParent.scrollToTop()

     backToTop: (e) ->
       e.preventDefault()


Corrige bug na ordenação da tabela no gráfico de distribuição por UO

--- app/assets/javascripts/angular_app/directives/uoGraph.js.coffee
+++ app/assets/javascripts/angular_app/directives/uoGraph.js.coffee
@@ -9,7 +9,7 @@

   options =
     bPaginate: false
-    aaSorting: [[ 3, 'desc' ]]
+    aaSorting: [[ 4, 'desc' ]]
     sDom: 'ft'
     fnRowCallback: (nRow, aData, iDisplayIndex) ->
       $('td:eq(0)', nRow).html(iDisplayIndex + 1)


7836: Set picker format based on times

7836: Set picker format based on times

--- app/assets/javascripts/backbone/views/work/job_form_view.coffee
+++ app/assets/javascripts/backbone/views/work/job_form_view.coffee
@@ -1,12 +1,29 @@
 Gather.Views.Work.JobFormView = Backbone.View.extend

   initialize: (options) ->
+    @formatFields()

   events:
     'cocoon:after-insert': 'shiftInserted'
+    'change #work_job_times': 'formatFields'
+
+  formatFields: ->
+    dateFormat = I18n.t('datepicker.pformat')
+    timeFormat = I18n.t('timepicker.pformat')
+    times = @$('#work_job_times').val()
+    switch times
+      when 'date_time' then @setPickerFormat("#{dateFormat} #{timeFormat}")
+      when 'date_only', 'full_period' then @setPickerFormat(dateFormat)

   shiftInserted: (event, inserted) ->
     @initDatePickers(inserted)

   initDatePickers: (inserted) ->
-    $(inserted).find(".datetimepicker").datetimepicker()
+    @$(inserted).find('.datetimepicker').datetimepicker()
+
+  setPickerFormat: (format) ->
+    @shiftDatePickers().map (picker) ->
+      $(this).data('DateTimePicker').format(format)
+
+  shiftDatePickers: ->
+    @$('#shift-table .datetimepicker')


fix a minor localization bug

fix a minor localization bug

--- library/exposure/localized.coffee
+++ library/exposure/localized.coffee
@@ -156,9 +156,9 @@
         @constructor.compiledTranslations ?= compiler()
         assert cache = @constructor.compiledTranslations
         assert session = @session, "no session detected"
+        delete @session.language unless @session.langlock
         negotiator = new require("negotiator") @request
         neg = negotiator.language(_.keys(cache) or [])
-        delete @session.language unless @session.langlock
         selector = language or @session.language or neg
         selector = selector or "en" # hardcoded default
         assert not _.isEmpty(sel = selector), "lang fail"


Added correction for the case of not being able to find the appropriate public hostedzone.

--- src/publish/bucket/dns/setup.coffee
+++ src/publish/bucket/dns/setup.coffee
@@ -44,7 +44,13 @@
       zone = root config.s3.bucket
       zones = yield route53.listHostedZones {}
       result = collect where {Name: zone}, zones.HostedZones
-      if empty result then return null else id = result[0].Id
+      if empty result
+        console.error("It appears you do not have a public hostedzone setup " +
+          "for #{root config.s3.bucket}  Without it, H9 cannot setup the DNS " +
+          "records to route traffic to your bucket.  Aborting.")
+        throw new Error()
+
+      id = result[0].Id

       # Scan this hosted zone and build the neccessary DNS changes for Route53
       data = yield route53.listResourceRecordSets HostedZoneId: id


git clone instead of unzip

git clone instead of unzip

--- client/code/model/tool.coffee
+++ client/code/model/tool.coffee
@@ -1,25 +1,13 @@
 class Cu.Model.Tool extends Backbone.Model
   idAttribute: 'name'
   base_url: "#{window.boxServer}"
-
-  git_url: (callback) ->
-    $.get '/github-login', (login) =>
-      login = login.replace '\n', ''
-      callback "https://#{login}@github.com/scraperwiki/#{@get 'name'}-tool.git"
-
-  zip_url: (callback) ->
-    $.get '/github-login', (login) =>
-      login = login.replace '\n', ''
-      callback "https://#{login}@github.com/scraperwiki/#{@get 'name'}-tool/archive/master.zip"

   install: (callback) ->
     @_create_box().complete (ajaxObj, status) =>
       if status != 'success'
         callback ajaxObj, status
       else
-        @zip_url (url) =>
-          n = @get 'name'
-          @exec("cd; curl -L -O #{url}; unzip master.zip; mv #{n}-tool-master #{n}").complete callback
+        @exec("cd; git clone #{@get 'gitUrl'} #{@get 'name'} --depth 1").complete callback

   setup: (callback) ->
     @exec("cd;~/#{@get 'name'}/setup").success callback


Split into two helpers

Split into two helpers

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -24,22 +24,7 @@
       @updateCurrentLocationRange(locationRange)

   getSelectedPointRange: ->
-    return unless domRange = getDOMRange()
-    rects = domRange.getClientRects()
-    if rects.length > 0
-      startRect = rects[0]
-      endRect = rects[rects.length - 1]
-      start = x: startRect.left, y: startRect.top + 1
-      end = x: endRect.right, y: endRect.top + 1
-    else
-      node = makeElement(tagName: "span", style: { marginLeft: "-0.01em" }, data: { trixMutable: true, trixSerialize: false })
-      try
-        domRange.insertNode(node)
-        rect = node.getBoundingClientRect()
-      finally
-        node.parentNode.removeChild(node)
-      start = x: rect.left, y: rect.top + 1
-    normalizeRange([start, end])
+    getExpandedPointRange() ? getCollapsedPointRange()

   setLocationRangeFromPointRange: (pointRange) ->
     pointRange = normalizeRange(pointRange)
@@ -157,6 +142,32 @@

     @createLocationRangeFromDOMRange(domRange)

+  cursorPositionPlaceholder = makeElement
+    tagName: "span"
+    style: marginLeft: "-0.01em"
+    data: trixMutable: true, trixSerialize: false
+
+  getCollapsedPointRange = ->
+    return unless domRange = getDOMRange()
+    node = cursorPositionPlaceholder.cloneNode(true)
+    try
+      domRange.insertNode(node)
+      rect = node.getBoundingClientRect()
+    finally
+      node.parentNode.removeChild(node)
+    start = x: rect.left, y: rect.top + 1
+    normalizeRange(start)
+
+  getExpandedPointRange = ->
+    return unless domRange = getDOMRange()
+    rects = domRange.getClientRects()
+    if rects.length > 0
+      startRect = rects[0]
+      endRect = rects[rects.length - 1]
+      start = x: startRect.left, y: startRect.top + 1
+      end = x: endRect.right, y: endRect.top + 1
+      normalizeRange(start, end)
+
   getDOMSelection = ->
     selection = window.getSelection()
     selection if selection.rangeCount > 0


just add a quick helpful sentence about account --> upgrades

--- src/smc-webapp/r_misc.cjsx
+++ src/smc-webapp/r_misc.cjsx
@@ -1702,7 +1702,8 @@
             <Alert bsStyle='warning'>

<Icon name='arrow-circle-up' /> Adjust your project quota contributions

-                Adjust your contributions to the quotas on this project (disk space, memory, cores, etc.).  The total quotas for this project are the sum of the contributions of all collaborators and the free base quotas.
+                Adjust your contributions to the quotas on this project (disk space, memory, cores, etc.).  The total quotas for this project are the sum of the contributions of all collaborators and the free base quotas.  Go to "Account --> Upgrades" to see how your upgrades are currently allocated.
+

                 <Row>
                     <Col md=2>


Allow multiple shipping categories to be set for a shipping method

--- backend/app/assets/javascripts/admin/shipping_methods.js.coffee
+++ backend/app/assets/javascripts/admin/shipping_methods.js.coffee
@@ -1,10 +0,0 @@
-$ ->
-  ($ 'fieldset.categories input[type=checkbox]:not(:checked)').attr 'disabled', true  if ($ '.categories input:checked').length > 0
-  categoryCheckboxes = '.categories input[type=checkbox]'
-  $(categoryCheckboxes).change ->
-    if ($ this).is(':checked')
-      ($ categoryCheckboxes + ':not(:checked)').attr 'disabled', true
-      ($ this).removeAttr 'disabled'
-    else
-      ($ 'input[type=checkbox]').removeAttr 'disabled'
-


Wait for open connection when starting hive metastore and server2

--- actions/hdp/lifecycle.coffee
+++ actions/hdp/lifecycle.coffee
@@ -110,44 +110,64 @@
       code_skipped: 1
     , (err, started) ->
       callback err, started
+  hive_metastore_status: (ctx, callback) ->
+    # {hive_metastore_pid} = ctx.config.hdp
+    lifecyle.is_pidfile_running ctx, "/var/run/hive/metastore.pid", (err, running) ->
+      callback err, running
   hive_metastore_start: (ctx, callback) ->
-    {hive_user, hive_log_dir, hive_pid_dir} = ctx.config.hdp
-    ctx.execute
-      # su -l hive -c 'nohup hive --service metastore >/var/log/hive/hive.out 2>/var/log/hive/hive.log & echo $! >/var/run/hive/metastore.pid'
-      # su -l hive -c 'hive --service metastore'
-      cmd: "su -l #{hive_user} -c 'nohup hive --service metastore >#{hive_log_dir}/hive.out 2>#{hive_log_dir}/hive.log & echo $! > /var/run/hive/metastore.pid'"
-      code_skipped: 1
-    , (err, started) ->
-      callback err, started
+    {hive_user, hive_log_dir, hive_pid_dir, hive_metastore_host, hive_metastore_port, hive_metastore_timeout} = ctx.config.hdp
+    lifecyle.hive_metastore_status ctx, (err, running) ->
+      return callback err, false if err or running
+      ctx.execute
+        # su -l hive -c 'nohup hive --service metastore >/var/log/hive/hive.out 2>/var/log/hive/hive.log & echo $! >/var/run/hive/metastore.pid'
+        # su -l hive -c 'hive --service metastore'
+        cmd: "su -l #{hive_user} -c 'nohup hive --service metastore >#{hive_log_dir}/hive.out 2>#{hive_log_dir}/hive.log & echo $! > /var/run/hive/metastore.pid'"
+        code_skipped: 1
+      , (err, started) ->
+        return callback err if err
+        ctx.waitForConnection hive_metastore_host, hive_metastore_port, hive_metastore_timeout, (err) ->
+          callback err, started
   hive_metastore_stop: (ctx, callback) ->
     {hive_user, hive_pid_dir} = ctx.config.hdp
-    ctx.execute
-      # su -l hive -c "kill `cat /var/run/hive/metastore.pid`"
-      cmd: "su -l #{hive_user} -c \"kill `cat #{hive_pid_dir}/metastore.pid`\""
-      code_skipped: 1
-    , (err, started) ->
-      callback err, started
+    lifecyle.hive_metastore_status ctx, (err, running) ->
+      return callback err, false if err or not running
+      ctx.execute
+        # su -l hive -c "kill `cat /var/run/hive/metastore.pid`"
+        cmd: "su -l #{hive_user} -c \"kill `cat #{hive_pid_dir}/metastore.pid`\""
+        code_skipped: 1
+      , (err, started) ->
+        callback err, started
   hive_metastore_restart: (ctx, callback) ->
     lifecyle.hive_metastore_stop ctx, (err) ->
       return callback err if err
       lifecyle.hive_metastore_start ctx, callback
+  hive_server2_status: (ctx, callback) ->
+    # {hive_server2_pid} = ctx.config.hdp
+    lifecyle.is_pidfile_running ctx, "/var/run/hive/server2.pid", (err, running) ->
+      callback err, running
   hive_server2_start: (ctx, callback) ->
-    {hive_user, hive_log_dir, hive_pid_dir} = ctx.config.hdp
-    ctx.execute
-      # su -l hive -c 'nohup /usr/lib/hive/bin/hiveserver2 >/var/log/hive/hiveserver2.out 2>/var/log/hive/hiveserver2.log & echo $! >/var/run/hive/server2.pid'
-      # su -l hive -c '/usr/lib/hive/bin/hiveserver2'
-      cmd: "su -l #{hive_user} -c 'nohup /usr/lib/hive/bin/hiveserver2 >#{hive_log_dir}/hiveserver2.out 2>#{hive_log_dir}/hiveserver2.log & echo $! > /var/run/hive/server2.pid'"
-      code_skipped: 1
-    , (err, started) ->
-      callback err, started
+    {hive_user, hive_log_dir, hive_pid_dir, hive_server2_host, hive_server2_port, hive_server2_timeout} = ctx.config.hdp
+    lifecyle.hive_server2_status ctx, (err, running) ->
+      return callback err, false if err or running
+      ctx.execute
+        # su -l hive -c 'nohup /usr/lib/hive/bin/hiveserver2 >/var/log/hive/hiveserver2.out 2>/var/log/hive/hiveserver2.log & echo $! >/var/run/hive/server2.pid'
+        # su -l hive -c '/usr/lib/hive/bin/hiveserver2'
+        cmd: "su -l #{hive_user} -c 'nohup /usr/lib/hive/bin/hiveserver2 >#{hive_log_dir}/hiveserver2.out 2>#{hive_log_dir}/hiveserver2.log & echo $! > /var/run/hive/server2.pid'"
+        code_skipped: 1
+      , (err, started) ->
+        return callback err if err
+        ctx.waitForConnection hive_server2_host, hive_server2_port, hive_server2_timeout, (err) ->
+          callback err, started
   hive_server2_stop: (ctx, callback) ->
     {hive_user, hive_pid_dir} = ctx.config.hdp
-    ctx.execute
-      # su -l hive -c "kill `cat /var/run/hive/server2.pid"
-      cmd: "su -l #{hive_user} -c \"kill `cat #{hive_pid_dir}/server2.pid`\""
-      code_skipped: 1
-    , (err, started) ->
-      callback err, started
+    lifecyle.hive_server2_status ctx, (err, running) ->
+      return callback err, false if err or not running
+      ctx.execute
+        # su -l hive -c "kill `cat /var/run/hive/server2.pid"
+        cmd: "su -l #{hive_user} -c \"kill `cat #{hive_pid_dir}/server2.pid`\""
+        code_skipped: 1
+      , (err, started) ->
+        callback err, started
   hive_server2_restart: (ctx, callback) ->
     lifecyle.hive_server2_stop ctx, (err) ->
       return callback err if err
@@ -156,16 +176,6 @@
     {oozie_pid_dir} = ctx.config.hdp
     lifecyle.is_pidfile_running ctx, "#{oozie_pid_dir}/oozie.pid", (err, running) ->
       callback err, running
-    # ctx.execute
-    #   cmd: """
-    #   if pid=`cat #{oozie_pid_dir}/oozie.pid`; then
-    #     if ps cax | grep -v grep | grep $pid; then exit 0; else
-    #       rm -f #{oozie_pid_dir}/oozie.pid
-    #   fi; fi; exit 1
-    #   """
-    #   code_skipped: 1
-    # , (err, started) ->
-    #   callback err, started
   oozie_start: (ctx, callback) ->
     {oozie_user} = ctx.config.hdp
     lifecyle.oozie_status ctx, (err, running) ->


fix key prop warning

fix key prop warning

--- src/scripts/views/entry-list-view.coffee
+++ src/scripts/views/entry-list-view.coffee
@@ -17,6 +17,7 @@
         React.createElement EntryListItemView,
           entry: i
           index: index
+          key: i.date
           isOpen: i is @props.entry
           onOpen: @props.onOpen
           onClose: @props.onClose


Fix id of example profile

Fix id of example profile

--- www/coffee/controllers.coffee
+++ www/coffee/controllers.coffee
@@ -33,7 +33,7 @@
           location: 12
         '1':
           name: "Profile 2"
-          id: 5
+          id: 1

     @persist @profileData


Inline editor helper method

Inline editor helper method

--- lib/snippets-available.coffee
+++ lib/snippets-available.coffee
@@ -1,18 +1,8 @@
 _ = require 'underscore-plus'
-{$$, SelectListView, View} = require 'atom'
+{$$, SelectListView} = require 'atom'

 module.exports =
 class SnippetsAvailable extends SelectListView
-  # Public: Retrieve the active Editor.
-  #
-  # Returns: The active Editor as {Object}.
-  editor: -> atom.workspace.getActiveEditor()
-
-  # Public: Filter the fuzzy-search for the prefix.
-  #
-  # Returns: {String}
-  getFilterKey: -> 'prefix'
-
   # Public: Initialize object.
   #
   # Returns: `undefined`
@@ -20,6 +10,11 @@
     super
     @addClass('overlay from-top available-snippets')
     @command 'snippets:available', => @toggle()
+
+  # Public: Filter the fuzzy-search for the prefix.
+  #
+  # Returns: {String}
+  getFilterKey: -> 'prefix'

   toggle: ->
     if @hasParent()
@@ -29,7 +24,8 @@
       @attach()

   populate: ->
-    snippets = _.values(@snippets.getSnippets(@editor()))
+    editor = atom.workspace.getActiveEditor()
+    snippets = _.values(@snippets.getSnippets(editor))
     @setItems(snippets)

   attach: ->


fix msg

fix msg

--- library/membrane/bower.coffee
+++ library/membrane/bower.coffee
@@ -96,12 +96,12 @@
         assert from = @identify().underline
         return unless destination.derives b
         into = destination.identify().underline
-        message = "Merging the Bower libs from %s"
+        message = "Merge Bowers from %s into %s"
         previous = destination.bowerings or []
         assert previous? and _.isArray previous
         assert merged = previous.concat currents
         assert _.isArray merged = _.unique merged
-        logger.debug message.blue, into, from
+        logger.debug message.blue, from, into
         assert destination.bowerings = merged
         try super catch error; return this


some cumulative changes to the kernel

some cumulative changes to the kernel

--- library/nucleus/kernel.coffee
+++ library/nucleus/kernel.coffee
@@ -330,35 +330,45 @@
     # the configured addresses and ports. This method reads up the
     # scoping configuration in order to obtain the data necessary
     # for instantiating, configuring and launching up the servers.
+    # See the method implementation for info on the exact semantic.
     startupHttpsServer: ->
         assert server = nconf.get "server" or {}
         assert hostname = nconf.get "server:host" or 0
         assert _.isNumber(server.https), "no HTTPS port"
         assert _.isObject options = @resolveSslDetails()
         running = "Running HTTPS server at %s".magenta
+        arrived = "New #{"HTTPS".bold} connection at %s"
         location = "#{hostname}:#{server.https}".toString()
         logger.info running.underline, location.underline
         @secure = https.createServer options, @connect
         assert _.isObject @domain; @domain.add @secure
         do => @secure.listen server.https, hostname
         return @secure.on "connection", (socket) ->
+            key = socket.server?._connectionKey
+            key = try key.toString().underline
+            logger.debug arrived.green, key
             return socket.setNoDelay yes

     # Setup and launch either HTTP or HTTPS servers to listen at
     # the configured addresses and ports. This method reads up the
     # scoping configuration in order to obtain the data necessary
     # for instantiating, configuring and launching up the servers.
+    # See the method implementation for info on the exact semantic.
     startupHttpServer: ->
         assert server = nconf.get "server" or {}
         assert hostname = nconf.get "server:host" or 0
         assert _.isNumber(server.http), "no HTTP port"
         running = "Running HTTP server at %s".magenta
+        arrived = "New #{"HTTP".bold} connection at %s"
         location = "#{hostname}:#{server.http}".toString()
         logger.info running.underline, location.underline
         @server = http.createServer @connect, undefined
         assert _.isObject @domain; @domain.add @server
         do => @server.listen server.http, hostname
         return @server.on "connection", (socket) ->
+            key = socket.server?._connectionKey
+            key = try key.toString().underline
+            logger.debug arrived.green, key
             return socket.setNoDelay yes

     # Setup and attach Socket.IO handlers to each of the servers.


adding a gene was not updating.

adding a gene was not updating.

--- static/js/controllers/designer.js.coffee
+++ static/js/controllers/designer.js.coffee
@@ -271,10 +271,8 @@
     $scope.guidesReady = false
     guidesFactory.data.tissues.splice(idx, 1)
     $scope.generateGuidesPromise = guidesFactory.generateGuides()
-    $scope.generateGuidesPromise.then (guidesData) ->
-      computeGuidesData(guidesData["gene_to_exon"])
-      $scope.gene = $scope.gene_to_exon[0]
-      $scope.guidesReady = true
+    $scope.generateGuidesPromise.then (task_id) ->
+      $location.path('/designer/' + task_id)

   # Searching
   $scope.geneTissueQuery = ""
@@ -296,9 +294,8 @@
             break

     $scope.guidesReady = false
-    $scope.generateGuidesPromise = guidesFactory.generateGuides().then (guidesData) ->
-      computeGuidesData(guidesData["gene_to_exon"])
-      $scope.guidesReady = true
+    $scope.generateGuidesPromise = guidesFactory.generateGuides().then (task_id) ->
+      $location.path('/designer/' + task_id)

   # individual guide selection
   $scope.show_different_guides = false
@@ -308,16 +305,14 @@
   $scope.selectedGene = (gene) ->
     guidesFactory.data.genes.push gene
     $scope.guidesReady = false
-    $scope.generateGuidesPromise = guidesFactory.generateGuides().then (guidesData) ->
-      computeGuidesData(guidesData["gene_to_exon"])
-      $scope.guidesReady = true
+    $scope.generateGuidesPromise = guidesFactory.generateGuides().then (task_id) ->
+      $location.path('/designer/' + task_id)

   $scope.selectedTissue = (tissue) ->
     guidesFactory.data.tissues.push tissue
     $scope.guidesReady = false
-    $scope.generateGuidesPromise = guidesFactory.generateGuides().then (guidesData) ->
-      computeGuidesData(guidesData["gene_to_exon"])
-      $scope.guidesReady = true
+    $scope.generateGuidesPromise = guidesFactory.generateGuides().then (task_id) ->
+      $location.path('/designer/' + task_id)

   # $scope.additionalGene = "" # what the person is typing.
   # $scope.$watch 'additionalGene', () ->


adapted uniform names for arbitrary modes, fixes #681

--- app/assets/javascripts/oxalis/geometries/materials/arbitrary_plane_material_factory.coffee
+++ app/assets/javascripts/oxalis/geometries/materials/arbitrary_plane_material_factory.coffee
@@ -32,7 +32,7 @@
     return _.template(
       """
       uniform sampler2D <%= colorName %>_texture;
-      uniform float brightness, contrast;
+      uniform float <%= colorName %>_brightness, <%= colorName %>_contrast;
       varying vec2 vUv;

       void main()
@@ -43,7 +43,7 @@
         color_value = texture2D( <%= colorName %>_texture, vUv).r;

         /* Brightness / Contrast Transformation */
-        color_value = (color_value + brightness - 0.5) * contrast + 0.5;
+        color_value = (color_value + <%= colorName %>_brightness - 0.5) * <%= colorName %>_contrast + 0.5;

         /* Set frag color */
         gl_FragColor = vec4(color_value, color_value, color_value, 1.0);


missing key for element array

missing key for element array

--- resources/assets/coffee/react/profile-page/historical.coffee
+++ resources/assets/coffee/react/profile-page/historical.coffee
@@ -101,7 +101,7 @@

       if @props.scoresRecent?.length
         [
-          el window._exported.PlayDetailList, scores: @props.scoresRecent
+          el window._exported.PlayDetailList, key: 'play-detail-list', scores: @props.scoresRecent

           span
             key: 'show-more-row'


Activity: AppView: Add filterWarning widget to show actively using filter

--- client/Social/Activity/AppView.coffee
+++ client/Social/Activity/AppView.coffee
@@ -45,7 +45,8 @@
     @header.bindTransitionEnd()

     @feedWrapper.ready =>
-      @activityHeader = @feedWrapper.controller.activityHeader
+      @activityHeader  = @feedWrapper.controller.activityHeader
+      {@filterWarning} = @feedWrapper
       @on 'scroll', (event) =>
         if event.delegateTarget.scrollTop > 50
           @activityHeader.setClass "scrolling-up-outset"
@@ -136,6 +137,7 @@
       # scrollView        : no

     @listWrapper = @controller.getView()
+    @filterWarning = new FilterWarning

     @controller.ready => @emit "ready"

@@ -144,5 +146,30 @@

   pistachio:->
     """
+      {{> @filterWarning}}
       {{> @listWrapper}}
     """
+
+class FilterWarning extends JView
+
+  constructor:->
+    super cssClass : 'filter-warning hidden'
+
+    @warning   = new KDCustomHTMLView
+    @goBack    = new KDButtonView
+      cssClass : 'goback-button'
+      callback : =>
+        {appManager} = KD.singletons
+        appManager.tell 'Activity', 'populateActivity', {}, @bound 'hide'
+
+  pistachio:->
+    """
+      {{> @warning}}
+      {{> @goBack}}
+    """
+
+  showWarning:(tag)->
+    @warning.updatePartial \
+      """You are now looking activities tagged with <strong>##{tag}</strong> """
+
+    @show()


Activity: ChatInputWidget: Test added

Activity: ChatInputWidget: Test added

--- client/activity/lib/components/chatinputwidget/test/view.coffee
+++ client/activity/lib/components/chatinputwidget/test/view.coffee
@@ -25,6 +25,20 @@
     query         : 'wh'
   }

+  dataWithOneChar = {
+    dropboxConfig : toImmutable {
+      component   : EmojiDropbox
+      getters     : {
+        'items'
+        'selectedIndex'
+        'query'
+      }
+    }
+    items         : toImmutable [ 'whale', 'white_check_mark', 'white_circle' ]
+    selectedIndex : 1
+    query         : 'w'
+  }
+
   describe '::render', ->

     it 'renders input with provided prop value and placeholder', ->
@@ -41,12 +55,42 @@
       expect(value).toEqual value
       expect(input.getAttribute 'placeholder').toEqual placeholder

+    it 'renders input with provided prop value with common emoji and placeholder', ->
+
+      value       = ':)'
+      placeholder = 'Type here...'
+      result = TestUtils.renderIntoDocument(
+        <ChatInputWidget placeholder={placeholder} data={{ value }} />
+      )
+
+      input = TestUtils.findRenderedDOMComponentWithTag result, 'textarea'
+
+      value = input.value
+      expect(value).toEqual value
+      expect(input.getAttribute 'placeholder').toEqual placeholder
+
+
+    it 'renders input with provided prop value with common emoji and meaningless word  and placeholder', ->
+
+      value       = ':pasdasdas'
+      placeholder = 'Type here...'
+      result = TestUtils.renderIntoDocument(
+        <ChatInputWidget placeholder={placeholder} data={{ value }} />
+      )
+
+      input = TestUtils.findRenderedDOMComponentWithTag result, 'textarea'
+
+      value = input.value
+      expect(value).toEqual value
+      expect(input.getAttribute 'placeholder').toEqual placeholder
+
+
     it 'renders dropbox according to dropbox config and passed data', ->

       result = TestUtils.renderIntoDocument(
         <ChatInputWidget data={data} />
       )
-
+      console.log 'result ', result
       component = TestUtils.findRenderedComponentWithType result, EmojiDropbox
       expect(component).toExist()

@@ -67,6 +111,21 @@

       expect(-> TestUtils.findRenderedComponentWithType  result, EmojiDropbox).toThrow()

+    it 'renders dropbox according to dropbox config and passed data with single character', ->
+
+      result = TestUtils.renderIntoDocument(
+        <ChatInputWidget data={dataWithOneChar} />
+      )
+
+      component = TestUtils.findRenderedComponentWithType result, EmojiDropbox
+      expect(component).toExist()
+      dropbox = TestUtils.findRenderedComponentWithType component, PortalDropbox
+      content = dropbox.getContentElement()
+      title   = content.parentNode.querySelector '.Dropbox-subtitle'
+      items   = content.querySelectorAll '.DropboxItem'
+
+      expect(title.textContent).toNotEqual ":#{data.query}"
+

   describe '::onChange', ->


apps-app-controller: Removed old and unused app create modal.

--- client/app/Applications/Apps.kdapplication/AppController.coffee
+++ client/app/Applications/Apps.kdapplication/AppController.coffee
@@ -124,13 +124,6 @@
       callback err, res
       @manageUpdateAllButton res

-  fetchAutoCompleteDataForTags:(inputValue,blacklist,callback)->
-    KD.remote.api.JTag.byRelevance inputValue, {blacklist}, (err,tags)->
-      unless err
-        callback? tags
-      else
-        log "there was an error fetching topics #{err.message}"
-
   updateApps:->
     @utils.wait 100, @feedController?.changeActiveSort "meta.modifiedAt"

@@ -166,176 +159,6 @@
       updateAppsButton.hide()
       updateAppsButton.setData []

-  putAddAnAppButton:->
-    {facetsController} = @feedController
-    innerNav = facetsController.getView()
-    innerNav.addSubView addButton = new KDButtonView
-      title     : "Add an App"
-      style     : "small-gray"
-      callback  : => @showAppSubmissionView()
-
   createApp:(formData, callback)->
     KD.remote.api.JApp.create formData, (err, app)->
       callback? err, app
-
-  showAppSubmissionView:->
-    modal       = new AppSubmissionModal
-    modal.$().css top : 75
-    {modalTabs} = modal
-    {forms}     = modalTabs
-    modal.on "AppSubmissionFormSubmitted", (formData)=>
-      @createApp formData, (err,res)=>
-        unless err
-          new KDNotificationView
-            title : "App created successfully!"
-          modal.destroy()
-        else
-          warn "there was an error creating the app",err
-          new KDNotificationView
-            title : "there was an error creating the app"
-
-    modalTabs.on "PaneDidShow", (pane)=>
-      # scriptForm = forms['Technical Stuff']
-      # scriptForm.addCustomData "scriptCode", scriptForm.ace.getValue()
-      # scriptForm.addCustomData "scriptSyntax", scriptForm.ace.getActiveSyntaxName()
-      # scriptForm.addCustomData "requirementsCode", scriptForm.reqs.getValue()
-      # scriptForm.addCustomData "requirementsSyntax", scriptForm.reqs.getActiveSyntaxName()
-      if pane.name is "Review & Submission"
-        @createAppSummary modal, pane
-
-    # TAGS AUTOCOMPLETE
-    selectedItemWrapper = new KDCustomHTMLView
-      tagName  : "div"
-      cssClass : "tags-selected-item-wrapper clearfix"
-
-    tagController = new TagAutoCompleteController
-      name                : "meta.tag"
-      type                : "tags"
-      itemClass           : TagAutoCompleteItemView
-      selectedItemClass   : TagAutoCompletedItemView
-      outputWrapper       : selectedItemWrapper
-      listWrapperCssClass : "tags"
-      form                : forms['Technical Stuff']
-      itemDataPath        : 'title'
-      dataSource          : (args, callback)=>
-        {inputValue} = args
-        blacklist = (data.getId() for data in tagController.getSelectedItemData() when 'function' is typeof data.getId)
-        @fetchAutoCompleteDataForTags inputValue,blacklist,callback
-
-    tagAutoComplete = tagController.getView()
-    tagsField       = forms['Technical Stuff'].fields.Tags
-    tagsField.addSubView tagAutoComplete
-    tagsField.addSubView selectedItemWrapper
-
-    modal.on "KDModalViewDestroyed", -> tagController.destroy()
-
-    # # INSTALL SCRIPT ACE
-    # scriptForm      = forms['Technical Stuff']
-    # scriptField     = scriptForm.fields.Script
-    #
-    # scriptField.addSubView aceWrapper = new KDCustomHTMLView
-    #   cssClass : "code-snip-holder dark-select"
-    #
-    # aceWrapper.addSubView scriptForm.ace = new MiniAceEditor
-    #   defaultValue  : "# Type your install script here..."
-    #   autoGrow      : yes
-    #   path          : "~~~/dummy-path/dummy.coffee"
-    #   name          : "dummy.coffee"
-    #
-    # scriptForm.ace.on 'sizes.height.change', (options) =>
-    #   {height} = options
-    #   scriptForm.ace.$().parent().height height + 25
-    #
-    # scriptForm.ace.refreshEditorView()
-    # scriptForm.ace.saveSyntaxForExtension "coffee"
-
-    # # REQUIREMENTS SCRIPT ACE
-    # reqsField        = scriptForm.fields.Reqs
-    #
-    # reqsField.addSubView reqsWrapper = new KDCustomHTMLView
-    #   cssClass : "code-snip-holder dark-select"
-    #
-    # reqsWrapper.addSubView scriptForm.reqs = new MiniAceEditor
-    #   defaultValue  : "# Type your requirement options here..."
-    #   autoGrow      : yes
-    #   path          : "~~~/dummy-path/dummy.coffee"
-    #   name          : "dummy.coffee"
-    #
-    # scriptForm.reqs.on 'sizes.height.change', (options) =>
-    #   {height} = options
-    #   scriptForm.ace.$().parent().height height + 55
-    #
-    # scriptForm.reqs.refreshEditorView()
-    # scriptForm.reqs.saveSyntaxForExtension "coffee"
-
-    # IMAGE UPLOADERS
-    thumbField = forms.Visuals.fields.thumbnail
-    thumbField.addSubView thumbUploader = new KDImageUploadView
-      limit           : 1
-      preview         : "thumbs"
-      extensions      : null
-      fileMaxSize     : 512
-      totalMaxSize    : 512
-      fieldName       : "thumbnails"
-      convertToBlob   : yes
-      actions         : {
-        listThumb     :
-          [
-            'scale', {
-              shortest: 160
-            }
-            'crop', {
-              width   : 160
-              height  : 80
-            }
-          ]
-        appThumb      :
-          [
-            'scale', {
-              shortest: 90
-            }
-            'crop', {
-              width   : 90
-              height  : 90
-            }
-          ]
-      }
-      title           : "Drop a logo of the app here..."
-
-    screenshotsField = forms.Visuals.fields.screenshots
-    screenshotsField.addSubView thumbUploader = new KDImageUploadView
-      limit           : 10
-      preview         : "thumbs"
-      extensions      : null
-      fileMaxSize     : 512
-      totalMaxSize    : 4096
-      fieldName       : "screenshots"
-      convertToBlob   : yes
-      actions         : {
-        screenshot    :
-          [
-            'scale', {
-              shortest: 768
-            }
-            'crop', {
-              width   : 1024
-              height  : 768
-            }
-          ]
-        thumb         :
-          [
-            'scale', {
-              shortest: 96
-            }
-            'crop', {
-              width   : 96
-              height  : 96
-            }
-          ]
-      }
-      title           : "Drop some screenshots here..."
-
-  createAppSummary:(modal, pane)->
-    modal.preview.destroy() if modal.preview
-    formData = modal.modalTabs.getFinalData()
-    pane.form.addSubView (modal.preview = new AppPreSubmitPreview {},formData),null,yes


SidebarMachineSharePopup: Added sticky option. Create overlay if it's sticky.

--- client/app/lib/activity/sidebar/sidebarmachinesharepopup.coffee
+++ client/app/lib/activity/sidebar/sidebarmachinesharepopup.coffee
@@ -3,6 +3,7 @@
 KDCustomHTMLView = kd.CustomHTMLView
 KDView = kd.View
 KDButtonView = kd.ButtonView
+KDOverlayView = kd.OverlayView
 AvatarView = require 'app/commonviews/avatarviews/avatarview'
 showError = require 'app/util/showError'
 remote = require('app/remote').getInstance()
@@ -17,14 +18,19 @@
     options.width    = 250
     options.height   = 'auto'
     options.cssClass = 'activity-modal share-modal'
+    options.sticky  ?= no

     super options, data

     @createArrow()
     @createElements()

-    kd.getSingleton('windowController').addLayer this
-    @on 'ReceivedClickElsewhere', @bound 'destroy'
+    if options.sticky
+      @createOverlay()
+      kd.singletons.router.once 'RouteInfoHandled', @bound 'destroy'
+    else
+      kd.singletons.windowController.addLayer this
+      @on 'ReceivedClickElsewhere', @bound 'destroy'

   createArrow: ->
@@ -34,6 +40,13 @@
     _addSubview new KDCustomHTMLView
       cssClass  : 'modal-arrow'
       position  : top : 20
+
+
+  createOverlay: ->
+
+    @overlay = new KDOverlayView
+      isRemovable : no
+      cssClass    : 'env-modal-overlay approve-overlay'

   createElements: ->


regex escape curator inbox search

regex escape curator inbox search

--- client/controllers/searchInput.coffee
+++ client/controllers/searchInput.coffee
@@ -10,6 +10,7 @@
     placeholder: Defaults to 'Search'
     classes:     Classes which will be applied to the input's parent element
 ####
+{ regexEscape } = require '/imports/utils'

 clearSearch = (instance) ->
   instance.textFilter.set('')
@@ -46,7 +47,7 @@
       clearSearch(instance)
     else
       instance.textFilter.set
-        $regex: instance.$(event.target).val()
+        $regex: regexEscape(instance.$(event.target).val())
         $options: 'i'
       if instance.data.tableId
         Meteor.defer ->


Actually allow newlines

Actually allow newlines

--- resources/assets/coffee/react/beatmap-discussions/new-reply.coffee
+++ resources/assets/coffee/react/beatmap-discussions/new-reply.coffee
@@ -180,7 +180,7 @@

   setMessage: (e) =>
-    @setState message: e.target.value.replace /\n/g, ' '
+    @setState message: e.target.value

   validPost: =>


IDEView: Added code comment.

IDEView: Added code comment.

--- client/ide/lib/views/tabview/ideview.coffee
+++ client/ide/lib/views/tabview/ideview.coffee
@@ -105,7 +105,7 @@

   createEditor: (file, content, callback = kd.noop, emitChange = yes) ->

-    unless file
+    unless file # create dummy file and pass machine to that file
       path    = @getDummyFilePath()
       machine = kd.singletons.appManager.getFrontApp().mountedMachine
       file    = FSHelper.createFileInstance { path, machine }