ScrollView: demo page WIP

ScrollView: demo page WIP

--- playground/main.coffee
+++ playground/main.coffee
@@ -1,17 +1,54 @@
+paragraphs   = ["Frankfurter ground round jowl, tongue flank tenderloin pastrami.  Ball tip jowl ground round, leberkas drumstick pork chop doner.  Short loin venison pork belly beef beef ribs shank.  Beef ribs ham bacon pork loin salami, biltong strip steak pastrami ham hock boudin pork belly.","Kevin flank tenderloin porchetta pork biltong salami, capicola beef ribs shankle turducken doner cow corned beef.  Short ribs biltong tenderloin turducken pork doner hamburger kielbasa venison ground round drumstick chuck jerky.  Swine rump ham landjaeger.  Venison ribeye biltong meatloaf shank doner tongue chicken ham filet mignon ball tip hamburger tenderloin ground round.","Tri-tip spare ribs ham hamburger landjaeger.  Ground round flank jowl, rump chuck prosciutto frankfurter tongue short ribs turkey jerky pork chop bacon venison pork belly.  Tail bresaola brisket shoulder leberkas.  Kielbasa cow turkey chuck, rump jowl meatball.  Porchetta bresaola shankle pork loin shoulder beef ribs.  Biltong tenderloin landjaeger t-bone bacon hamburger.","Beef ribs shank biltong drumstick rump salami venison pork loin prosciutto ribeye filet mignon frankfurter meatloaf.  Chuck turducken ground round shoulder tail fatback.  Turkey hamburger frankfurter shank fatback pork pork belly short loin porchetta.  Shankle pork loin shoulder ham hock.  Pastrami pig ham chicken flank bresaola.","Beef ribs biltong filet mignon bacon rump shankle landjaeger pork chop chuck ball tip kevin cow.  Drumstick corned beef salami, biltong beef shoulder spare ribs flank sirloin beef ribs bacon.  Rump jowl tail, short loin doner kielbasa pig beef ribs drumstick pastrami jerky strip steak.  Ham fatback andouille shankle drumstick pork loin.","Beef ribs brisket pork belly, ball tip venison short loin pork chop sausage bacon kielbasa jerky.  Short ribs sirloin pork chop pork loin tenderloin brisket tri-tip bresaola.  Ham fatback drumstick venison strip steak sausage prosciutto leberkas biltong meatball pork loin doner.  Flank chuck brisket pork loin turkey.","Leberkas ribeye beef ribs, filet mignon brisket short loin pork tenderloin rump sirloin.  Sirloin bacon beef, ball tip doner pork belly pork loin strip steak venison chicken.  Spare ribs tri-tip andouille capicola pork belly sausage landjaeger, bacon kevin chuck shoulder hamburger leberkas biltong ham.  Beef ribs filet mignon shoulder meatloaf.  Tri-tip pig ribeye venison short loin meatball.","Meatball brisket porchetta, shankle jowl beef sausage chuck landjaeger ground round cow ham hock t-bone shank.  Pork belly cow kielbasa, t-bone bacon shoulder rump.  Shankle beef ribs turducken, ham filet mignon corned beef bacon beef meatloaf swine t-bone ground round.  Boudin leberkas shoulder ribeye biltong chuck.  Cow hamburger turducken, salami pastrami porchetta pork chop kevin jowl shoulder swine chuck bresaola.  Chicken ribeye turkey pork pancetta leberkas.","Tongue jerky meatball, kielbasa short loin pork shank turkey pork belly boudin porchetta biltong.  Turducken beef ribs chicken swine turkey chuck.  Tongue ham hock chuck turkey, pig fatback tail frankfurter.  Jowl kielbasa shankle landjaeger doner capicola, filet mignon beef pork short loin.  Flank pork chop tongue pork belly venison, bacon tri-tip frankfurter filet mignon fatback turkey t-bone biltong boudin.  Corned beef pancetta ribeye jerky, tenderloin doner strip steak swine.","Venison turducken pig porchetta boudin.  Flank biltong beef ribs prosciutto.  Pig pork rump drumstick, corned beef bresaola ham hock shankle doner tenderloin flank leberkas.  Ribeye prosciutto chicken turkey bacon.  Shoulder pork chop drumstick t-bone short loin meatloaf prosciutto.  Pancetta salami t-bone, cow swine turducken corned beef ground round shankle pork filet mignon frankfurter tenderloin.  Flank ham hock strip steak, corned beef salami short loin hamburger spare ribs pig boudin meatball tri-tip sausage."]
+container    = new KDView
+addParagraph = -> scrollView.setPartial "<p>#{paragraphs[KD.utils.getRandomNumber(10)-1]}</p>"

-container = new KDView
+container.addSubView scrollView = new KDScrollView
+  ownScrollBars : yes

-container.addSubView new KDHeaderView
-  type  : 'big'
-  title : 'KD Framework Playground'
+scrollView.setStyle
+  margin     : '100px auto'
+  width      : 300
+  height     : 200
+  background : '#1aa5fd'
+  color      : 'white'
+  padding    : 30

-container.addSubView new KDCustomHTMLView
-  partial : "Of brilliant syntheses not a sunrise but a galaxyrise. Drake Equation, decipherment radio telescope culture. Citizens of distant epochs. Bits of moving fluff venture dispassionate extraterrestrial observer, hundreds of thousands. Star stuff harvesting star light a very small stage in a vast cosmic arena, a billion trillion a still more glorious dawn awaits, hundreds of thousands the only home we've ever known colonies kindling the energy hidden in matter radio telescope, preserve and cherish that pale blue dot network of wormholes. Kindling the energy hidden in matter Jean-François Champollion Drake Equation star stuff harvesting star light birth star stuff harvesting star light a very small stage in a vast cosmic arena the sky calls to us, of brilliant syntheses. Shores of the cosmic ocean the only home we've ever known consciousness? Rig Veda the sky calls to us are creatures of the cosmos shores of the cosmic ocean tesseract gathered by gravity a mote of dust suspended in a sunbeam!"

 container.addSubView new KDButtonView
-  title     : "Hello World!"
+  title     : "Add a paragraph"
+  callback  : addParagraph
+
+
+container.addSubView new KDButtonView
+  title     : "Remove a paragraph"
   callback  : ->
-    new KDNotificationView
-      title : "Hello World!!"
+    $paragraphs = scrollView.$('p')
+    $paragraphs[$paragraphs.length - 1]?.remove()
+
+addParagraph() for i in [0...100]

 container.appendToDomBody()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


fix the proposal description name can get longer than 140 chars, but is then truncated

Former-commit-id: 0533f3529f3fef0616c2679a0a23021017d1fd8f [formerly ba404858be71cd06a72f4e7c538ec06a9eef1500] [formerly e3931c74e7cdcecdcaab9fadf2318e23dcae6a2d [formerly e3931c74e7cdcecdcaab9fadf2318e23dcae6a2d [formerly f357bffaa47182f3424daf6076cf4a11e642f7f4]]]
Former-commit-id: 938f6367b90514edd2cfad0746350f67ef1e106f [formerly 0a9322239ccfffe7aa53a304153b71e3dcd6b687]
Former-commit-id: f45bd136118eb2965609301b82ba6eeda1b43929
Former-commit-id: 711a59c1bcb98af7591b6070dae0b4160ff69c5d

--- app/assets/javascripts/backbone/apps/franklin/proposal/_abstract_proposal_view.coffee
+++ app/assets/javascripts/backbone/apps/franklin/proposal/_abstract_proposal_view.coffee
@@ -51,7 +51,7 @@
     bindings :
       '.m-proposal-description-title' :
         observe : ['name', 'description']
-        onGet : (values) -> @model.title()
+        onGet : (values) -> @model.title(280)
       '.m-proposal-description-body' :
         observe : 'description'
         updateMethod: 'html'


ugh remove fake people data again

ugh remove fake people data again

--- app/components/review-bulk-invite-members-page/review-bulk-invite-members-page.coffee
+++ app/components/review-bulk-invite-members-page/review-bulk-invite-members-page.coffee
@@ -7,12 +7,7 @@
   url: '/groups/:groupId/invite_members/review_upload'
   template: require('./review-bulk-invite-members-page.html')
   params:
-    people: [
-      {email: 'derek@enspiral.com', name: 'Derek Razo', new_member: false},
-      {email: 'eugene@enspiral.com', name: 'Eugene Lynch', new_member: true},
-      {email: 'data@doge.com', name: 'Data Doge', new_member: false},
-      {email: 'chelsearobinson@gmail.com', name: '', new_member: true}
-    ]
+    people: null
     groupId: null
   controller: (config, CurrentUser, Dialog, Error, LoadBar, $location, $q, Records, $scope, $state, $stateParams, $timeout, UserCan) ->


get active node while finding decision #347

--- app/assets/javascripts/oxalis/view/skeletontracing/abstract_tree_renderer.coffee
+++ app/assets/javascripts/oxalis/view/skeletontracing/abstract_tree_renderer.coffee
@@ -236,12 +236,10 @@

   drawChainWithChainIndicatorFromTo : (top, middle, tree, decision) ->

-    hasActiveNode = @chainContainsActiveNode(tree, decision.node)
-
     # Draw root, chain indicator and decision point
     @addNode(middle, top, tree.id)
     @drawEdge(middle, top, middle, top + 0.5 * @nodeDistance)
-    @drawChainIndicator(middle, top + 0.5 * @nodeDistance, top + 1.5 * @nodeDistance, hasActiveNode)
+    @drawChainIndicator(middle, top + 0.5 * @nodeDistance, top + 1.5 * @nodeDistance, decision.hasActiveNode)
     @drawEdge(middle, top + 1.5 * @nodeDistance, middle, top + 2 * @nodeDistance)
     @addNode(middle, top + 2 * @nodeDistance, decision.node.id)

@@ -266,6 +264,7 @@
     # Decision points will definitely be drawn.

     chainCount = 0
+    hasActiveNode = false

     # skip comment check on first node
     if tree.children.length == 1
@@ -273,6 +272,8 @@
         chainCount++

     while tree.children.length == 1 and !@nodeIdHasComment(tree.id)
+      if !hasActiveNode
+          hasActiveNode = tree.id == @activeNodeId
       tree = tree.children[0]
       chainCount++

@@ -281,22 +282,8 @@
       chainCount,
       isBranch: tree.children.length > 1
       isLeaf: tree.children.length == 0
-      isComment: @nodeIdHasComment(tree.id)
+      hasActiveNode: hasActiveNode
     }
-
-
-  chainContainsActiveNode : (root, decisionNode) ->
-
-      # Find out, if the chain contains an active node
-      node = root.children[0]
-      hasActiveNode = false
-
-      while node.id != decisionNode.id
-        if node.id == @activeNodeId
-          return true
-        node = node.children[0]
-
-      return false

   recordWidths : (tree) ->


Fix test

Fix test

--- spec/ResizeBuffer.coffee
+++ spec/ResizeBuffer.coffee
@@ -88,15 +88,13 @@
     it 'should resize it right to default dimension', (done) ->
       expected =
         width: c.defaultDimension
-        height: c.defaultDimension
-      out.on 'data', (data) ->
-        buffer = sharp data
-        buffer.metadata (err, meta) ->
-          chai.expect(meta.width).to.be.equal expected.width
-          chai.expect(meta.height).to.be.equal expected.height
-          done()
-
-      testutils.getBuffer __dirname + '/fixtures/lenna.png', (buffer) ->
+      out.on 'data', (data) ->
+        buffer = sharp data
+        buffer.metadata (err, meta) ->
+          chai.expect(meta.width).to.be.equal expected.width
+          done()
+
+      testutils.getBuffer __dirname + '/fixtures/foo.jpeg', (buffer) ->
         ins.send buffer

     it 'should not resize up (just down, without enlargement)', (done) ->


Implemented copy and exists functions

Implemented copy and exists functions

--- src/utils.coffee
+++ src/utils.coffee
@@ -15,3 +15,12 @@
 		fs.openAsync(path.dirname(file), 'r', constants.O_DIRECTORY)
 	.tap(fs.fsyncAsync)
 	.then(fs.closeAsync)
+
+exports.copyFile = (source, target) ->
+	fs.readFileAsync(source)
+	.then (rf) ->
+		fs.writeFileAsync(target, rf)
+
+exports.exists = (path) ->
+	new Promise (resolve) ->
+        fs.exists(path, resolve)


sidebar in small screens rendering bug fix

--- js/render.coffee
+++ js/render.coffee
@@ -67,6 +67,18 @@
      else
       throw new Error "Unknown type #{node.type} - #{node.level}"

+    _sidebarAligned: ->
+     sidebar = @getOffsetTop @elems.sidebar, document.body
+     main = @getOffsetTop @elems.main, document.body
+
+     mainHeight = @elems.main.offsetHeight
+
+     diff = Math.abs main - sidebar
+     if diff < mainHeight / 4
+      return true
+     else
+      return false
+
     getBreakCost: (node) ->
      parent = node.parent()
      cost = 0
@@ -237,8 +249,7 @@
      @elems.pageBackgrounds = elem

     setPages: (H, W) ->
-     #@setFills()
-     #return
+     return if not @_sidebarAligned()
      @pageHeight = H
      @mainNodes = @getMainNodes()
      @sidenoteMap =  @getSidenoteMap()
@@ -338,6 +349,7 @@

     setFills: ->
+     return if not @_sidebarAligned()
      for sidenote in @sidenotes
       elemSidenote = sidenote.elem
       elemContent = @map.nodes[sidenote.link].elem


Compute the expected scale factor in tests for more consistent results

--- spec/minimap-spec.coffee
+++ spec/minimap-spec.coffee
@@ -4,7 +4,7 @@
 Minimap = require '../lib/minimap'

 describe 'Minimap', ->
-  [editor, editorElement, minimap, largeSample, smallSample] = []
+  [editor, editorElement, minimap, largeSample, smallSample, minimapVerticalScaleFactor, minimapHorizontalScaleFactor] = []

   beforeEach ->
     atom.config.set 'minimap.charHeight', 4
@@ -17,7 +17,9 @@
     jasmine.attachToDOM(editorElement)
     editorElement.setHeight(50)
     editorElement.setWidth(200)
-    editor.setLineHeightInPixels(10)
+
+    minimapVerticalScaleFactor = 5 / editor.getLineHeightInPixels()
+    minimapHorizontalScaleFactor = 2 / editor.getDefaultCharWidth()

     dir = atom.project.getDirectories()[0]

@@ -42,12 +44,12 @@
     expect(minimap.getHeight()).toEqual(editor.getScreenLineCount() * 5)

   it 'measures the scaling factor between the editor and the minimap', ->
-    expect(minimap.getVerticalScaleFactor()).toEqual(0.5)
-    expect(minimap.getHorizontalScaleFactor()).toEqual(2 / editor.getDefaultCharWidth())
+    expect(minimap.getVerticalScaleFactor()).toEqual(minimapVerticalScaleFactor)
+    expect(minimap.getHorizontalScaleFactor()).toEqual(minimapHorizontalScaleFactor)

   it 'measures the editor visible area size at minimap scale', ->
     editor.setText(largeSample)
-    expect(minimap.getTextEditorScaledHeight()).toEqual(25)
+    expect(minimap.getTextEditorScaledHeight()).toEqual(50 * minimapVerticalScaleFactor)

   it 'measures the available minimap scroll', ->
     editor.setText(largeSample)
@@ -161,8 +163,8 @@
       editorScrollRatio = editorElement.getScrollTop() / (editorElement.getScrollHeight() - editorElement.getHeight())

     it 'scales the editor scroll based on the minimap scale factor', ->
-      expect(minimap.getTextEditorScaledScrollTop()).toEqual(500)
-      expect(minimap.getTextEditorScaledScrollLeft()).toEqual(200 * minimap.getHorizontalScaleFactor())
+      expect(minimap.getTextEditorScaledScrollTop()).toEqual(1000 * minimapVerticalScaleFactor)
+      expect(minimap.getTextEditorScaledScrollLeft()).toEqual(200 * minimapHorizontalScaleFactor)

     it 'computes the offset to apply based on the editor scroll top', ->
       expect(minimap.getScrollTop()).toEqual(editorScrollRatio * minimap.getMaxScrollTop())


Add JSBeautify extra_liners option in Settings View

--- src/languages/html.coffee
+++ src/languages/html.coffee
@@ -82,5 +82,11 @@
       type: 'boolean'
       default: false
       description: "End output with newline"
+    extra_liners:
+      type: 'array'
+      default: ['head', 'body', '/html']
+      items:
+        type: 'string'
+      description: "List of tags (defaults to [head,body,/html] that should have an extra newline before them."

 }


Remove spurious comment

Remove spurious comment

--- spec/motions-spec.coffee
+++ spec/motions-spec.coffee
@@ -396,7 +396,6 @@
       it "moves the cursor to the specified search pattern", ->
         keydown '/'

-        # How to simulate typing?
         editor.commandModeInputView.editor.setText 'def'
         editor.commandModeInputView.editor.trigger 'core:confirm'


Made `Population.addBird()` more flexible.

It used to take no arguments and always generate a random bird to add to the population. Now it takes a bird as an argument, and if that bird is undefined then it creates a new random bird.

--- src/lib/population.coffee
+++ src/lib/population.coffee
@@ -34,11 +34,12 @@
 class Population

   constructor: (popSize) ->
-    @_unpairedBirds = (new Bird(0) for [0...popSize])
+    @_unpairedBirds = (new Bird() for [0...popSize])
     @_pairings = []

-  addBird: () ->
-    @_unpairedBirds.push(new Bird())
+  addBird: (bird) ->
+    bird ?= new Bird()
+    @_unpairedBirds.push(bird)

   birds: -> @_unpairedBirds.concat([].concat.apply([], @_pairings))


Convert views to Marionette.

Convert views to Marionette.

--- src/views.coffee
+++ src/views.coffee
@@ -6,6 +6,7 @@
     class SMItemView extends Marionette.ItemView
         templateHelpers:
             t: i18n.t
+            static_path: jade.static_path
         getTemplate: ->
             return jade.get_template @template

@@ -141,14 +142,16 @@
         render: =>
             @el.innerHTML = jade.template 'title-view', lang: p13n.get_language()

-    class LandingTitleView extends Backbone.View
+    class LandingTitleView extends SMItemView
+        template: 'landing-title-view'
         id: 'title'
         className: 'landing-title-control'
         initialize: ->
             @listenTo(app.vent, 'title-view:hide', @hideTitleView)
             @listenTo(app.vent, 'title-view:show', @unHideTitleView)
-        render: =>
-            @el.innerHTML = jade.template 'landing-title-view', isHidden: @isHidden, lang: p13n.get_language()
+        serializeData: ->
+            isHidden: @isHidden
+            lang: p13n.get_language()
         hideTitleView: ->
             $('body').removeClass 'landing'
             @isHidden = true
@@ -158,20 +161,22 @@
             @isHidden = false
             @render()

-    class TitleBarView extends Backbone.View
+    class TitleBarView extends SMItemView
+        template: 'embedded-title-bar'
         events:
             'click a': 'preventDefault'
             'click .show-button': 'toggleShow'
             'click .panel-heading': 'collapseCategoryMenu'

         initialize: ->
-            @listenTo(app.vent, 'administration-divisions-fetched', @render)
+            @listenTo(app.vent, 'administration-divisions-fetched', @receiveData)
             @listenTo(app.vent, 'details_view:show', @hide)
             @listenTo(app.vent, 'details_view:hide', @show)

-        render: (divisionNamePartials)->
-            @el.innerHTML = jade.template 'embedded-title-bar', 'titleText': divisionNamePartials
-
+        receiveData: (divisionNamePartials) ->
+            @divisionNamePartials = divisionNamePartials
+        serializeData:
+            titleText: @divisionNamePartials
         show: ->
             @delegateEvents
             @$el.removeClass 'hide'
@@ -344,7 +349,7 @@
                     view = new DetailsView
                         collection: @selected_units
                 else
-                    @contents.reset()
+                    @contents.close()

             if view?
                 # todo: animations


Make values set name

Make values set name

--- src/world.coffee
+++ src/world.coffee
@@ -87,6 +87,7 @@
     assert _.isString(value), "_from_value requires string"
     world = @_spawn_world "#{value}"
     world.put("value", value)
+    world.put("name", value)
     world

   _from_dict: (dict) ->


added refreshAll widgets method to ImpacWidgetsSvc

--- src/services/widgets/widgets.svc.coffee
+++ src/services/widgets/widgets.svc.coffee
@@ -108,7 +108,7 @@
             if !_.isEmpty(currentDhb.widgets)
               for widget in currentDhb.widgets
                 promises.push _self.update(widget, {metadata: metadata}).then(
-                  (updatedWidget) ->
+                  (updatedWidget) ->
                     updatedWidget.isLoading=true
                     _self.show(updatedWidget, true).then( (renderedWidget)-> renderedWidget.isLoading=false )
                 )
@@ -121,6 +121,22 @@
             $log.error "ImpacWidgetsSvc - currentDhb.widgets is null", currentDhb
             return $q.reject(null)

+    @refreshAll = ->
+      ImpacDashboardsSvc.load().then(
+        (config)->
+          widgets = config.currentDashboard.widgets
+          _.forEach(widgets, (w) ->
+            w.isLoading ||= true
+            _self.show(w).then(
+              (updatedWidget) ->
+                w.isLoading = false
+              (errorResponse) ->
+                w.isLoading = false
+                # TODO: better error management
+                $log.error(errorResponse.data.error) if errorResponse.data? && errorResponse.data.error
+            )
+          )
+      )

     #====================================
     # CRUD methods


menu improvements

menu improvements

--- src/smc-webapp/jupyter/top-menubar.cjsx
+++ src/smc-webapp/jupyter/top-menubar.cjsx
@@ -40,6 +40,7 @@
             cur_id              : rtypes.string
             trust               : rtypes.bool
             view_mode           : rtypes.string
+            toolbar             : rtypes.bool

     shouldComponentUpdate: (next) ->
         return next.has_unsaved_changes != @props.has_unsaved_changes or \
@@ -49,6 +50,7 @@
             next.cur_id != @props.cur_id or \
             next.cells != @props.cells or \
             next.trust != @props.trust or \
+            next.toolbar != @props.toolbar or \
             next.view_mode != @props.view_mode

     propTypes :
@@ -100,10 +102,12 @@
             json   : '>view notebook json'
         shownb[@props.view_mode] = {name:shownb[@props.view_mode], style:SELECTED_STYLE}

+        toolbar = {name:'toggle toolbar', display:if @props.toolbar then 'Hide Toolbar' else 'Show Toolbar'}
+
         @render_menu
             heading : 'View'
             names : \
-                ['toggle header', 'toggle toolbar', 'toggle all line numbers', '', \
+                ['toggle header', toolbar, 'toggle all line numbers', '', \
                  '<Cell Toolbar...', '>cell toolbar none', '>cell toolbar metadata', '>cell toolbar slideshow', '>cell toolbar attachments', '>cell toolbar tags', '', \
                  'zoom in', 'zoom out', '', \
                  "<Show Notebook as...", shownb.normal, shownb.raw, shownb.json]


empty buttons removed on codeshareview

empty buttons removed on codeshareview

--- client/app/Applications/Activity.kdapplication/views/codesharetabpaneview.coffee
+++ client/app/Applications/Activity.kdapplication/views/codesharetabpaneview.coffee
@@ -356,10 +356,11 @@
     @openAllButton.$().twipsy twOptions("Open All")

   pistachio:->
+    #
{{> @runButton}}{{> @saveButton}}{{> @openButton}}{{> @openAllButton}}{{> @copyButton}}

     """

       {pre{> @codeView}}
-
{{> @runButton}}{{> @saveButton}}{{> @openButton}}{{> @openAllButton}}{{> @copyButton}}

+
{{> @saveButton}}{{> @openButton}}{{> @copyButton}}

     {{> @syntaxMode}}
     """


Fixed bold username issue

Fixed bold username issue

--- client/app/Applications/Members.kdapplication/ContentDisplays/contactlink.coffee
+++ client/app/Applications/Members.kdapplication/ContentDisplays/contactlink.coffee
@@ -14,7 +14,7 @@

   pistachio:->
     name = KD.utils.getFullnameFromAccount @getData(), yes
-    "Contact#{name}"
+    "Contact #{name}"

   click:(event)->


whitespace cleanup

whitespace cleanup

--- src/api.coffee
+++ src/api.coffee
@@ -67,22 +67,22 @@
 start = ->
   apiHelper ExerciseActions, ExerciseActions.load, ExerciseActions.loaded, 'GET', (id) ->
     url: "/api/exercises/#{id}@draft"
-
+
   apiHelper ExerciseActions, ExerciseActions.save, ExerciseActions.saved, 'PUT', (id) ->
-
+
     # backend expects the changed props and the entire exercise for some reason
     obj = ExerciseStore.getChanged(id)
     obj.exercise = ExerciseStore.get(id)

     exerciseId = if id.indexOf("@") is -1 then id else id.split("@")[0]
-
+
     url:"/api/exercises/#{exerciseId}@draft"
     httpMethod: 'PUT'
     payload: obj

   apiHelper ExerciseActions, ExerciseActions.publish, ExerciseActions.saved, 'PUT', (id) ->
-
+
     obj = ExerciseStore.get(id)
     uid = ExerciseStore.getId(id)


Corrected error message assembly.

Corrected error message assembly.

--- src/api.coffee
+++ src/api.coffee
@@ -106,7 +106,7 @@
                   logger.info "Push WebSocket replaced for namespace '#{@namespace}' topic #{jsonify(@attributes)}"
                   @emit 'reconnect'
                 .catch (error) =>
-                  logger.error "Error replacing push WebSocket for namespace '#{@namespace}' topic #{jsonify(@attributes)} : ${error}\n${error.stack}"
+                  logger.error "Error replacing push WebSocket for namespace '#{@namespace}' topic #{jsonify(@attributes)} : #{error}\n#{error.stack}"
                   @emit 'error', error

               logger.info "Connection closed. Reconnecting in 5 seconds."


Proper minification safe declaration of ngMaterial config

--- src/app.coffee
+++ src/app.coffee
@@ -11,8 +11,11 @@
 app.constant 'Modernizr', Modernizr

 # Configure ngMaterial
-app.config ($mdThemingProvider) ->
+app.config [
+  '$mdThemingProvider'
+  ($mdThemingProvider) ->
   $mdThemingProvider.theme 'default'
+]

 # Create console.log for incompatible browsers
 window.console = window.console or {}


set style as a global attribute

set style as a global attribute

--- lib/completions-jsx.coffee
+++ lib/completions-jsx.coffee
@@ -76,6 +76,7 @@
 	{ name: 'dangerouslySetInnerHTML'}
 	{ name: 'data-'}
 	{ name: 'aria-'}
+	{ name: 'style'}
 ]

 htmlElements: [


fix error

fix error

--- jqTabs.coffee
+++ jqTabs.coffee
@@ -36,7 +36,7 @@
 		#and show only the first
 		$(@$tabContent[0]).removeClass @settings.hiddenClass

-		$('ul.tab-headers', $tabsContainer).on 'click', 'li', (e) =>
+		$('ul.tab-headers', @$tabsContainer).on 'click', 'li', (e) =>
 			e.preventDefault()

 			if @settings.tabsClickable


Revert "Apparently, this is not needed and just double logs?"

This seems to be an issue, where it logs when running
in development, but not when install as a package?

Leaving double log for now.

This reverts commit 45b503d2edb69f73c1cea5d6cae619d024c5a8c1.

--- src/cli.coffee
+++ src/cli.coffee
@@ -21,6 +21,8 @@
   cmd += " -a " + o.algorithm if o.algorithm

   res = shell.exec cmd
+
+  process.stdout.write res.output

   if res.code != 0
     log.error "synt-rb script failed!"


made js-yaml an optional dependency

made js-yaml an optional dependency

--- src/cli.coffee
+++ src/cli.coffee
@@ -1,5 +1,4 @@
 fs = require 'fs'
-#yaml = require 'js-yaml'

 module.exports.CLI = class CLI
    constructor : (argv) ->
@@ -22,7 +21,7 @@
                      console.dir e
                      @file = []
                when 'yaml','yml'
-                  @file = yaml.load file
+                  @file = require('js-yaml').yaml.load file

       stubOptionIndex = process.argv.indexOf('--stub') + 1
       @ports.stub = parseInt(process.argv[stubOptionIndex]) ? @ports.stub if stubOptionIndex


Fix cli help messages

Fix cli help messages

--- src/cli.coffee
+++ src/cli.coffee
@@ -10,8 +10,8 @@
 { check } = require './equality-checker'
 { compile } = require './compiler'
 { time } = require './timer'
-{ profile, addProfilingOption, OPERF_EVENTS_REGEX, OPERF_EVENTS } = require './profiler'
-{ SECOND_ALIAS_FOR_COMMANDS, PROFILING_OUTPUT_FOLDER } = require './constants'
+{ profile, addProfilingOption, OPERF_EVENTS_REGEX } = require './profiler'
+{ SECOND_ALIAS_FOR_COMMANDS, PROFILING_OUTPUT_FOLDER, OPERF_EVENTS } = require './constants'
 Program = require './program'

 ######### - Argument checks and preprocess - ##########
@@ -61,7 +61,7 @@
     .command 'equal <original-program> [others...]'
     .alias 'e'
     .description 'Check equality of stdout output to verify correctness'
-    .option '-a, --forward-args <[program-specification:]arguments-string>',
+    .option '-a, --forward-args [program-specification:]<arguments-string>',
         "Forward arguments to the programs", Program.addArguments
     .option '-A, --all',
         "Check equality for all programs. Otherwise check only for the program with latest modification time"
@@ -122,20 +122,19 @@
     .option '-A, --all',
         "Profile all programs. Otherwise profile only the program with latest modification time"
     .option '-e, --event [event]',
-        "Event which is going to be profiled. One of #{(key for key of OPERF_EVENTS).toString()}",
+        "Event which is going to be profiled. One of: #{(key for key of OPERF_EVENTS).join(', ')}",
         OPERF_EVENTS_REGEX,
         'cycles'
     .option '-c, --counter [counter]', "Specify event counter value"
     .option '-S, --assembly',
         "Annotate assembly code instead of C code"
     .option '-F, --save-file [filename]',
-        "Save the profiling output to a file inside the folder #{PROFILING_OUTPUT_FOLDER}.
-         If filename is not specified the filename will be of the form <program-executable>_<timestamp>.(ann|gprof).txt"
+        "Save output to file inside #{PROFILING_OUTPUT_FOLDER} folder"
     .option '-g, --gprof', "Profile with gprof"
     .option '-n, --no-clean', "Do not clean profiler's intermediate files"
     .option '-f, --forward-options [program-specification:]<options>',
         "Forward options to the profiler (operf or gprof)", addProfilingOption
-    .option '-a, --forward-args <[program-specification:]arguments-string>',
+    .option '-a, --forward-args [program-specification:]<arguments-string>',
         "Forward arguments to the programs", Program.addArguments
     .option '-i, --input-file [program-specification:]<file>',
         "Specify file that will serve as input for the execution of the programs", Program.addInputFile


Updated tag_autocomplete to use $timeout.

--- src/directives/tag_autocomplete.coffee
+++ src/directives/tag_autocomplete.coffee
@@ -30,8 +30,9 @@

 angular.module("Mac").directive "macTagAutocomplete", [
   "$parse",
+  "$timeout"
   "keys",
-  ($parse, keys) ->
+  ($parse, $timeout, keys) ->
     restrict:    "E"
     templateUrl: "template/tag_autocomplete.html"
     replace:     true
@@ -138,7 +139,7 @@
             item = $scope.onEnter {item}

           $scope.selected.push item if item?
-          setTimeout ->
+          $timeout ->
             $scope.$apply -> $scope.textInput = ""
           , 0


Add scoring history vis to DL model output.

--- src/ext/components/model-output.coffee
+++ src/ext/components/model-output.coffee
@@ -68,6 +68,42 @@
             g.limit 25
           )

+      if table = _.inspect 'output - Scoring History', _model
+        if table.schema['validation_MSE']
+          renderPlot 'Scoring History', _.plot (g) ->
+            g(
+              g.path(
+                g.position 'epochs', 'training_MSE'
+                g.strokeColor g.value '#1f77b4'
+              )
+              g.path(
+                g.position 'epochs', 'validation_MSE'
+                g.strokeColor g.value '#ff7f0e'
+              )
+              g.point(
+                g.position 'epochs', 'training_MSE'
+                g.strokeColor g.value '#1f77b4'
+              )
+              g.point(
+                g.position 'epochs', 'validation_MSE'
+                g.strokeColor g.value '#ff7f0e'
+              )
+              g.from table
+            )
+        else
+          renderPlot 'Scoring History', _.plot (g) ->
+            g(
+              g.path(
+                g.position 'epochs', 'training_MSE'
+                g.strokeColor g.value '#1f77b4'
+              )
+              g.point(
+                g.position 'epochs', 'training_MSE'
+                g.strokeColor g.value '#1f77b4'
+              )
+              g.from table
+            )
+
     when 'gbm', 'drf'
       if table = _.inspect 'output - Scoring History', _model
         if table.schema['validation_MSE']


Updates the command descriptions

Updates the command descriptions

--- src/ghe.coffee
+++ src/ghe.coffee
@@ -10,10 +10,16 @@
 #
 # Commands:
 #   hubot ghe license - returns license information
-#   hubot ghe stats issues - returns issue information
-#   hubot ghe stats hooks - returns hook information
-#   hubot ghe stats milestones - returns milestone information
-#   hubot ghe stats issues - returns user information
+#   hubot ghe stats issues - returns the number of open and closed issues
+#   hubot ghe stats hooks - returns the number of active and inactive hooks
+#   hubot ghe stats milestones - returns the number of open and closed milestones
+#   hubot ghe stats orgs - returns the number of organizations, teams, team members, and disabled organizations
+#   hubot ghe stats comments - returns the number of comments on issues, pull requests, commits, and gists
+#   hubot ghe stats pages - returns the number of GitHub Pages sites
+#   hubot ghe stats users - returns the number of suspended and admin users
+#   hubot ghe stats gists - returns the number of private and public gists
+#   hubot ghe stats pulls - returns the number of merged, mergeable, and unmergeable pull requests
+#   hubot ghe stats repos - returns the number of organization-owned repositories, root repositories, forks, pushed commits, and wikis
 #
 # Authors:
 #   pnsk, mgriffin


Handle submodules in getDiffStats/isSubmodule

--- src/git.coffee
+++ src/git.coffee
@@ -212,14 +212,18 @@
   # Returns an {Object} with the following keys:
   #   :added - The {Number} of added lines.
   #   :deleted - The {Number} of deleted lines.
-  getDiffStats: (path) -> @getRepo().getDiffStats(@relativize(path))
+  getDiffStats: (path) ->
+    repo = @getRepo(path)
+    repo.getDiffStats(repo.relativize(path))

   # Public: Is the given path a submodule in the repository?
   #
   # path - The {String} path to check.
   #
   # Returns a {Boolean}.
-  isSubmodule: (path) -> @getRepo().isSubmodule(@relativize(path))
+  isSubmodule: (path) ->
+    repo = @getRepo(path)
+    repo.isSubmodule(repo.relativize(path))

   # Public: Get the status of a directory in the repository's working directory.
   #


bring in customizable host config from https://github.com/mmavko/hem/commit/0f382b1ca66e66ff379f9bd6815d754b0d0bf222

--- src/hem.coffee
+++ src/hem.coffee
@@ -38,6 +38,7 @@
     paths:        ['./app']
     dependencies: []
     port:         process.env.PORT or argv.port or 9294
+    host:         argv.host or 'localhost'
     cssPath:      '/application.css'
     jsPath:       '/application.js'

@@ -70,7 +71,7 @@
     if fs.existsSync(@options.public)
       strata.use(strata.file, @options.public, ['index.html', 'index.htm'])

-    strata.run(port: @options.port)
+    strata.run(port: @options.port, host: @options.host)

   build: ->
     source = @hemPackage().compile(not argv.debug)


bringing over new proxy redirect logic for handling changes in port number. Using 'patchRedirect' will keep all redirect responses on the same port as the hem server.

--- src/hem.coffee
+++ src/hem.coffee
@@ -56,8 +56,8 @@

   options:
     server:
-      port: argv.port or 9294
-      host: argv.host or 'localhost'
+      port: 9294
+      host: 'localhost'

   constructor: (options = {}) ->
     @options[key] = value for key, value of options
@@ -69,6 +69,9 @@
      process.exit(1)
     # setup packages from options/slug
     @packages = (@createPackage(name, config) for name, config of @options.packages)
+    # allow overrides
+    @options.server.port = argv.port if argv.port
+    @options.server.host or= ""

   server: ->
     # create app
@@ -99,10 +102,13 @@
         else
           console.log "ERROR: The folder #{value} does not exist."
           process.exit(1)
-      else
+      else if value.host
         # setup proxy
         console.log "Proxy requests from #{url} to #{value.host}" if argv.v
-        app.use(url, @createRoutingProxy(value)) if value.host
+        app.use(url, @createRoutingProxy(value))
+        @patchServerResponseForRedirects(@options.server.port, value) if value.patchRedirect
+      else
+        throw new Error("Invalid route configuration for #{url}")

     # start server
     http.createServer(app).listen(@options.server.port, @options.server.host)
@@ -175,6 +181,18 @@
     return (req, res, next) ->
       req.url = "#{options.hostPath}#{req.url}"
       proxy.proxyRequest(req, res, options)
+
+  patchServerResponseForRedirects: (port, config) ->
+      writeHead = http.ServerResponse.prototype.writeHead
+      http.ServerResponse.prototype.writeHead = ->
+        @.emit('header') if (!@._emittedHeader)
+        @._emittedHeader = true
+        [ status, head ] = arguments
+        if status in [301,302]
+          oldLocation = "://#{config.host}:#{config.port}"
+          newLocation = "://#{config.host}:#{port}"
+          head.location = head.location.replace(oldLocation,newLocation)
+        return writeHead.apply(this, arguments)

   startTestacular: (targets = [], singleRun = true) ->
     # use custom testacular config file provided by user
@@ -218,3 +236,4 @@
     bestMatch.url

 module.exports = Hem
+


fixed invitations list not showing up

fixed invitations list not showing up

--- client/app/Applications/Groups.kdapplication/Views/groupsinvitationlistitemview.coffee
+++ client/app/Applications/Groups.kdapplication/Views/groupsinvitationlistitemview.coffee
@@ -13,7 +13,7 @@

     if @getData().koding
       @recipient = @getData().koding.username
-      KD.remote.cacheable recipient, (err,account)=>
+      KD.remote.cacheable @recipient, (err,account)=>
         @avatar.setData account
         @avatar.render()
     else


Set css all: initial for sku reviews root elements

--- src/plugins/partner_sku_reviews.coffee
+++ src/plugins/partner_sku_reviews.coffee
@@ -561,7 +561,7 @@
     /* INLINE */

     ##{flavor}-product-reviews-inline.sa-reviews-inline-root {
-      all: unset;
+      all: initial;
       display: inline-flex;
       display: -ms-inline-flexbox;
       flex-direction: column;
@@ -799,7 +799,7 @@
     /* EXTENDED */

     ##{flavor}-product-reviews-extended.sa-reviews-extended-root {
-      all: unset;
+      all: initial;
       display: block;
       border: 1px solid #e8e8e8;
       border-radius: 2px;


cell padding

cell padding

--- layout.coffee
+++ layout.coffee
@@ -1,3 +1,11 @@
+pad = {
+    top: 20
+    bottom: 5
+    left: 5
+    right: 5
+}
+
+
 walk = (state, callback, parent=null) ->
     callback(state, parent)
     for child in state.children or []
@@ -68,10 +76,10 @@
     force.on 'tick', ->
         for node in nodes
             if node.children.length > 0
-                xMin = d3.min(node.children, (d) -> d.x - d.width / 2) - 5
-                xMax = d3.max(node.children, (d) -> d.x + d.width / 2) + 5
-                yMin = d3.min(node.children, (d) -> d.y - d.height / 2) - 5
-                yMax = d3.max(node.children, (d) -> d.y + d.height / 2) + 5
+                xMin = d3.min(node.children, (d) -> d.x - d.width / 2) - pad.left
+                xMax = d3.max(node.children, (d) -> d.x + d.width / 2) + pad.right
+                yMin = d3.min(node.children, (d) -> d.y - d.height / 2) - pad.top
+                yMax = d3.max(node.children, (d) -> d.y + d.height / 2) + pad.bottom
                 node.width = xMax - xMin
                 node.height = yMax - yMin
                 dx = xMin + node.width / 2 - node.x
@@ -94,6 +102,9 @@
             .attr('y', (node) -> - node.height / 2)
             .attr('width', (node) -> node.width)
             .attr('height', (node) -> node.height)
+
+        svg.selectAll('.cell text')
+            .attr('y', (node) -> pad.top - node.height / 2 - 5)

         link.attr('x1', (d) -> d.source.x)
             .attr('y1', (d) -> d.source.y)


fix tests

fix tests

--- src/scripts/configs/embeddables.coffee
+++ src/scripts/configs/embeddables.coffee
@@ -22,7 +22,7 @@
         matchType: 'a'
         embeddableType: 'exercise'
         apiUrl: () ->
-          "#{exercises}/api/exercises?q=tag:\"#{@itemCode}\""
+          "#{exercises}/api/exercises?q=tag:\"#{encodeURIComponent(@itemCode)}\""

         # # Adds flexibility for if data needs transformation post API call
         # # For now, not needed for when using actual API.  Comment in if using local stub.
@@ -41,7 +41,7 @@
         matchType: 'a'
         embeddableType: 'exercise'
         apiUrl: () ->
-          "#{exercises}/api/exercises?q=nickname:\"#{@itemCode}\""
+          "#{exercises}/api/exercises?q=nickname:\"#{encodeURIComponent(@itemCode)}\""

         # # Adds flexibility for if data needs transformation post API call
         # # For now, not needed for when using actual API.  Comment in if using local stub.


remove debug line

remove debug line

--- src/scripts/modules/media/media.coffee
+++ src/scripts/modules/media/media.coffee
@@ -30,8 +30,6 @@

       @uuid = options.uuid
       @model = new Content({id: @uuid, page: options.page})
-
-      window.DEBUG_MODEL = @model

       @listenTo(@model, 'change:googleAnalytics', @trackAnalytics)
       @listenTo(@model, 'change:title', @updateTitle)


fix aip param

fix aip param

--- lib/ga.coffee
+++ lib/ga.coffee
@@ -52,8 +52,8 @@
     # https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters
     {
       v: 1
-      aip: true
+      aip: 1
       ds: "app"
       tid: "UA-53539506-9"
-      ua: "Atom v#{atom.appVersion} #{atom.getReleaseChannel()}"
+      ua: "Atom v#{atom.appVersion} (#{atom.getReleaseChannel()})"
     }


Initialise session value

Initialise session value

--- phpsession.coffee
+++ phpsession.coffee
@@ -26,6 +26,7 @@
 		@mem = new Memcached("#{host}:#{port}")

 	get: ({id}) ->
+		session = ''
 		@mem.get "sessions/#{id}", (err, raw) =>
 			if err? or not raw? or raw.length is 0
 				return


show overlays on halloactivated event, not halloselected

--- plugins/overlay.coffee
+++ plugins/overlay.coffee
@@ -41,7 +41,7 @@

             if not @options.bound
                 @options.bound = true
-                widget.options.editable.element.bind "halloselected", (event, data) ->
+                widget.options.editable.element.bind "halloactivated", (event, data) ->
                     widget.options.currentEditable = jQuery(event.target)
                     widget.showOverlay()


don't include "undefined" in img classes (#48)

* don't include "undefined" in img classes.

* don't override 'react-gravatar' class.

--- src/index.cjsx
+++ src/index.cjsx
@@ -56,22 +56,26 @@
       # this is not NodeJS
       modernBrowser = 'srcset' of document.createElement('img')

+    className = 'react-gravatar'
+    if @props.className
+      className = "#{className} #{@props.className}"
+
     if not modernBrowser and isRetina()
       <img
         style={@props.style}
-        className={"react-gravatar " + @props.className}
         src={retinaSrc}
         height={@props.size}
         width={@props.size}
         {...@props}
+        className={className}
       />
     else
       <img
         style={@props.style}
-        className={"react-gravatar " + @props.className}
         src={src}
         srcSet={retinaSrc + " 2x"}
         height={@props.size}
         width={@props.size}
         {...@props}
+        className={className}
       />


Send MachineStateFailed event to DataDog

--- client/ide/views/modals/machinestatemodal.coffee
+++ client/ide/views/modals/machinestatemodal.coffee
@@ -228,6 +228,8 @@

     return  unless @hasError

+    KD.utils.sendDataDogEvent "MachineStateFailed"
+
     @errorMessage = new KDCustomHTMLView
       cssClass    : 'error-message'
       partial     : """


[fix] Only watch changes when the 'two-way' option is available'

--- cli.coffee
+++ cli.coffee
@@ -126,7 +126,7 @@

         launchDaemons = ->
             # Watch local changes
-            if args.toRemote or (not args.toRemote and not args.fromRemote)
+            if args['two-way']
                 filesystem.watchChanges continuous, fromNow

             # Replicate databases


Add missing preventDefault

Add missing preventDefault

--- resources/assets/coffee/react/profile-page/contents-tab.coffee
+++ resources/assets/coffee/react/profile-page/contents-tab.coffee
@@ -19,7 +19,8 @@
 el = React.createElement

 class ProfilePage.ContentsTab extends React.Component
-  onClick: =>
+  onClick: (e) =>
+    e.preventDefault()
     $.publish 'profilePageMode:change', @props.mode

   render: =>


blocking doesn't prevent reporting

blocking doesn't prevent reporting

--- resources/assets/coffee/react/profile-page/header-extra.coffee
+++ resources/assets/coffee/react/profile-page/header-extra.coffee
@@ -126,15 +126,17 @@
                       devices: rowValue playsWith
             @renderPostCount()

-          if !currentUser.id? || currentUser.id != @props.user.id && !isBlocked
+          if !currentUser.id? || currentUser.id != @props.user.id
             div className: "#{bn}__rows #{bn}__rows--actions",
-              a
-                className: 'user-action-button user-action-button--message user-action-button--right-margin'
-                href: laroute.route 'messages.users.show', user: @props.user.id
-                title: osu.trans('users.card.send_message')
-                i className: 'fas fa-envelope'
-
-              el BlockButton, user_id: @props.user.id
+              if !isBlocked
+                span null,
+                  a
+                    className: 'user-action-button user-action-button--message user-action-button--right-margin'
+                    href: laroute.route 'messages.users.show', user: @props.user.id
+                    title: osu.trans('users.card.send_message')
+                    i className: 'fas fa-envelope'
+
+                  el BlockButton, user_id: @props.user.id

               el _exported.ReportForm, user: @props.user


Clean up some dead code.

Clean up some dead code.

--- src/DreamSync.coffee
+++ src/DreamSync.coffee
@@ -87,35 +87,3 @@
         throw new Error "Unable to save doc #{JSON.stringify doc}"

       Promise.all(allPromises).then onSuccess, onError
-
-  saveDocWithSnapshot: (doc, snapshot) ->
-    if doc.id?
-      new Promise (resolve, reject) =>
-        @getDoc(doc.id).then (existingDoc) =>
-          if existingDoc.lastModifiedTime > doc.lastModifiedTime
-            # TODO handle this by re-rendering etc
-            alert "Your document is out of sync! Please refresh."
-          else
-            persistDocAndSnapshot(@db, doc, snapshot).then resolve, reject
-    else
-      doc.id = DreamSync.getRandomSha()
-      persistDocAndSnapshot @db, doc, snapshot
-
-persistDocAndSnapshot = (db, doc, snapshot) ->
-  new Promise (resolve, reject) ->
-    snapshot.id    ?= DreamSync.getRandomSha()
-
-    currentTime = new Date().getTime()
-
-    doc.creationTime           ||= currentTime
-    doc.lastModifiedTime         = currentTime
-    snapshot.creationTime      ||= doc.creationTime
-    snapshot.lastModifiedTime    = doc.lastModifiedTime
-
-    for chapter in doc.chapters
-      chapter.id ||= DreamSync.getRandomSha()
-
-    Promise.all([
-      db.docs.update(doc)
-      db.snapshots.update(snapshot)
-    ]).then (-> resolve doc), reject


make home-dir-relative, add jn widgets and symbol ref

--- projects.wme03370.cson
+++ projects.wme03370.cson
@@ -3,7 +3,7 @@
     title: "LF Word Count"
     group: "Atom"
     paths: [
-      "/home/aflanagan/Devel/atom/lloyd-flanagan-word-count"
+      "~/Devel/atom/lloyd-flanagan-word-count"
     ]
     icon: "atom-icon"
   }
@@ -11,7 +11,7 @@
     title: "Atom Configuration"
     group: "Atom"
     paths: [
-      "/home/aflanagan/Devel/atom/atom_configuration"
+      "~/Devel/atom/atom_configuration"
     ]
     icon: "atom-icon"
   }
@@ -19,7 +19,7 @@
     title: "Atom"
     group: "Atom"
     paths: [
-      "/home/aflanagan/github/atom"
+      "~/github/atom"
     ]
     icon: "atom-icon"
   }
@@ -27,7 +27,7 @@
     title: "Svgmaps"
     group: "BHMedia"
     paths: [
-      "/home/aflanagan/Devel/svgmaps"
+      "~/Devel/svgmaps"
     ]
     icon: "icon-tools"
   }
@@ -35,7 +35,7 @@
     title: "Atom Language Utl"
     group: "Atom"
     paths: [
-      "/home/aflanagan/Devel/atom/atom-language-utl"
+      "~/Devel/atom/atom-language-utl"
     ]
     icon: "atom-icon"
   }
@@ -43,7 +43,7 @@
     title: "Utl Indexer"
     group: "BLOX"
     paths: [
-      "/home/aflanagan/Devel/utl_indexer"
+      "~/Devel/utl_indexer"
     ]
     icon: "fa fa-server"
   }
@@ -51,7 +51,7 @@
     title: "Townnews Reader"
     group: "BLOX"
     paths: [
-      "/home/aflanagan/Devel/townnews_reader"
+      "~/Devel/townnews_reader"
     ]
     icon: "fa fa-server"
   }
@@ -59,7 +59,7 @@
     title: "Shared Libraries"
     group: "BH Media"
     paths: [
-      "/home/aflanagan/Devel/shared_libraries"
+      "~/Devel/shared_libraries"
     ]
     icon: "fa fa-server"
   }
@@ -67,14 +67,14 @@
     title: "Utl Lookup"
     group: "BLOX"
     paths: [
-      "/home/aflanagan/Devel/utl_lookup"
+      "~/Devel/utl_lookup"
     ]
   }
   {
     title: "Bootstrap 3 3 6"
     group: "Frameworks"
     paths: [
-      "/home/aflanagan/src/bootstrap/bootstrap-3.3.6"
+      "~/src/bootstrap/bootstrap-3.3.6"
     ]
     icon: "fa fa-bold"
   }
@@ -82,7 +82,7 @@
     title: "Gmail Intfc"
     group: "Mail"
     paths: [
-      "/home/aflanagan/Devel/personal/mail-electron-intfc"
+      "~/Devel/personal/mail-electron-intfc"
     ]
     icon: "icon-mail-read"
   }
@@ -90,7 +90,7 @@
     title: "Hacker Web"
     group: "HackRVA"
     paths: [
-      "/home/aflanagan/Devel/personal/hackrva/hacker-web"
+      "~/Devel/personal/hackrva/hacker-web"
     ]
     icon: "ion-social-html5"
   }
@@ -98,7 +98,7 @@
     title: "Gmail Web Svcs"
     group: "Mail"
     paths: [
-      "/home/aflanagan/Devel/personal/mail-web-srvcs"
+      "~/Devel/personal/mail-web-srvcs"
     ]
     icon: "icon-mail-read"
   }
@@ -106,8 +106,26 @@
     title: "Badge2017"
     group: "HackRVA"
     paths: [
-      "/home/aflanagan/Devel/personal/hackrva/Harmony-Badge-2017/firmware"
+      "~/Devel/personal/hackrva/Harmony-Badge-2017/firmware"
     ]
     icon: "icon-circuit-board"
   }
+  {
+    title: "The Jobs Network Widgets"
+    group: "BH Media"
+    paths: [
+      "~/Devel/richmond.com/jobs.thejobnetwork.com"
+    ]
+    icon: "icon-package"
+  }
+  {
+    title: "Javascript Symbol Ref"
+    group: "Atom"
+    paths: [
+      "~/Devel/atom/javascript-symbol-ref"
+      "~/Devel/atom/atom/src"
+    ]
+    icon: "devicon-javascript-plain"
+    devMode: true
+  }
 ]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/distance.coffee
+++ src/distance.coffee
@@ -1,2 +1,2 @@
 Distance = (I={}, self) ->
-  distance:+  distance: (otherObj) ->


UI/autocompleter adding the 'highlight' option

--- ui/autocompleter/src/autocompleter.coffee
+++ ui/autocompleter/src/autocompleter.coffee
@@ -11,6 +11,7 @@
       method:    'get'  # ajax requests default HTTP method
       minLength: 1      # min text length for the autocompleter to start working
       threshold: 200    # the user's typing pause length for the autocompleter to watch
+      highlight: true   # autohighlight matching entries
       cache:     true   # cache search results internally

   #
@@ -100,9 +101,9 @@
     if isArray(content) && highlight isnt undefined
       highlight = String(highlight).toLowerCase()

-      content = core.L(content).map (entry)->
+      content = core.L(content).map (entry)=>

-        if highlight && (index = entry.toLowerCase().indexOf(highlight)) isnt -1
+        if @options.highlight && highlight && (index = entry.toLowerCase().indexOf(highlight)) isnt -1
           entry = entry.substr(0, index) + '<strong>' +
             entry.substr(index, index + highlight.length) + '</strong>' +
             entry.substr(index + highlight.length)


check if user is null #94

check if user is null #94

--- both/methods/shift/shiftSchedule.coffee
+++ both/methods/shift/shiftSchedule.coffee
@@ -226,10 +226,10 @@
 			check { tagId: shift.tagId, userId: userId }, isTagParticipant

 			for team in shift.teams when team._id == teamId
-				for notAcceptedUser in team.declined.concat(team.pending) when notAcceptedUser._id == userId
+				for notAcceptedUser in team.declined.concat(team.pending) when notAcceptedUser? && notAcceptedUser._id == userId
 					throw new Meteor.Error 500, TAPi18n.__('modal.addParticipant.alreadyRequested')

-				for acceptedUser in team.participants when acceptedUser._id == userId
+				for acceptedUser in team.participants when acceptedUser? && acceptedUser._id == userId
 					throw new Meteor.Error 500, TAPi18n.__('modal.addParticipant.alreadyParticipating')
 				break


bugfix: remove thisTeamleader attribute when cancelling a team

--- both/methods/shift/shiftSchedule.coffee
+++ both/methods/shift/shiftSchedule.coffee
@@ -22,6 +22,7 @@
 					for participant in team.participants
 						participant.checked = true
 						participant.informed = false
+						participant.thisTeamleader = false

 						Shifts.update _id: shiftId, 'teams._id': teamId,
 							$pull: 'teams.$.participants': _id: participant._id


modal should be using mixin for modal behaviour

--- lib/components/eui-modal.coffee
+++ lib/components/eui-modal.coffee
@@ -10,107 +10,6 @@
   classNameBindings: ['class']

   class: null
-
-  # Stores the element that had focus before the modal was opened if the user uses the
-  # keyboard to open it
-
-  previousFocus: null
-
-
-  # We don't really want to modal to be focusable, but we do need it to catch all key
-  # presses so we can scope the tab key to only tab the modal contents.
-
-  tabindex: 0
-
-
-  # If the dialog was created programatically we set this to true which renders the
-  # view specified by the user instead of yielding.
-
-  programmatic: false
-
-
-  # Set to true when we want the closing animations to play.
-
-  isClosing: false
-
-
-  # Determines if the modal contents should be rendered into the DOM
-
-  renderModal: false
-
-
-  # Proxy for renderModal. Allows us to animate the modal closing by delaying the setting of
-  # renderModal until the animation is done playing
-
-  open: Ember.computed 'renderModal', (key, value) ->
-    # setter
-    if arguments.length is 2
-
-      # If we are showing the modal we can do it immediately since it won't bugger animations
-      if value
-        @set 'renderModal', value
-
-      # Enure that the modal is currently in the DOM before we hide it via hide method
-      else
-        @hide() if @get 'renderModal'
-
-      value
-
-    # getter
-    else
-      value = @get 'renderModal'
-      value
-
-
-  # Initial setup when modal is shown programatically
-
-  didInsertElement: ->
-    if @get 'programmatic'
-      # Store orignal focus so we can restore it when the modal is closed
-      @set 'previousFocus', $(document.activeElement)
-
-      # Focus on modal so we can catch key events
-      @.$().focus()
-
-      # Add a class to the body element of the page so we can disable page scrolling
-      $('body').addClass('eui-modal-open')
-
-
-  # Initial setup when modal is used as a block component
-
-  didOpenModal: (->
-    if @get 'renderModal'
-      # Focus on modal so we can catch key events
-      @.$().focus()
-
-      # Add a class to the body element of the page so we can disable page scrolling
-      $('body').addClass('eui-modal-open')
-  ).observes 'renderModal'
-
-
-  # Method to call to hide the Modal
-
-  hide: ->
-    # Set isClosing to true so the animation class gets added
-    @set 'isClosing', true
-
-    @animationsDidComplete().then =>
-      @remove()
-
-
-  # Removes the Modal from the DOM
-
-  remove: ->
-    # Restore focus to where it was before the modal was open
-    @get('previousFocus')?.focus()
-
-    # Remove class set on body to disable page scrolling
-    $('body').removeClass('eui-modal-open')
-
-    if @get 'programmatic'
-      @destroy()
-    else
-      @setProperties { isClosing: false, renderModal: false }

   # Actions will not bubble up from the programmatic modal so we need to create
@@ -137,7 +36,7 @@
       @sendAction 'cancel'
       @hide()

-
+
 modal.reopenClass
   # Creates the modal programmatically and inserts it into the DOM


defualt result view height

defualt result view height

--- lib/data-atom-controller.coffee
+++ lib/data-atom-controller.coffee
@@ -26,10 +26,10 @@
       atom.workspaceView.command "data-atom:execute", => @execute()
       atom.workspaceView.command 'data-atom:toggle-results-view', => @toggleView()

-      atom.workspaceView.on 'pane-container:active-pane-item-changed', => @updateResultsView()
+      atom.workspaceView.on 'pane-container:active-pane-item-changed', => @onActivePaneChanged()

    # show the results view for the selected editor
-   updateResultsView: ->
+   onActivePaneChanged: ->
       @mainView.hide()
       @currentViewState = null;
       if atom.workspace.getActiveEditor() && atom.workspace.getActiveEditor().getPath()
@@ -46,7 +46,7 @@
    # Gets or creates the ResultView state for the current editor
    getOrCreateCurrentResultView: ->
       if !@currentViewState && !@viewToEditor[atom.workspace.getActiveEditor().getPath()]
-         @viewToEditor[atom.workspace.getActiveEditor().getPath()] = {view: new DataResultView(), isShowing: false, dataManager: null}
+         @viewToEditor[atom.workspace.getActiveEditor().getPath()] = {view: new DataResultView(), isShowing: false, dataManager: null, height: 200}

       @currentViewState = @viewToEditor[atom.workspace.getActiveEditor().getPath()]

@@ -73,11 +73,12 @@
             break;

    onDisconnect: ->
-      # currentViewState Will have the dataManager
-      @currentViewState.dataManager.destroy()
       # see if other views have it as an active connection
       for key, value of @viewToEditor
          value.dataManager = null if value.dataManager?.getConnectionName() == @currentViewState.dataManager.getConnectionName()
+
+      # currentViewState Will have the dataManager
+      @currentViewState.dataManager.destroy()
       @currentViewState.dataManager = null

    createNewConnection: (thenDo) ->


:bomb: , :gift: make replace operator target flexibile

Existing normal-mode r is  renamed from replace to replaceCharacter

--- lib/operator-transform-string.coffee
+++ lib/operator-transform-string.coffee
@@ -80,20 +80,24 @@
   requireInput: true

   initialize: ->
+    @onDidSetTarget =>
+      @focusInput()
     super
-    if @isMode('normal')
-      @target = 'MoveRightBufferColumn'
-    @focusInput()

   mutateSelection: (selection) ->
     if @target.is('MoveRightBufferColumn')
-      return unless selection.getText().length is @getCount()
-
-    input = @getInput() or "\n"
-    if input is "\n"
+      return selection.getText().length isnt @getCount()
+
+    input = @getInput()
+    if input is ''
+      input = "\n"
       @restorePositions = false
     text = selection.getText().replace(/./g, input)
     selection.insertText(text, autoIndentNewline: true)
+
+class ReplaceCharacter extends Replace
+  @extend()
+  target: "MoveRightBufferColumn"

 # -------------------------
 # DUP meaning with SplitString need consolidate.


Debug error on iPad

Debug error on iPad

--- js/script.coffee
+++ js/script.coffee
@@ -32,7 +32,7 @@
 ), 500

 cb = (r) ->
-	console.log 'cb return'
+	console.log 'cb return: ', r
 	boxes = []
 	divOn = '

'
 	divOff = '

'
@@ -43,13 +43,13 @@
 		dayNumber = new Date(p.date).getDay()
 		diff = Math.floor (today-practice)/ milliseconds
 		if diff < 16 then boxes[diff] = 1
+	console.log 'boxes: ', boxes
 	for i in [14..0] by -1
 		document.querySelector('.practices').innerHTML += if boxes[i] then divOn else divOff
 	return

 # XMLHttpRequest.coffee
 loadJSON = (url) ->
-  console.log 'request'
   req = new XMLHttpRequest()
   req.addEventListener 'readystatechange', ->
     if req.readyState is 4 # ReadyState Complete
@@ -62,7 +62,6 @@
       return
   req.open 'GET', url, true
   req.send()
-  console.log 'req send'
   return

 loadJSON '{{ site.practices_url }}?date=' + new Date().getTime()


Update machinestatemodal.coffee

Update machinestatemodal.coffee

--- client/Main/providers/machinestatemodal.coffee
+++ client/Main/providers/machinestatemodal.coffee
@@ -292,16 +292,16 @@
       cssClass : 'expired-message'
       partial  : if destroyVMs then """

Delete all existing VMs

-
To be able to downgrade your current plan #{plan} to Free
+
To be able to downgrade your current plan #{plan} to the Free
         plan, you need to delete all your existing VMs. This action will
-        destroy all your existing VMs, (including your files) and cannot
-        be undone! Do you want to continue?

+        destroy all your VMs, (including YOUR FILES) and cannot
+        be UNDONE! Are you sure you want to continue?

       """ else """

Plan Expired

Your current plan #{plan} is expired. For accessing
-        your vm you need to upgrade your plan first. Or you can downgrade
-        to Free plan which will destroy all your existing vms and
-        files in it.

+        your VM you need to upgrade your plan first. Or you can downgrade
+        to the Free plan which will destroy all your existing VMs and their
+        files.

       """

     unless destroyVMs


Admin: PermissionSwitch: Code styling.

Admin: PermissionSwitch: Code styling.

--- client/admin/lib/views/permissionswitch.coffee
+++ client/admin/lib/views/permissionswitch.coffee
@@ -1,29 +1,36 @@
 KodingSwitch = require 'app/commonviews/kodingswitch'
-$ = require 'jquery'
+$            = require 'jquery'

 module.exports = class PermissionSwitch extends KodingSwitch

-  constructor: (options={}, data)->
-    super options, data

-  setDomElement:(cssClass)->
-    @domElement = $ "

"
+  setDomElement: (cssClass) ->

-  setOff :->
-    kdSwitch = @domElement.children(".koding-on-off")
+    @domElement = $ """
+

+

+
+

+

+    """

-    kdSwitch.removeClass "on"
-    kdSwitch.addClass "off"

-    super
+  setOff: ->

-  setOn :->
-    kdSwitch = @domElement.children(".koding-on-off")
+    kdSwitch = @domElement.children '.koding-on-off'

-    kdSwitch.removeClass "off"
-    kdSwitch.addClass "on"
+    kdSwitch.removeClass 'on'
+    kdSwitch.addClass    'off'

     super

+  setOn: ->
+
+    kdSwitch = @domElement.children '.koding-on-off'
+
+    kdSwitch.removeClass 'off'
+    kdSwitch.addClass    'on'
+
+    super


Deleted to rename

Deleted to rename

--- src/_DivSugar.coffee
+++ src/_DivSugar.coffee
@@ -1,28 +0,0 @@
-DivSugar =
-  _initialize: ->
-    #
-
-  createScreen: ->
-    div = document.createElement 'div'
-
-    for name, func of @_Screen
-      div[name] = func
-
-    div._initialize()
-
-    # parent = document.getElementById(parentId)
-    # parent.appendChild(div)
-
-    return div
-
-    createSprite: ->
-      div = document.createElement 'div'
-
-      for name, func of @_Sprite
-        div[name] = func
-
-      div._initialize()
-
-      return div
-
-DivSugar._initialize()


update util name and change 'delete stack template' to 'delete'

--- client/app/lib/components/sidebar/index.coffee
+++ client/app/lib/components/sidebar/index.coffee
@@ -104,7 +104,7 @@
         linkController.openOrFocus remoteUrl
       when 'Make Team Default'
         computeController.makeTeamDefault draft.toJS(), yes
-      when 'Delete Stack Template'
+      when 'Delete'
         computeController.deleteStackTemplate draft.toJS(), yes

@@ -128,7 +128,7 @@
     ['Edit', 'Initialize'].forEach (name) => menuItems[name] = { callback }
     menuItems['Clone'] = { callback }  if canCreateStacks() or isAdmin()
     menuItems['Make Team Default'] = { callback } if isAdmin() and draft.get('machines').size
-    menuItems['Delete Stack Template'] = { callback }
+    menuItems['Delete'] = { callback }
     { top } = findDOMNode(@refs["draft-#{id}"]).getBoundingClientRect()

     menuOptions = { cssClass: 'SidebarMenu', x: 36, y: top + 31 }


Environment: Actions: _bindMachineEvents turn off events before turning on for given machines

Since this `_bindMachineEvents` is getting called multiple times we need to turn off all existing listeners for any existing machines

--- client/app/lib/flux/environment/actions.coffee
+++ client/app/lib/flux/environment/actions.coffee
@@ -29,15 +29,19 @@

       _eventsCache.machine[id] = yes

-      computeController.on "public-#{id}", (event) ->
+      publicHandler = (event) ->
         reactor.dispatch actions.MACHINE_UPDATED, { id, event }
-
-      computeController.on "revive-#{id}", (newMachine) ->
+      computeController.off "public-#{id}", publicHandler
+      computeController.on  "public-#{id}", publicHandler
+
+      reviveHandler = (newMachine) ->
         return loadMachines()  unless newMachine
         reactor.dispatch actions.MACHINE_UPDATED, { id, machine: newMachine }
+      computeController.off "revive-#{id}", reviveHandler
+      computeController.on  "revive-#{id}", reviveHandler

       if stack = computeController.findStackFromMachineId id
-        computeController.on "apply-#{stack._id}", (event) ->
+        applyHandler = (event) ->
           reactor.dispatch actions.MACHINE_UPDATED, { id, event }


Fix: #196

Fix: #196

 - Use inline worker as dataURL

--- files.coffee
+++ files.coffee
@@ -534,6 +534,13 @@
         unsetTokenCookie()

       check @onbeforeunloadMessage, Match.OneOf String, Function
+
+      if window?.Worker
+        @_supportWebWorker = true
+        @_webWorkerUrl     = 'data:application/javascript,InVzZSBzdHJpY3QiO3NlbGYub25tZXNzYWdlPWZ1bmN0aW9uKGEpe3ZhciBiO3NlbGYuRmlsZVJlYWRlcj8oYj1uZXcgRmlsZVJlYWRlcixiLm9ubG9hZGVuZD1mdW5jdGlvbihjKXtwb3N0TWVzc2FnZSh7YmluOihiLnJlc3VsdHx8Yy5zcmNFbGVtZW50fHxjLnRhcmdldCkuc3BsaXQoIiwiKVsxXSxjaHVua0lkOmEuZGF0YS5jYyxzOmEuZGF0YS5zfSl9LGIub25lcnJvcj1mdW5jdGlvbihhKXt0aHJvdyhhLnRhcmdldHx8YS5zcmNFbGVtZW50KS5lcnJvcn0sYi5yZWFkQXNEYXRhVVJMKGEuZGF0YS5mLnNsaWNlKGEuZGF0YS5jcyooYS5kYXRhLmNjLTEpLGEuZGF0YS5jcyphLmRhdGEuY2MpKSk6c2VsZi5GaWxlUmVhZGVyU3luYz8oYj1uZXcgRmlsZVJlYWRlclN5bmMscG9zdE1lc3NhZ2Uoe2JpbjpiLnJlYWRBc0RhdGFVUkwoYS5kYXRhLmYuc2xpY2UoYS5kYXRhLmNzKihhLmRhdGEuY2MtMSksYS5kYXRhLmNzKmEuZGF0YS5jYykpLnNwbGl0KCIsIilbMV0sY2h1bmtJZDphLmRhdGEuY2N9KSk6cG9zdE1lc3NhZ2Uoe2JpbjpudWxsLGNodW5rSWQ6YS5kYXRhLmNjLGVycm9yOiJGaWxlIEFQSSBpcyBub3Qgc3VwcG9ydGVkIGluIFdlYldvcmtlciEifSl9Ow=='
+      else
+        @_supportWebWorker = false
+
     else
       @strict            ?= true
       @throttle          ?= false
@@ -1523,8 +1530,12 @@
           console.time('insert ' + @config.file.name)
           console.time('loadFile ' + @config.file.name)

-        if Worker and @config.allowWebWorkers
-          @worker = new Worker Meteor.absoluteUrl 'packages/ostrio_files/worker.min.js'
+        if @collection._supportWebWorker and @config.allowWebWorkers
+          try
+            @worker = new Worker @collection._webWorkerUrl
+          catch wwError
+            @worker = false
+            console.warn '[FilesCollection] [insert] [create WebWorker]: Can\'t create WebWorker, fallback to MainThread', wwError if @collection.debug
         else
           @worker = null


extracted method with intention revealing name

--- js-library/app/js/models/fact.coffee
+++ js-library/app/js/models/fact.coffee
@@ -57,8 +57,9 @@
         @balloon.hide()

       @balloon_timeout = setTimeout hideBalloon, 300
+  click: => @openFactlinkModal()

-  click: =>
+  openFactlinkModal: =>
     @balloon.startLoading()

     Factlink.showInfo @id, =>


remove fullscreen setting

remove fullscreen setting

--- client/ide/workspace/panes/settingspane.coffee
+++ client/ide/workspace/panes/settingspane.coffee
@@ -73,19 +73,6 @@
     @bell             = new KodingSwitch
       size            : "tiny settings-on-off"

-    mainView          = KD.getSingleton "mainView"
-    @fullscreen       = new KodingSwitch
-      size            : "tiny settings-on-off"
-      callback        : (state) =>
-        if state
-          mainView.enableFullscreen()
-        else
-          mainView.disableFullscreen()
-        {menu} = @getOptions()
-        menu.contextMenu.destroy()
-        menu.click()
-      defaultValue  : mainView.isFullscreen()
-
     @scrollback     = new KDSelectBox
       selectOptions : __terminalSettings.scrollback

@@ -117,5 +104,4 @@

Scrollback         {{> @scrollback}}

Use visual bell                        {{> @bell}}

-
Fullscreen                             {{> @fullscreen}}

     """


Split createSettings into 2 different methods for terminal and editor.

--- client/ide/workspace/panes/settingspane.coffee
+++ client/ide/workspace/panes/settingspane.coffee
@@ -6,9 +6,10 @@

     super options, data

-    @createSettings()
+    @createEditorSettings()
+    @createTerminalSettings()

-  createSettings: ->
+  createEditorSettings: ->
     @useSoftTabs         = new KodingSwitch
       cssClass           : "tiny settings-on-off"
       callback           : (state) -> console.log state
@@ -52,6 +53,7 @@
       partial            : "⌘ Keyboard Shortcuts"
       click              : => log "show shortcuts"

+  createTerminalSettings: ->
   pistachio: ->
     """

Use soft tabs                           {{> @useSoftTabs}}


Starting work on server-side denormed reactives

--- models.coffee
+++ models.coffee
@@ -27,7 +27,7 @@

 class J.Model
-    @_getEscapedSubdoc = (subDoc) ->
+    @_getEscapedSubdoc: (subDoc) ->
         if J.util.isPlainObject subDoc
             ret = {}
             for key, value of subDoc
@@ -39,7 +39,7 @@
             subDoc

-    @_getUnescapedSubdoc = (subDoc) ->
+    @_getUnescapedSubdoc: (subDoc) ->
         if J.util.isPlainObject subDoc
             ret = {}
             for key, value of subDoc
@@ -51,7 +51,7 @@
             subDoc

-    @escapeDot = (key) ->
+    @escapeDot: (key) ->
         key.replace(/\./g, '*DOT*').replace(/\$/g, '*DOLLAR*')

@@ -108,8 +108,55 @@
         helper x

-    @unescapeDot = (key) =>
+    @unescapeDot: (key) =>
         key.replace(/\*DOT\*/g, '.').replace(/\*DOLLAR\*/g, '$')
+
+
+    _save: (upsert, collection, callback) ->
+        unless collection instanceof Mongo.Collection
+            throw new Meteor.Error "Invalid collection to #{@modelClass.name}.save"
+
+        if @attached and @collection is collection
+            throw new Meteor.Error "Can't save #{@modelClass.name} instance into its own attached collection"
+
+        unless @alive
+            throw new Meteor.Error "Can't save dead #{@modelClass.name} instance"
+
+        doc = Tracker.nonreactive => @toDoc()
+
+        if @modelClass.idSpec is J.PropTypes.key and not doc._id?
+            key = @key()
+            if not key?
+                throw new Error "<#{@modelClass.name}>.key() returned #{key}"
+            doc._id = key
+        else
+            doc._id = @_id ? Random.hexString(10)
+
+        Meteor.call '_jSave', @modelClass.name, doc, upsert, callback
+
+        # Returns @_id
+        @_id ?= doc._id
+
+
+    _setReactives: (reactivesObj) ->
+        ###
+            reactivesObj:
+                The _reactives field from the raw Mongo doc,
+                filtered down to only the denormed reactives
+                we cared to fetch.
+        ###
+
+        reactivesSetter = {}
+
+        for reactiveName, reactiveSpec of @modelClass.reactiveSpecs
+            if reactiveSpec.denorm
+                if reactiveName of reactivesObj
+                    reactivesSetter[reactiveName] =
+                        @modelClass._getUnescapedSubdoc reactivesObj[reactiveName].val
+                if reactivesSetter[reactiveName] is undefined
+                    reactivesSetter[reactiveName] = J.makeValueNotReadyObject()
+
+        @_reactives._forceSet reactivesSetter

     clone: ->
@@ -240,32 +287,6 @@
         @_save true, collection, callback

-    _save: (upsert, collection, callback) ->
-        unless collection instanceof Mongo.Collection
-            throw new Meteor.Error "Invalid collection to #{@modelClass.name}.save"
-
-        if @attached and @collection is collection
-            throw new Meteor.Error "Can't save #{@modelClass.name} instance into its own attached collection"
-
-        unless @alive
-            throw new Meteor.Error "Can't save dead #{@modelClass.name} instance"
-
-        doc = Tracker.nonreactive => @toDoc()
-
-        if @modelClass.idSpec is J.PropTypes.key and not doc._id?
-            key = @key()
-            if not key?
-                throw new Error "<#{@modelClass.name}>.key() returned #{key}"
-            doc._id = key
-        else
-            doc._id = @_id ? Random.hexString(10)
-
-        Meteor.call '_jSave', @modelClass.name, doc, upsert, callback
-
-        # Returns @_id
-        @_id ?= doc._id
-
-
     set: (fields) ->
         unless J.util.isPlainObject fields
             throw new Meteor.Error "Invalid fields setter: #{fields}"
@@ -427,6 +448,13 @@
             @reactives[reactiveName] = do (reactiveName, reactiveSpec) =>
                 J.AutoVar "<#{modelName} ##{@_id}>.!#{reactiveName}",
                     => reactiveSpec.val.call @
+
+        # The @_reactives dict stores the published values of reactives
+        # with denorm:true (i.e. server handles all their reactivity).
+        @_reactives = J.Dict() # denormedReactiveName: value
+        for reactiveName, reactiveSpec of @modelClass.reactiveSpecs
+            if reactiveSpec.denorm
+                @_reactives.setOrAdd reactiveName, J.makeValueNotReadyObject()

         null

@@ -518,25 +546,8 @@
                     instance = modelClass.fromDoc doc
                     instance.collection = collection
                     instance.attached = true
-
                     instance._fields.setReadOnly true, true
-
-                    # The @_reactives dict stores the published values of reactives
-                    # with denorm:true (i.e. server handles all their reactivity).
-                    instance._reactives = J.Dict() # denormedReactiveName: value
-                    for reactiveName, reactiveSpec of modelClass.reactiveSpecs
-                        if reactiveSpec.denorm
-                            instance._reactives.setOrAdd reactiveName, J.makeValueNotReadyObject()
-
-                    if reactivesObj?
-                        reactivesSetter = {}
-
-                        for reactiveName, reactiveObj of reactivesObj
-                            reactivesSetter[reactiveName] = modelClass._getUnescapedSubdoc reactiveObj.val
-                            if reactivesSetter[reactiveName] is undefined
-                                reactivesSetter[reactiveName] = J.makeValueNotReadyObject()
-
-                        instance._reactives.set reactivesSetter
+                    instance._setReactives reactivesObj ? {}

                     collection._attachedInstances[id] = instance

@@ -554,18 +565,7 @@
                             fieldsSetter[fieldName] = J.makeValueNotReadyObject()
                     instance._fields._forceSet fieldsSetter

-                    if reactivesObj?
-                        reactivesSetter = {}
-
-                        for reactiveName, reactiveSpec of modelClass.reactiveSpecs
-                            if reactiveSpec.denorm
-                                if reactiveName of reactivesObj
-                                    reactivesSetter[reactiveName] =
-                                        modelClass._getUnescapedSubdoc reactivesObj[reactiveName].val
-                                if reactivesSetter[reactiveName] is undefined
-                                    reactivesSetter[reactiveName] = J.makeValueNotReadyObject()
-
-                        instance._reactives._forceSet reactivesSetter
+                    instance._setReactives reactivesObj ? {}

                 removed: (id) ->
                     instance = collection._attachedInstances[id]
@@ -575,22 +575,14 @@
                     delete collection._attachedInstances[id]

         if Meteor.isServer
-            # The server uses exclusively detached instances and
-            # doesn't make use of much reactivity.
+            # The server uses exclusively detached instances
             collection = new Mongo.Collection collectionName,
                 transform: (doc) ->
-                    doc = _.clone doc
-
-                    reactivesObj = modelClass._getUnescapedSubdoc doc._reactives
-                    if reactivesObj?
-                        console.warn "No reason for the server-side ORM to ever
-                            fetch the _reactives field."
-                        console.trace()
+                    reactivesObj = doc._reactives
                     delete doc._reactives
-
                     instance = modelClass.fromDoc doc
                     instance.collection = collection
-
+                    instance._setReactives reactivesObj ? {}
                     instance

             for indexSpec in modelClass.indexSpecs
@@ -650,7 +642,10 @@
                     skip: options.skip
                     limit: options.limit

-                if Meteor.isServer
+                if Meteor.isClient
+                    J.fetching.requestQuery querySpec
+
+                else
                     qsString = J.fetching.stringifyQs querySpec

                     if J._watchedQuerySpecSet.get()?
@@ -664,12 +659,7 @@
                         fieldName = fieldSpec.split('.')[0]
                         continue if fieldName is '_id'

-                        if fieldName is '_reactives'
-                            # Loading denormalized reactive values isn't currently
-                            # supported on the server
-                            delete mongoFieldsArg[fieldSpec]
-                        else
-                            fieldNameSet[fieldName] = true
+                        fieldNameSet[fieldName] = true

                     mongoOptions = _.clone options
                     mongoOptions.fields = mongoFieldsArg
@@ -680,6 +670,8 @@
                     # though they were included in the query) as having a
                     # default value of null.
                     for fieldName of fieldNameSet
+                        continue if fieldName is '_reactives'
+
                         instances.forEach (instance) =>
                             setter = {}

@@ -691,8 +683,6 @@
                             instance.set setter

                     return instances
-
-                J.fetching.requestQuery querySpec

             fetchOne: (selector = {}, options = {}) ->
                 if selector instanceof J.Dict


- fetchByUserKey is internal method, don't share it. - Use fetchByKey method, which gets username from session.

--- client/landing/app/MainApp/kodingrouter.coffee
+++ client/landing/app/MainApp/kodingrouter.coffee
@@ -355,8 +355,7 @@
             """
             return showModal title, content

-          KD.remote.api.JKodingKey.fetchByUserKey
-            username: KD.whoami().profile.nickname
+          KD.remote.api.JKodingKey.fetchByKey
             key: key
           , (err, kodingKey) =>
             unless kodingKey


LocalStorage: It’s failing to use localStorage when user disabled it in browser settings, we need to handle that. Now LocalStorage tries to use window.localStorage if fails it keeps in memory

--- client/landing/app/MainApp/localstorage.coffee
+++ client/landing/app/MainApp/localstorage.coffee
@@ -1,6 +1,12 @@
 class LocalStorage extends AppStorage

-  storage = window.localStorage
+  # This is necessary to check if its allowed to use window.localStorage
+  # otherwise it crashes the rest of the code
+  try
+    storage = window.localStorage
+  catch e
+    warn "#{e.name} occured while getting localStorage:", e.message
+    storage = {}

   fetchStorage:->
     KD.utils.defer => @emit "ready"


fix "drop" hubot-auth integration (#1)

fix "drop" hubot-auth integration (#1)

--- src/factoids.coffee
+++ src/factoids.coffee
@@ -114,7 +114,7 @@
     msg.send "OK #{who}, aliased #{alias} to #{target}" if factoids.set msg.match[1], "@#{msg.match[2]}", msg.message.user.name

   robot.respond /drop (.{3,})/i, (msg) ->
-    user = msg.message.user.name.toLowerCase()
+    user = msg.envelope.user
     isAdmin = robot.auth?.hasRole(user, 'factoids-admin') or robot.auth?.hasRole(user, 'admin')
     if isAdmin or not robot.auth?
       factoid = msg.match[1]


Auto update cond

Auto update cond

--- views/components/ship-parts/shippane.cjsx
+++ views/components/ship-parts/shippane.cjsx
@@ -31,8 +31,13 @@
     label
   onCondChange: (cond) ->
     condDynamicUpdateFlag = true
+    ships = Object.clone @state.ships
+    for shipData, j in ships
+      ships[j].cond = cond[j]
+      window._ships[shipData.id].api_cond = cond[j]
     @setState
       cond: cond
+      ships: ships
   handleResponse: (e) ->
     {method, path, body, postBody} = e.detail
     label = Object.clone @state.label


add breakpoint toggle button

add breakpoint toggle button

--- lib/python-debugger-view.coffee
+++ lib/python-debugger-view.coffee
@@ -32,6 +32,10 @@
       @subview "commandEntryView", new TextEditorView
         mini: true,
         placeholderText: "> Enter debugger commands here"
+      @button outlet: "breakpointBtn", click: "toggleBreakpoint", class: "btn", =>
+        @span "breakpoint"
+      @button class: "btn", =>
+        @span "        "
       @button outlet: "runBtn", click: "runApp", class: "btn", =>
         @span "run"
       @button outlet: "stopBtn", click: "stopApp", class: "btn", =>


Server: Tests: add empty suggestedUsername by default

--- servers/testhelper/handler/createuserhelper.coffee
+++ servers/testhelper/handler/createuserhelper.coffee
@@ -11,10 +11,11 @@
 generateCreateUserRequestBody = (opts = {}) ->

   defaultBodyObject =
-    email     : generateRandomEmail()
-    username  : generateRandomUsername()
-    lastName  : generateRandomString()
-    firstName : generateRandomString()
+    email             : generateRandomEmail()
+    username          : generateRandomUsername()
+    lastName          : generateRandomString()
+    firstName         : generateRandomString()
+    suggestedUsername : ''

   defaultBodyObject = deepObjectExtend defaultBodyObject, opts

@@ -22,8 +23,6 @@

 generateCreateUserRequestParams = (opts = {}) ->
-
-  body = generateCreateUserRequestBody()

   params =
     url     : generateUrl { route : '-/api/user/create' }


Add create and addProject methods

Add create and addProject methods

--- src/Models/Groups.coffee
+++ src/Models/Groups.coffee
@@ -14,7 +14,7 @@
       fn = params
       params = {}
     @debug "Groups::all()"
-
+
     params.page ?= 1
     params.per_page ?= 100

@@ -40,7 +40,7 @@
   listProjects: (groupId, fn = null) =>
     @debug "Groups::listProjects()"
     @get "groups/#{parseInt groupId}", (data) => fn data.projects if fn
-
+
   listMembers: (groupId, fn = null) =>
     @debug "Groups::listMembers()"
     @get "groups/#{parseInt groupId}/members", (data) => fn data if fn
@@ -62,5 +62,12 @@

     @post "groups/#{parseInt groupId}/members", params, (data) -> fn data if fn

+  create: (params = {}, fn = null) =>
+    @debug "Groups::create()"
+    @post "groups", params, (data) -> fn data if fn
+
+  addProject: (groupId, projectId, fn = null) =>
+    @debug "Groups::addProject(#{groupId}, #{projectId})"
+    @post "groups/#{parseInt groupId}/projects/#{parseInt projectId}", null, (data) -> fn data if fn

 module.exports = (client) -> new Groups client


Tests: Helpers: MessagesHelpers: Added leaveConversation helper.

--- client/test/lib/helpers/messageshelpers.coffee
+++ client/test/lib/helpers/messageshelpers.coffee
@@ -10,7 +10,7 @@
     formSelector       = '.new-message-form.with-fields .formline.recipient'
     textareaSelector   = '.reply-input-widget.private [testpath=ActivityInputView]'
     itemSelector       = '.kdlistitemview-dropdown-member span.profile'
-    messageSelector    = "[testpath=main-sidebar] .profile[href='/#{user.userName}']"
+    messageSelector    = ".activity-sidebar .messages .sidebar-message-text [href='/kodingtester']"
     message          or= 'Hello World!'

     browser.element 'css selector', messageSelector, (result) =>
@@ -39,3 +39,22 @@
           .assert.containsText     '.message-pane.privatemessage', message # Assertion
           .waitForElementVisible   '.message-pane.privatemessage .with-parent', 20000
           .assert.containsText     '.activity-sidebar .messages', user.fullName # Assertion
+
+
+  leaveConversation: (browser, user, message) ->
+
+    buttonSelector          = '.privatemessage .chat-heads span.chevron'
+    listWrapperSelector     = '.context-list-wrapper li.leave-conversation'
+    sidebarMessageSelector  = '.activity-sidebar .messages'
+
+    browser
+      .waitForElementVisible  '.privatemessage .chat-heads', 20000
+      .waitForElementVisible  buttonSelector, 20000
+      .click                  buttonSelector
+      .waitForElementVisible  listWrapperSelector, 20000
+      .click                  listWrapperSelector
+      .waitForElementVisible  '.kdmodal-inner', 20000
+      .click                  '.kdmodal-inner .kdmodal-buttons button.red'
+      .waitForElementVisible  sidebarMessageSelector, 20000, ->
+        text = browser.getText sidebarMessageSelector
+        assert.notEqual text, user.fullName # Assertion


Go-to-decl context menu item

Go-to-decl context menu item

--- lib/haskell-ghc-mod.coffee
+++ lib/haskell-ghc-mod.coffee
@@ -280,6 +280,9 @@
           ,
             'label': 'Insert Import'
             'command': 'haskell-ghc-mod:insert-import'
+          ,
+            'label': 'Go To Declaration'
+            'command': 'haskell-ghc-mod:go-to-declaration'
         ]
       ]


use atom.tooltips instead of html title attribute

--- lib/status-bar-tile-view.coffee
+++ lib/status-bar-tile-view.coffee
@@ -1,3 +1,4 @@
+{Disposable} = require 'atom'
 path = require 'path'

 class StatusBarTileView extends HTMLElement
@@ -26,6 +27,8 @@
     @msg?.textContent = @prepend + content

   setTitle: (text) ->
-    @setAttribute('title', text)
+    @tooltip?.dispose()
+    @tooltip = atom.tooltips.add this,
+      title: text

 module.exports = document.registerElement('wakatime-status-bar-tile', prototype: StatusBarTileView.prototype, extends: 'div')


Support promise catch on wiredRemote

Support promise catch on wiredRemote

--- src/remote_factory.coffee
+++ src/remote_factory.coffee
@@ -37,6 +37,8 @@
       names = [names] unless names instanceof Array
       then: (callback) =>
         callback @_domainEvents.filter (x) -> names.indexOf(x.name) > -1
+      catch: (callback) ->
+        callback()

     wiredRemote.findDomainEventsByNameAndAggregateId = (names, aggregateIds) ->
@@ -45,6 +47,8 @@
       then: (callback) =>
         callback @_domainEvents.filter (x) ->
           names.indexOf(x.name) > -1 and x.aggregate and aggregateIds.indexOf(x.aggregate.id) > -1
+      catch: (callback) ->
+        callback()

     wiredRemote


Add GIFs

Add GIFs

--- src/highfive.coffee
+++ src/highfive.coffee
@@ -51,6 +51,7 @@
             msg.send """
             @channel WOOOOOO #{to_user}!
             #{from_user} is high-fiving you for #{reason}!
+            #{msg.random GIFs}
             """

             if amt > 0 and process.env.HUBOT_HIGHFIVE_AWARD_LIMIT != 0
@@ -86,3 +87,39 @@

     getUser: (uid, callback) ->
         @get "users.info?user=#{uid}", callback
+
+# GIFs for celebration
+GIFs = [
+    'http://i.giphy.com/zl170rmVMCpEY.gif',
+    'http://i.giphy.com/yoJC2vEwxkwbMZmSCk.gif',
+    'http://i.giphy.com/Qh5dZDCFqr1dK.gif',
+    'http://i.giphy.com/GCLlQnV7wzKLu.gif',
+    'http://i.giphy.com/MhHXeM4SpKrpC.gif',
+    'http://i.giphy.com/Z7bxVQl7nWes.gif',
+    'http://i.giphy.com/ns8SCo6O6g7nO.gif',
+    'http://a.fod4.com/images/GifGuide/dancing/280sw007883.gif',
+    'http://a.fod4.com/images/GifGuide/dancing/pr2.gif',
+    'http://0.media.collegehumor.cvcdn.com/46/28/291cb0abc0c99142aace1353dc12b755-car-race-high-five.gif',
+    'http://2.media.collegehumor.cvcdn.com/75/26/b31d5b98a4a27537d075960b7b247773-giant-high-five-from-jackass.gif',
+    'http://2.media.collegehumor.cvcdn.com/84/67/ff88c44dec5f9c2747e30549a375d481-bear-high-five.gif',
+    'http://0.media.collegehumor.cvcdn.com/17/53/30709bc3c9b060baf771c0b2e2626f95-snow-white-high-five.gif',
+    'http://i.giphy.com/p3LmvxiO6noGc.gif',
+    'http://i.giphy.com/DYvroxifyHEmA.gif',
+    'http://i.giphy.com/kolvlRnXh8Jj2.gif',
+    'http://i.giphy.com/tX5iDEX1n1Xxe.gif',
+    'http://i.giphy.com/xeXEpUVvAxCV2.gif',
+    'http://i.giphy.com/UkhHIZ37IDRGo.gif',
+    'http://i.giphy.com/oUZqX2UgK2xnq.gif',
+    'http://a.fod4.com/images/GifGuide/dancing/163563561.gif',
+    'http://i.giphy.com/mEOjrcTumos80.gif',
+    'http://i.giphy.com/99dauSQPLUuIg.gif',
+    'http://i.giphy.com/3HICMfLGqgWRy.gif',
+    'http://i.giphy.com/GYU7rBEQtBGfe.gif',
+    'http://i.giphy.com/vXEeRBP3QeJ2w.gif',
+    'http://i.giphy.com/Cj3Ce7e8h2EKY.gif',
+    'http://i.giphy.com/3Xtt7hlXvUTvi.gif',
+    'http://i.giphy.com/1453cgfKvRLMyc.gif',
+    'http://i.giphy.com/WdxAL8nmOCQ5a.gif',
+    'http://a.fod4.com/images/GifGuide/dancing/tumblr_llatbbCeky1qbnthu.gif',
+    'http://i.giphy.com/FrDlVZMD96nzG.gif',
+]


Boyscout

Boyscout

--- core/app/backbone/regions/crossFadeRegion.coffee
+++ core/app/backbone/regions/crossFadeRegion.coffee
@@ -6,13 +6,13 @@
     currentView = @currentView

     if currentView
-      @.$el.fadeOut(@theTimeout, () =>
+      @$el.fadeOut(@theTimeout, () =>
         @show(newView)
       )
     else
       @show(newView)

   open: (view) ->
-    @.$el.hide();
-    @.$el.html(view.el);
-    @.$el.fadeIn(@theTimeout);+    @$el.hide();
+    @$el.html(view.el);
+    @$el.fadeIn(@theTimeout);


Don't fetch the cursor twice (for infinite)

See alethes/meteor-pages#69

--- lib/pages.coffee
+++ lib/pages.coffee
@@ -446,7 +446,7 @@
           sort: @sort
           skip: if @infiniteItemsLimit isnt Infinity and n > @infiniteItemsLimit then n - @infiniteItemsLimit else 0
           limit: @infiniteItemsLimit
-        ).fetch()
+        )
       else #if page in @received
         c = @PaginatedCollection.find(
           _.object([


remove extraneous semi colon

remove extraneous semi colon

--- src/htmldiff.coffee
+++ src/htmldiff.coffee
@@ -311,7 +311,7 @@
 find_matching_blocks.create_index = create_index
 diff.calculate_operations = calculate_operations
 diff.render_operations = render_operations
-diff.render_operations_dual_pane = render_operations_dual_pane;
+diff.render_operations_dual_pane = render_operations_dual_pane

 if typeof define is 'function'
   define [], ()-> diff


Add xunit istanbul reporter

Add xunit istanbul reporter

--- lib/mocha-phantomjs.coffee
+++ lib/mocha-phantomjs.coffee
@@ -247,6 +247,10 @@
   constructor: (config) ->
     super 'html-cov', config

+class XunitIstanbul extends Reporter
+
+  constructor: -> super 'xunit-istanbul'
+

 reporterString = system.args[2] || 'spec'
 reporterString = ("#{s.charAt(0).toUpperCase()}#{s.slice(1)}" for s in reporterString.split('-')).join('')


put some missing files

put some missing files

--- client/includes.coffee
+++ client/includes.coffee
@@ -139,6 +139,8 @@
   "app/MainApp/notificationcontroller.coffee",

   # COMMON VIEWS
+  "app/CommonViews/applicationview/applicationtabview.coffee",
+  "app/CommonViews/applicationview/applicationtabhandleholder.coffee",

   "app/CommonViews/linkviews/linkview.coffee",
   "app/CommonViews/linkviews/customlinkview.coffee",
@@ -163,6 +165,7 @@

   "app/CommonViews/LikeView.coffee",
   "app/CommonViews/Tags/TagViews.coffee",
+  "app/CommonViews/Tags/TagAutoCompleteController.coffee",
   "app/CommonViews/FormViews.coffee",
   "app/CommonViews/messagesList.coffee",
   "app/CommonViews/CommonInputWithButton.coffee",
@@ -175,7 +178,6 @@
   "app/CommonViews/HelpBox.coffee",
   "app/CommonViews/KeyboardHelper.coffee",
   "app/CommonViews/Navigation.coffee",
-  "app/CommonViews/Tags/TagAutoCompleteController.coffee",
   "app/CommonViews/VerifyPINModal.coffee",

   "app/CommonViews/followbutton.coffee",
@@ -372,6 +374,7 @@
   # new ace
   "app/Applications/Ace.kdapplication/AppController.coffee",
   "app/Applications/Ace.kdapplication/AppView.coffee",
+  "app/Applications/Ace.kdapplication/aceappview.coffee",
   "app/Applications/Ace.kdapplication/ace.coffee",
   "app/Applications/Ace.kdapplication/acesettingsview.coffee",
   "app/Applications/Ace.kdapplication/acesettings.coffee",
@@ -389,6 +392,7 @@
   # webterm
   "app/Applications/WebTerm.kdapplication/AppController.coffee",
   "app/Applications/WebTerm.kdapplication/AppView.coffee",
+  "app/Applications/WebTerm.kdapplication/webtermappview.coffee",
   "app/Applications/WebTerm.kdapplication/webtermsettingsview.coffee",
   "app/Applications/WebTerm.kdapplication/webtermsettings.coffee",
   "app/Applications/WebTerm.kdapplication/src/ControlCodeReader.coffee",


fixes #1205

fixes #1205

--- jsapp/xlform/src/model.base.coffee
+++ jsapp/xlform/src/model.base.coffee
@@ -116,6 +116,10 @@
         else
           vals2set.value = value
         @set(vals2set)
+      else if @key is "required" and !value
+        @set({
+          value: false,
+        })
       @_order = $configs.columnOrder(@key)
       @postInitialize()


Delete timeout

Delete timeout

--- lib/proxy.coffee
+++ lib/proxy.coffee
@@ -123,11 +123,7 @@
               # Delay 3s for retry
               yield Promise.delay(3000) unless success
           else
-            if parsed.pathname.startsWith '/kcs'
-              [response, body] = yield requestAsync resolve options
-            else
-              [response, body] = yield requestAsync _.extend (resolve options),
-                timeout: 4000
+            [response, body] = yield requestAsync resolve options
             res.writeHead response.statusCode, response.headers
             res.end body
           if parsed.pathname in ['/kcs/mainD2.swf', '/kcsapi/api_start2', '/kcsapi/api_get_member/basic', '/kcsapi/api_port/port']


:art: be descriptive for intention

:art: be descriptive for intention

--- lib/operation-stack.coffee
+++ lib/operation-stack.coffee
@@ -78,13 +78,14 @@
     if @vimState.isMode('normal')
       unless @editor.getLastSelection().isEmpty()
         throw new OperationStackError('Selection remains on normal-mode')
-
-      for c in @editor.getCursors() when c.isAtEndOfLine() and not c.isAtBeginningOfLine()
-        withKeepingGoalColumn c, (c) ->
-          c.moveLeft()
-
+      @ensureCursorNotOnEOL()
     @vimState.showCursors()
     @vimState.reset()
+
+  ensureCursorNotOnEOL: ->
+    for c in @editor.getCursors() when c.isAtEndOfLine() and not c.isAtBeginningOfLine()
+      withKeepingGoalColumn c, (c) ->
+        c.moveLeft()

   peekTop: ->
     _.last @stack


Use friendlier error message when apm available fails

--- lib/package-manager.coffee
+++ lib/package-manager.coffee
@@ -29,7 +29,7 @@

         callback(null, @packages)
       else
-        error = new Error("apm available failed with code: #{code}")
+        error = new Error('Fetching available packages and themes failed.')
         error.stdout = stdout
         error.stderr = stderr
         callback(error)


add a wormhole to the central invocation

--- library/exposure/central.coffee
+++ library/exposure/central.coffee
@@ -99,7 +99,7 @@
     # some other useful data, when it will be build the container.
     # The event will be persisted in the MongoDB and propagate to an
     # every node that may listen to it via the pub/sub kit of Redis.
-    central: (event, metadata=new Object()) ->
+    central: (event, metadata=new Object(), append) ->
         deficient = "supplied event name is not correct"
         unordered = "must have the valid metadata object"
         return unless nconf.get("central:enabled") is yes
@@ -112,6 +112,8 @@
         assert _.isString packed.platform = os.platform()
         assert _.isString packed.scope = @kernel.scope.tag
         assert _.isObject packed.server = nconf.get "server"
+        append = _.find(arguments, _.isFunction) or append
+        append.call @, event, metadata, packed if append
         return this.publishCentralEvent event, packed

     # A part of the internal implementation of the central events


throw out what makes little sense now

throw out what makes little sense now

--- library/nucleus/cakefile.coffee
+++ library/nucleus/cakefile.coffee
@@ -67,7 +67,6 @@
     option "-d", "--documents [PATH]", "Path to the documents directory"
     option "-s", "--scoping [SCOPE]", "The name of the scope to boot kernel"
     option "-i", "--logging [LEVEL]", "The level to use for the logging output"
-    option "-w", "--watch", "Watch the library sources and recompile it"
     option "-g", "--git-hub-pages", "Publish documents to GitHub pages"
     option "-c", "--compress-code", "Compress JS code once is compiled"

@@ -103,9 +102,6 @@
         assert library = options.library or DEFAULT_LIBRARY
         assert artifacts = options.artifacts or DEFAULT_ARTIFACTS
         assert parameters = ["-c", "-o", artifacts, library]
-        parameters.unshift "-w" if options.watch or false
-        assert watching = "Watching the %s directory".blue
-        logger.info watching, library.bold if options.watch
         norm = mangle: no, compress: no, output: beautify: yes
         comp = mangle: no, compress: yes, output: beautify: no
         opts = if "compress-code" of options then comp else norm


a missing bit from the client code

a missing bit from the client code

--- client/security.coffee
+++ client/security.coffee
@@ -45,3 +45,7 @@
   # we will replace font-awesome with a small number of svg icons at a later date...
   if (!$("link[href='https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css']").length)
     $('<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">').appendTo("head")
+
+  update_footer ownerName, isAuthenticated
+
+window.plugins.security = {setup, update_footer}


fix cli requirements

fix cli requirements

--- cli.coffee
+++ cli.coffee
@@ -11,8 +11,8 @@
 filesystem         = require './backend/filesystem'
 pouch              = require './backend/db'
 device             = require './backend/device'
-localEventWatcher  = require './backend/localEventWatcher'
-remoteEventWatcher = require './backend/remoteEventWatcher'
+localEventWatcher  = require './backend/local_event_watcher'
+remoteEventWatcher = require './backend/remote_event_watcher'
 pkg                = require './package.json'


Check if group exists

Check if group exists

--- client/app/CommonViews/LinkViews.coffee
+++ client/app/CommonViews/LinkViews.coffee
@@ -117,7 +117,7 @@
       @createParticipantSubviews()
       @render()

-    if group[0].constructorName
+    if group[0]?.constructorName
       lastFour = group.slice -4
       bongo.cacheable lastFour, (err, bucketContents)=>
         callback bucketContents