Update gutter when bookmarks are created/destroyed

Previously the gutter of splits would not update when bookmarks
were added or removed.

--- lib/bookmarks.coffee
+++ lib/bookmarks.coffee
@@ -18,6 +18,7 @@
     {@editor, @gutter} = editorView

     editorView.on 'editor:display-updated', @renderBookmarkMarkers
+    @editor.getBuffer().on 'bookmarks:created bookmarks:destroyed', @renderBookmarkMarkers

     editorView.command 'bookmarks:toggle-bookmark', @toggleBookmark
     editorView.command 'bookmarks:jump-to-next-bookmark', @jumpToNextBookmark
@@ -30,8 +31,9 @@
       position = cursor.getBufferPosition()
       bookmarks = @findBookmarkMarkers(startBufferRow: position.row)

-      if bookmarks and bookmarks.length
+      if bookmarks?.length > 0
         bookmark.destroy() for bookmark in bookmarks
+        @editor.getBuffer().emit 'bookmarks:destroyed'
       else
         newmark = @createBookmarkMarker(position.row)

@@ -100,13 +102,14 @@
     bookmark = @displayBuffer().markBufferRange(range, @bookmarkMarkerAttributes(invalidate: 'surround'))
     bookmark.on 'changed', ({isValid}) ->
       bookmark.destroy() unless isValid
+    @editor.getBuffer().emit 'bookmarks:created'
     bookmark

   findBookmarkMarkers: (attributes={}) ->
     @displayBuffer().findMarkers(@bookmarkMarkerAttributes(attributes))

   bookmarkMarkerAttributes: (attributes={}) ->
-    _.extend(attributes, class: 'bookmark', displayBufferId: @displayBuffer().id)
+    _.extend(attributes, class: 'bookmark')

   displayBuffer: ->
     @editor.displayBuffer


Removed outdated code

Removed outdated code

--- .metalsmith/client/animations.coffee
+++ .metalsmith/client/animations.coffee
@@ -76,12 +76,6 @@
 setupBackTopAnimations = ->
     offset = 250
     duration = 300
-    t = $('#').offset().top
-    $(document).scroll ->
-      if $(this).scrollTop() > t
-        $('#voice2').css 'border-bottom': '2px solid #f4f5f8'
-        $('#voice3').css 'border-bottom': '2px solid #2e375b'
-      return
     jQuery(window).scroll ->
       if jQuery(this).scrollTop() > offset
         jQuery('.back-to-top').fadeIn duration


Remove console.log()s.

Remove console.log()s.

--- lib/dom_utils.coffee
+++ lib/dom_utils.coffee
@@ -247,8 +247,6 @@
             element.setSelectionRange element.value.length, element.value.length

   simulateClick: (element, modifiers = {}) ->
-    console.log modifiers
-    console.log Object.keys(modifiers).length
     eventSequence = ["mouseover", "mousedown", "mouseup", "click"]
     for event in eventSequence
       defaultActionShouldTrigger =


ipinfo token added, translation fixed

ipinfo token added, translation fixed

--- js/main.coffee
+++ js/main.coffee
@@ -3,7 +3,7 @@

 jQuery(document).ready ($) ->
 	$('input').focus()
-	$.get 'http://ipinfo.io', ((response) ->
+	$.get 'https://ipinfo.io?token=83129ddfd413c4', ((response) ->

 		if response.country = 'GB'
 			$('.country').html ' UK'
@@ -17,7 +17,7 @@
 		else if response.country = 'ES'
 			$('.country').html ' España'
 			$('form').attr 'action', 'https://www.google.es/search'
-			$('label').html 'Escribe and presiona intro para buscar'
+			$('label').html 'Escribe y presiona intro para buscar'

 		else if response.country = 'DE'
 			$('.country').html ' Deutschland'


Modify Heartbeat#start to use intavalMillis got from Tracker Server

--- lib/heartbeat.coffee
+++ lib/heartbeat.coffee
@@ -1,11 +1,27 @@
 class Heartbeat
-  constructor: (@socket)->
+  constructor: (@connection)->
+    connection.on 'heartbeat', (data)->
+      console.log data
+
+  @intervalMillis = undefined

   start: ->
-    socket = @socket;
-    @intervalId = setInterval ->
-      socket.send '{"category":"heartbeat"}'
-    , 3000
+    connection = @connection
+
+    if Heartbeat.intervalMillis?
+      @intervalId = setInterval ->
+        connection.send '{"category":"heartbeat"}'
+      , Heartbeat.intervalMillis
+    else
+      self = @
+      trackerResponseHander = (data)->
+        if data.values?.heartbeatinterval
+          Heartbeat.intervalMillis = data.values.heartbeatinterval
+          connection.removeListener 'tracker', trackerResponseHander
+          self.start()
+
+      connection.on 'tracker', trackerResponseHander
+        .send '{"category":"tracker","request":"get","values":["heartbeatinterval"]}'
     @

   stop: ->


fix(languages): Make grock aware of .cson files

--- lib/languages.coffee
+++ lib/languages.coffee
@@ -45,7 +45,7 @@
     foldPrefix:        '^'

   'CoffeeScript':
-    nameMatchers:      ['.coffee', 'Cakefile']
+    nameMatchers:      ['.coffee', '.cson', 'Cakefile']
     pygmentsLexer:     'coffee-script'
     highlightJS:       'coffeescript'
     # **CoffeScript's multi-line block-comment styles.**


Autoclose client alerts when task is finished

--- client/app/components/toast.coffee
+++ client/app/components/toast.coffee
@@ -84,14 +84,22 @@
                     t 'there were errors', smart_count: toast.errors.length

     componentDidMount: ->
+        @shouldAutoclose()
+
+    componentDidUpdate: ->
+        @shouldAutoclose()
+
+    shouldAutoclose: ->
         hasErrors = @props.toast.errors? and @props.toast.errors.length
         if @props.toast.autoclose or (@props.toast.finished and not hasErrors)
-            setTimeout =>
-                @getDOMNode().classList.add 'autoclose'
-            , 1000
-            setTimeout =>
-                @acknowledge()
-            , 10000
+            target = @getDOMNode()
+            if not target.classList.contains 'autoclose'
+                setTimeout ->
+                    target.classList.add 'autoclose'
+                , 1000
+                setTimeout =>
+                    @acknowledge()
+                , 10000

 module.exports.Container = ToastContainer =  React.createClass
     displayName: 'ToastContainer'


[Gutter] Add ::getName to LineNumberGutterComponent

--- src/line-number-gutter-component.coffee
+++ src/line-number-gutter-component.coffee
@@ -6,7 +6,7 @@
 class LineNumberGutterComponent
   dummyLineNumberNode: null

-  constructor: ({@onMouseDown, @editor}) ->
+  constructor: ({@onMouseDown, @editor, @name}) ->
     @lineNumberNodesById = {}

     @domNode = document.createElement('div')
@@ -17,6 +17,9 @@

     @domNode.addEventListener 'click', @onClick
     @domNode.addEventListener 'mousedown', @onMouseDown
+
+  getName: ->
+    @name

   updateSync: (state) ->
     @newState = state.lineNumberGutter


Selections and characters as temp variables

--- lib/rich-text.coffee
+++ lib/rich-text.coffee
@@ -8,19 +8,19 @@
   # @param author [String] the name of the local author
   constructor: (content, editor, author) ->
     # TODO: generate a UID
-    @selections = new Selections
-    @characters = new Characters content, @selections
-
+    @_selections = new Selections()
+    @_characters = new Characters content, @_selections
     @author = author

     @editor = editor
-    # shouldn't go there because of modularity
+    # FIXME: shouldn't go there because of modularity
     @editor.cursors = @editor.getModule("multi-cursor")

   _getModel: (Y, Operation) ->
     if not @_model?
-      sels = @selections._getModel(Y, Operation)
-      chars = @characters._getModel(Y, Operation)
+      # call the _getModel for each
+      sels = @_selections._getModel(Y, Operation)
+      chars = @_characters._getModel(Y, Operation)
       cursors = new Operation.ListManager(@).execute()

       @_model = new Operation.MapManager(@).execute()
@@ -47,9 +47,9 @@

   setCursor: () ->
     if @editor.getSelection()
-      position = @characters.val @editor.getSelection().start
+      position = (@get "characters").val @editor.getSelection().start
     else
-      position = @characters.val 0
+      position = (@get "characters").val 0

     selfCursor =
       author: @author
@@ -62,16 +62,15 @@
       switch event.name
         when "cursors"
           id = event.object.author
-          index = @characters.indexOf event.object.char
+          index = (@get "characters").indexOf event.object.char
           text = event.object.author
           color = "grey" # FIXME

           if event.type == "update" or event.type == "add"
             @editor.cursors.setCursor id, index, text, color

-
         when "characters"
-          charPos = @characters.indexOf event.object
+          charPos = (@get "characters").indexOf event.object
           delta = {ops: [{retain: charPos}]}
           del = {delete: 1}
           ins = {insert: event.object.char, attributes: event.object.attributes}
@@ -89,8 +88,8 @@
           @editor.setContents delta

         when "selections"
-          selectionStart = @characters.indexOf (event.object.left or event.oldValue.left)
-          selectionEnd = @characters.indexOf (event.object.right or event.oldValue.right)
+          selectionStart = (@get "characters").indexOf (event.object.left or event.oldValue.left)
+          selectionEnd = (@get "characters").indexOf (event.object.right or event.oldValue.right)
           attributes = event.object.attributes
           if event.type == "update" or event.type == "insert"
             delta = {ops: [{retain: selectionStart},
@@ -98,5 +97,6 @@
             ]}
             @editor.setContents delta
           else if event.type == "delete"
-            #FIXME
+            #FIXME: depending on how it is implemented, selections can be overriden by negating
+            #their values (set them to null) or the can just be deleted, let's see then…
             console.log "Ohow… what am I supposed to do there?"


jupyter pg-up/down at cell boundary

jupyter pg-up/down at cell boundary

--- src/smc-webapp/jupyter/codemirror-editor.cjsx
+++ src/smc-webapp/jupyter/codemirror-editor.cjsx
@@ -177,9 +177,30 @@
         else
             CodeMirror.commands.goLineDown(@cm)

-    adjacent_cell: (y) ->
+    page_up_key: ->
+        if not @cm?
+            return
+        cur = @cm.getCursor()
+        if cur?.line == @cm.firstLine() and cur?.ch == 0
+            @adjacent_cell(-1, -1)
+        else
+            CodeMirror.commands.goPageUp(@cm)
+
+    page_down_key: ->
+        if not @cm?
+            return
+        cur = @cm.getCursor()
+        n = @cm.lastLine()
+        if cur?.line == n and cur?.ch == @cm.getLine(n)?.length
+            @adjacent_cell(0, 1)  # the down arrow goes to the general key handler instead
+        else
+            CodeMirror.commands.goPageDown(@cm)
+
+    adjacent_cell: (y, delta) ->
         @props.actions.set_mode('escape')
         finish = =>
+            if delta?
+                @props.actions.move_cursor(delta)
             @props.actions.set_mode('edit')
             @props.actions.set_cursor(@props.actions.store.get('cur_id'), {x:0, y:y})
         setTimeout(finish, 0)
@@ -217,6 +238,8 @@
             options0.extraKeys["Tab"]  = @tab_key
             options0.extraKeys["Up"]   = @up_key
             options0.extraKeys["Down"] = @down_key
+            options0.extraKeys["PageUp"]   = @page_up_key
+            options0.extraKeys["PageDown"] = @page_down_key
         else
             options0.readOnly = true


cleaned up

cleaned up

--- lib/soda-sync.coffee
+++ lib/soda-sync.coffee
@@ -26,39 +26,35 @@
   # useful when writting helpers
   current: -> Fiber.current.soda_sync_browser

-cleanArgs = (browser, cb) ->
-  [browser,cb] = [null,browser] if typeof browser is 'function'
-  browser = browser?.with if browser?.with?
-  [browser,cb]
-
-Soda = (browser, cb) ->
-  [browser,cb] = cleanArgs browser, cb
-  if cb? then Sync ->
-    Fiber.current.soda_sync_browser = browser
-    cb.apply browser, []
-  if browser
+Soda = (options, cb) ->
+  [options,cb] = [null,options] if typeof options is 'function'
+  if cb?
+    Sync ->
+      Fiber.current.soda_sync_browser = options?.with
+      cb.apply options?.with, []
+  if options
     # returning an identical function with context(browser) preconfigured
-    (browser2, cb2) ->
-      [browser2,cb2] = cleanArgs browser2, cb2
-      browser2 = browser if not browser2?
-      Soda with:browser2, cb2
+    (options2, cb2) ->
+      [options2,cb2] = cleanArgs options2, cb2
+      options2 = options if not options2?
+      Soda options2, cb2

 # careful, below browser is a function so it get evaluated with the rest
 # of the code
-SodaCan = (browser, cb) ->
-  [browser,cb] = cleanArgs browser, cb
+SodaCan = (options, cb) ->
+  [options,cb] = [null,options] if typeof options is 'function'
   if cb?
     return (done) ->
       Sync ->
-        Fiber.current.soda_sync_browser = browser?()
-        cb.apply browser?(), []
+        Fiber.current.soda_sync_browser = options?.with?()
+        cb.apply options?.with?(), []
         done() if done?
-  if browser
+  if options
     # returning an identical function with context(browser) preconfigured
-    return (browser2, cb2) ->
-      [browser2,cb2] = cleanArgs browser2, cb2
-      browser2 = browser if not browser2?
-      SodaCan with:browser2, cb2
+    return (options2, cb2) ->
+      [options2,cb2] = cleanArgs options2, cb2
+      options2 = options if not options2?
+      SodaCan options2, cb2

 exports.Soda = Soda
 exports.SodaCan = SodaCan


HLS Segmenter: Speed up snapshots

HLS Segmenter: Speed up snapshots

* Now that there isn't actual data to strip out, just return a clone of the
  segment array

--- src/streammachine/rewind/hls_segmenter.coffee
+++ src/streammachine/rewind/hls_segmenter.coffee
@@ -313,13 +313,7 @@
         #----------

         snapshot: (cb) ->
-            # A snapshot is the segments array with the data stripped out
-            snapshot = []
-            for seg in @segments
-                seg = _.clone(seg)
-                delete seg.data
-                snapshot.push seg
-
+            snapshot = @segments.slice(0)
             cb null, snapshot

         #----------


Replace upperCase with shift-lowerCase

Replace upperCase with shift-lowerCase

--- keymaps/win32.cson
+++ keymaps/win32.cson
@@ -25,13 +25,13 @@

   # Sublime Parity
   'ctrl-,': 'application:show-settings'
-  'ctrl-N': 'application:new-window'
-  'ctrl-W': 'window:close'
+  'ctrl-shift-n': 'application:new-window'
+  'ctrl-shift-w': 'window:close'
   'ctrl-o': 'application:open-file'
-  'ctrl-T': 'pane:reopen-closed-item'
+  'ctrl-shift-t': 'pane:reopen-closed-item'
   'ctrl-n': 'application:new-file'
   'ctrl-s': 'core:save'
-  'ctrl-S': 'core:save-as'
+  'ctrl-shift-s': 'core:save-as'
   'ctrl-f4': 'core:close'
   'ctrl-w': 'core:close'
   'ctrl-z': 'core:undo'
@@ -133,7 +133,7 @@
   'ctrl-down': 'editor:move-line-down'
   'ctrl-/': 'editor:toggle-line-comments'
   'ctrl-j': 'editor:join-lines'
-  'ctrl-D': 'editor:duplicate-lines'
+  'ctrl-shift-d': 'editor:duplicate-lines'

   'ctrl-alt-[': 'editor:fold-current-row'
   'ctrl-alt-]': 'editor:unfold-current-row'
@@ -154,10 +154,10 @@
   'ctrl-alt-=': 'pane:increase-size'
   'ctrl-alt--': 'pane:decrease-size'

-# allow standard input fields to work correctly
+# Allow standard input fields to work correctly
 'body .native-key-bindings':
   'ctrl-z': 'native!'
-  'ctrl-Z': 'native!'
+  'ctrl-shift-z': 'native!'
   'ctrl-x': 'native!'
   'ctrl-c': 'native!'
   'ctrl-v': 'native!'


fixes exit event not being fired in the case the atom pane is destroyed

--- lib/term-view.coffee
+++ lib/term-view.coffee
@@ -114,8 +114,7 @@
         @term.write utf8
         @emitter.emit('stdout', utf8)

-      @ptyProcess.on 'term3:exit', (data) =>
-        @emitter.emit('exit', data)
+      @ptyProcess.on 'term3:exit', () =>
         @exit()

@@ -238,14 +237,16 @@
     pane.destroyItem(this);

   destroy: ->
-    @off 'focus', @focus
-    $(window).off 'resize', @resizeToPane_
     if @ptyProcess
       @ptyProcess.terminate()
       @ptyProcess = null
+    # we always have a @term
     if @term
+      @emitter.emit('exit')
       @term.destroy()
       @term = null
+      @off 'focus', @focus
+      $(window).off 'resize', @resizeToPane_
     if @disposable
       @disposable.dispose()
       @disposable = null


Only listen for tree-view:toggle-side in one place

Listening in both the view the package main causes the toggle
to be done twice when run

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -48,7 +48,6 @@
     @command 'tree-view:show-in-file-manager', => @showSelectedEntryInFileManager()
     @command 'tree-view:copy-project-path', => @copySelectedEntryPath(true)
     @command 'tool-panel:unfocus', => @unfocus()
-    @command 'tree-view:toggle-side', => @toggleSide()

     @on 'tree-view:directory-modified', =>
       if @hasFocus()


its not necessarily normalMode specific

its not necessarily normalMode specific

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -49,7 +49,7 @@
     @subscriptions.add @editor.onDidAddCursor @ensureCursorIsWithinLine

     @editorElement.classList.add("vim-mode")
-    @setupNormalMode()
+    @init()
     if settings.startInInsertMode()
       @activateInsertMode()
     else
@@ -71,7 +71,7 @@
   # Private: Creates the plugin's bindings
   #
   # Returns nothing.
-  setupNormalMode: ->
+  init: ->
     @registerCommands
       'activate-normal-mode': => @activateNormalMode()
       'activate-linewise-visual-mode': => @activateVisualMode('linewise')


add operation yank-to-last-character-of-line

...to allow mapping `Y` to do this instead of yanking the entire line.

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -92,6 +92,7 @@
       'toggle-case-now': => new Operators.ToggleCase(@editor, this, complete: true)
       'yank': => @linewiseAliasedOperator(Operators.Yank)
       'yank-line': => [new Operators.Yank(@editor, this), new Motions.MoveToRelativeLine(@editor, this)]
+      'yank-to-last-character-of-line': => [new Operators.Yank(@editor, this), new Motions.MoveToLastCharacterOfLine(@editor, this)]
       'put-before': => new Operators.Put(@editor, this, location: 'before')
       'put-after': => new Operators.Put(@editor, this, location: 'after')
       'join': => new Operators.Join(@editor, this)


participant: Update TODO

participant: Update TODO

--- src/participant.coffee
+++ src/participant.coffee
@@ -25,12 +25,6 @@
   def.inports = portsWithQueue def.inports
   def.outports = portsWithQueue def.outports
   return def
-
-# Improvements in participant definition
-# TODO: introduce a 'role' field. This will .
-# Multiple participants with same role will be automatically merged into one
-# TODO: when no id specified (normal case), generate id = role+uuid.v4()
-# TODO: take address string, create transport automatically

 addQueues = (ports, role) ->
   for p in ports


Fix error message upon page load failure.

--- src/phantomtask.coffee
+++ src/phantomtask.coffee
@@ -27,7 +27,7 @@

 		@page.open url, (status) =>
 			unless status is "success"
-				callback new Error "Failed to open page: #{src}"
+				callback new Error "Failed to open page: #{url}"

 			for script in [].concat(@scripts)
 				@page.injectJs script


reorder conditionals

reorder conditionals

--- src/cleaner/cleaner.coffee
+++ src/cleaner/cleaner.coffee
@@ -12,34 +12,18 @@
 cleanLine = (line) ->
   clean = ''
   for char in line.split ''
-    if state is quoting
-      if escape_stack.length
+    if escape_stack.length
+      if state is quoting
         prev = escape_stack.pop()
         clean += prev + char
-      else
-        switch char
-          when '\\'
-            escape_stack.push char
-          when '\''
-            clean += char
-            state = ready
-          else
-            clean += char
-    else if state is commenting
-      if escape_stack.length
+      else if state is commenting
         prev = escape_stack.pop()
         if char is '/'
           state = ready
           clean += '  '
         else
           clean += '  '
-      else
-        if char is '*'
-          escape_stack.push char
-        else
-          clean += ' '
-    else # if state is ready
-      if escape_stack.length
+      else # if state is ready
         prev = escape_stack.pop()
         switch char
           when '*'
@@ -52,7 +36,22 @@
             state = quoting
           else
             clean += prev + char
-      else
+    else
+      if state is quoting
+        switch char
+          when '\\'
+            escape_stack.push char
+          when '\''
+            clean += char
+            state = ready
+          else
+            clean += char
+      else if state is commenting
+        if char is '*'
+          escape_stack.push char
+        else
+          clean += ' '
+      else # if state is ready
         switch char
           when '/'
             escape_stack.push char


Explicit return, just to be safe

Explicit return, just to be safe

--- src/projectiles.coffee
+++ src/projectiles.coffee
@@ -44,6 +44,7 @@
               else
                 ent.destroy()

-            @destroy()
+            return @destroy()
+

     @destroy() if @position.x < 0 || @position.x > Kona.Canvas.width


Add win32/linux keybindings.

Add win32/linux keybindings.

--- keymaps/atomatigit.cson
+++ keymaps/atomatigit.cson
@@ -1,9 +1,16 @@
-'.workspace':
+'.platform-darwin .workspace':
   'cmd-alt-g': 'atomatigit:show'
   'cmd-alt-c': 'atomatigit:complete-commit'

-'.atomatigit':
+'.platform-win32 .workspace, .platform-linux .workspace':
+  'ctrl-alt-g': 'atomatigit:show'
+  'ctrl-alt-c': 'atomatigit:complete-commit'
+
+'.platform-darwin .atomatigit':
   'cmd-alt-g': 'atomatigit:close'
+
+'.platform-win32 .atomatigit, .platform-linux .atomatigit':
+  'ctrl-alt-g': 'atomatigit:close'

 '.atomatigit .file-list-view, .atomatigit .branch-list-view, .atomatigit .commit-list-view':
   'down': 'atomatigit:next'
@@ -27,8 +34,13 @@
   'shift-z': 'atomatigit:stash-pop'
   'u': 'atomatigit:unstage'
   'tab': 'atomatigit:toggle-diff'
+  'c': 'atomatigit:commit'
+
+'.platform-darwin .atomatigit .file-list-view':
   'cmd-shift-z': 'atomatigit:show'
-  'c': 'atomatigit:commit'
+
+'.platform-win32 .atomatigit .file-list-view, platform-linux .atomatigit .file-list-view':
+  'ctrl-shift-z': 'atomatigit:show'

 '.atomatigit .branch-list-view':
   'c': 'atomatigit:create-branch'


Update iPad app detection for iOS 9

Update iPad app detection for iOS 9

--- app/core/application.coffee
+++ app/core/application.coffee
@@ -46,7 +46,7 @@
 Application = initialize: ->
   Router = require('core/Router')
   @isProduction = -> document.location.href.search('https?://localhost:3000') is -1
-  @isIPadApp = webkit?.messageHandlers? and navigator.userAgent?.indexOf('iPad') isnt -1
+  @isIPadApp = webkit?.messageHandlers? and navigator.userAgent?.indexOf('iPhone OS') isnt -1
   $('body').addClass 'ipad' if @isIPadApp
   @tracker = new Tracker()
   @facebookHandler = new FacebookHandler()


Changed name in UT

Changed name in UT

--- client/src/tests/tests.spec.coffee
+++ client/src/tests/tests.spec.coffee
@@ -6,7 +6,7 @@
 compileExpression = (expression) -> RW.compileExpression(expression, eval)

-describe "gamEvolve", ->
+describe "RedWire", ->
   beforeEach ->
     @addMatchers
       toDeeplyEqual: (expected) -> _.isEqual(@actual, expected)


Fixed default position for first run

Fixed default position for first run

--- source/Main.coffee
+++ source/Main.coffee
@@ -177,19 +177,26 @@

 root.loadLocation = ->
+	loadedLocation = false
+
 	if not window.location.hash
 		# load previously saved location from localStorage
 		if typeof(Storage) != undefined
+			loadedLocation = true
+
+			for i in [0..3]
+				x = localStorage["kosmosRotation" + i]
+				if x == undefined || x == NaN || not x
+					loadedLocation = false
+					break
+				desiredRotation[i] = parseFloat(x)
+
+			quat.copy(smoothRotation, desiredRotation)
+
 			camera.position[i] = parseFloat(localStorage["kosmosOffset" + i]) || 0.0 for i in [0..2]
 			originOffset[i] = parseFloat(localStorage["kosmosOrigin" + i]) || 0.0 for i in [0..2]

-			for i in [0..3]
-				x = localStorage["kosmosRotation" + i]
-				if x == undefined || x == NaN || not x then break
-				desiredRotation[i] = parseFloat(x)
-			quat.copy(smoothRotation, desiredRotation)
-
-	else
+	if not loadedLocation
 		# set location from url hash
 		if not parseLocationString(window.location.hash)
 			# else, use a default location


debug

debug

--- JSKit/04_JSMenuView.coffee
+++ JSKit/04_JSMenuView.coffee
@@ -35,15 +35,13 @@
         $(@_viewSelector).append(@_div)
         $(@_viewSelector+"_menu").css("left", "0px")
         $(@_viewSelector+"_menu").css("top", "0px")
-        $(@_viewSelector+"_menu").css("width", @_frame.size.width+"px")
-        $(@_viewSelector+"_menu").css("height", (@_frame.size.height * @_menuitem.length)+"px")
+        #$(@_viewSelector+"_menu").css("width", @_frame.size.width+"px")
+        #$(@_viewSelector+"_menu").css("height", (@_frame.size.height * @_menuitem.length)+"px")
         $(@_viewSelector+"_menu").css("position", "absolute")
         $(@_viewSelector+"_menu").css("overflow", "visible")
         $(@_viewSelector+"_menu").css("font-size", @_textSize+"pt")
         #$(@_viewSelector+"_menu").css("background-color", @_backgroundColor)
         $(@_viewSelector+"_menu").menu
-            _closeOnDocumentClick: (event)->
-              return false
             select: (event, ui) =>
                 if (@_userInteractionEnabled == false)
                     return


Tests: Remove unnecessary require.

Tests: Remove unnecessary require.

--- tests/src/messages/privatemessage.coffee
+++ tests/src/messages/privatemessage.coffee
@@ -1,6 +1,5 @@
 helpers = require '../helpers/helpers.js'
 assert  = require 'assert'
-curl    = require 'curlrequest'

 module.exports =


Remove trailing ;

Remove trailing ;

--- src/pane-container-view.coffee
+++ src/pane-container-view.coffee
@@ -115,7 +115,7 @@
     distance: (pointA, pointB) ->
       x = pointB.x - pointA.x
       y = pointB.y - pointA.y
-      Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
+      Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))

     pane = @getActivePane()
     box = @boundingBoxForPane(pane)


watev

watev

--- spec/a_spec.coffee
+++ spec/a_spec.coffee
@@ -1,4 +1,8 @@
 expect = require('chai').expect
+
+Immutable = require 'immutable'
+# ExpectHelpers = require './helpers/expect_helpers'
+# expectIs = ExpectHelpers.expectIs

 describe 'an array', ->
   # arr = [1,2,3]
@@ -6,16 +10,22 @@
   #   expect(arr.length).to.eq(3)
   # it 'should have a 2 in the middle', ->
   #   expect(arr[1]).to.eq(2)
-  it 'box', ->
-    x = 0
-    y = 0
-    w = 4
-    h = 4
-    left = x+w
-    right = x+w
-    top = y
-    bottom = y+h

-    ts = 16
-
-
+  # it 'wat', ->
+  #   console.log "HI!"
+  #   a = Immutable.fromJS(
+  #     hello: "world"
+  #     one: "two"
+  #   )
+  #
+  #   b = Immutable.fromJS(
+  #     hello: "world"
+  #     one: "two"
+  #   )
+  #   expectIs a,b
+  #   b = b.set('one',' two')
+  #
+  #   expectIs a,b
+  #   # expect(a).to.eq(b)
+  #
+  #


Tests for resolve()

Tests for resolve()

--- spec/simrou.coffee
+++ spec/simrou.coffee
@@ -135,14 +135,53 @@

     describe 'resolve()', ->
         router = null
+        route = null
+        spy = jasmine.createSpy('actionHandler')

         beforeEach ->
             router = new Simrou()
+            route = router.addRoute('foo')
+            spy.reset()

         it 'invokes the "any" action handler of a registered route', ->
-            spy = jasmine.createSpy('actionHandler')
-            route = router.addRoute('foo')
             route.any(spy)
+            router.resolve('foo')
+            expect(spy).toHaveBeenCalled()

-            router.resolve('foo')
-            expect(spy).toHaveBeenCalled()+            spy.reset()
+            router.resolve('bar')
+            expect(spy).not.toHaveBeenCalled()
+
+        it 'invokes the event handlers specific to the HTTP method', ->
+            route.post(spy)
+            router.resolve('foo', 'POST')
+            expect(spy).toHaveBeenCalled()
+
+            spy.reset()
+            router.resolve('foo', 'PUT')
+            expect(spy).not.toHaveBeenCalled()
+
+        it 'reports whether the route has been successfully resolved', ->
+            route.any(spy)
+            expect(router.resolve('foo')).toBeTruthy()
+            expect(router.resolve('bar')).toBeFalsy()
+
+        it 'ignores leading hash symbols', ->
+            expect(router.resolve('###foo')).toBeTruthy()
+
+        it 'ignores trailing slashes', ->
+            expect(router.resolve('foo///')).toBeTruthy()
+
+        it 'can resolve "/"', ->
+            router.addRoute('/')
+            expect(router.resolve('/')).toBeTruthy()
+
+
+    describe 'navigate()', ->
+        # ...
+
+
+    describe 'removeRoute()', ->
+        # ...
+
+


adding more info about ticket status

adding more info about ticket status

--- src/scripts/jira-issues.coffee
+++ src/scripts/jira-issues.coffee
@@ -53,7 +53,21 @@
                 try
                   json = JSON.parse(body)
                   key = json.key
-                  msg.send "[" + key + "] " + json.fields.summary
+
+                  message = "[" + key + "] " + json.fields.summary
+                  message += '\nStatus: '+json.fields.status.name
+                  if (json.fields.assignee and json.fields.assignee.displayName)
+                    message += ', assigned to ' + json.fields.assignee.displayName
+                  else
+                    message += ', unassigned'
+                  message += ", rep. by "+json.fields.reporter.displayName
+                  if json.fields.fixVersions and json.fields.fixVersions.length > 0
+                    message += ', fixVersion: '+json.fields.fixVersions[0].name
+                  else
+                    message += ', fixVersion: NONE'
+
+                  msg.send message
+
                   urlRegex = new RegExp(jiraUrl + "[^\\s]*" + key)
                   if not msg.message.text.match(urlRegex)
                     msg.send jiraUrl + "/browse/" + key


upsert instead of remove/save

upsert instead of remove/save

--- src/scripts/mongo-brain.coffee
+++ src/scripts/mongo-brain.coffee
@@ -52,13 +52,15 @@

       robot.brain.on 'save', (data) ->
         db.collection 'storage', (err, collection) ->
-          collection.remove {}, (err) ->
-            if err?
-              throw err
-            else
-              data = encodeKeys data
-              collection.save data, (err) ->
-                throw err if err?
+          # https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js#L373
+          # update(selector, document, options, callback)
+          data = encodeKeys data
+          opts =
+            safe: true
+            upsert: true
+          collection.update data, opts, (err) ->
+            throw err if err?

       robot.brain.on 'close', ->
         db.close()
+


Add jobs page

Add jobs page

--- services/web/app/coffee/Features/StaticPages/StaticPagesRouter.coffee
+++ services/web/app/coffee/Features/StaticPages/StaticPagesRouter.coffee
@@ -13,6 +13,7 @@
 		webRouter.get '/privacy_policy', HomeController.externalPage("privacy", "Privacy Policy")
 		webRouter.get '/planned_maintenance', HomeController.externalPage("planned_maintenance", "Planned Maintenance")
 		webRouter.get '/style', HomeController.externalPage("style_guide", "Style Guide")
+		webRouter.get '/jobs', HomeController.externalPage("jobs", "Jobs")

 		webRouter.get '/dropbox', HomeController.externalPage("dropbox", "Dropbox and ShareLaTeX")


fix the regex

fix the regex

--- modules/danbooru.coffee
+++ modules/danbooru.coffee
@@ -41,7 +41,7 @@
       includeCommandNameInArgs: true
       allowDM: true
     }, (msg, args, d)=>
-      waifu = (args[1].match(/w+/) or [''])[0]
+      waifu = (args[1].match(/\w+/) or [''])[0]
       return msg.reply "Usage: ```#{@prefix}#{args[0]} <danbooru_tag>```" if not waifu
       # Do a dummy search
       qs = {


update atom snippets

update atom snippets

--- atom.symlink/snippets.cson
+++ atom.symlink/snippets.cson
@@ -1,6 +1,6 @@
 '.js':
   'it test block':
-    'prefix': 'it'
+    'prefix': 'ii'
     'body': """
       it('${1:does this thing}', () => {
         $2
@@ -40,6 +40,14 @@
       });
     """

+  'then promise block':
+    'prefix': 'then'
+    'body': """
+      .then((${1:returnValueFromLastPromise}) => {
+
+      });
+    """
+
 '.coffee':
   'context test block':
     'prefix': 'co'


Disable for an entire page by adding 'data-no-turbolink' to body tag.

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -143,10 +143,13 @@
 newTabClick = (event) ->
   event.which > 1 or event.metaKey or event.ctrlKey

+disabledForPage = ->
+  window.document.body.getAttribute('data-no-turbolink')?
+
 ignoreClick = (event, link) ->
   samePageLink(link) or crossOriginLink(link) or anchoredLink(link) or
   nonHtmlLink(link)  or remoteLink(link)      or noTurbolink(link)  or
-  newTabClick(event)
+  newTabClick(event) or disabledForPage()

 browserSupportsPushState =


remove args from show view method

remove args from show view method

--- lib/client/javascripts/nali/model.js.coffee
+++ lib/client/javascripts/nali/model.js.coffee
@@ -34,7 +34,7 @@
         @views[ short = view._shortName ] = view
         shortCap = short.capitalize()
         unless @[ viewMethod = 'view' + shortCap ]? then @[ viewMethod ] = -> @view short
-        unless @[ showMethod = 'show' + shortCap ]? then @[ showMethod ] = ( insertTo ) -> @show short, insertTo
+        unless @[ showMethod = 'show' + shortCap ]? then @[ showMethod ] = -> @show short
         unless @[ hideMethod = 'hide' + shortCap ]? then @[ hideMethod ] = -> @hide short
     @


make connect problem message less sensitive

--- src/smc-webapp/init_app.coffee
+++ src/smc-webapp/init_app.coffee
@@ -268,7 +268,7 @@
     # avoid buffer overflow
     recent_disconnects = recent_disconnects[-100..]

-num_recent_disconnects = (minutes=10) ->
+num_recent_disconnects = (minutes=5) ->
     # note the "+", since we work with timestamps
     ago = +misc.minutes_ago(minutes)
     return (x for x in recent_disconnects when x > ago).length
@@ -353,7 +353,7 @@
         {SITE_NAME} = require('smc-util/theme')
         SiteName = redux.getStore('customize').site_name ? SITE_NAME
         if (reconnection_warning == null) or (reconnection_warning < (+misc.minutes_ago(1)))
-            if num_recent_disconnects() >= 5 or attempt >= 20
+            if num_recent_disconnects() >= 7 or attempt >= 20
                 reconnect
                     type: "error"
                     timeout: 10


Lower the target age for scoring

Lower the target age for scoring

--- entity.coffee
+++ entity.coffee
@@ -220,7 +220,7 @@
   doHitGoal: ->
     currentTime = unixTime()
     age = currentTime - @spawnTime
-    @callback(this) unless age < 5
+    @callback(this) unless age < 0.4

 class LimitedLifespanAdapter extends EntityAdapter
   constructor: (@lifespan, @callback, @next) ->


throttle onScroll handler when triggered by osu:page:change.

Every discussion post triggers an osu:page:change event when mounted;
this ends up not working so well when there're hundreds of them
continually triggering the handler that needs to do a layout before the
page has even finished rendering all the components.

--- resources/assets/coffee/_classes/sticky-header.coffee
+++ resources/assets/coffee/_classes/sticky-header.coffee
@@ -7,6 +7,7 @@
 # 2. Add 'js-sticky-header' class to a marker element that should cause the sticky to show.
 class @StickyHeader
   constructor: ->
+    @debouncedOnScroll = _.debounce @onScroll, 100
     @header = document.getElementsByClassName('js-pinned-header')
     @marker = document.getElementsByClassName('js-sticky-header')
     @pinnedSticky = document.getElementsByClassName('js-pinned-header-sticky')
@@ -14,7 +15,7 @@
     @stickyContent = document.getElementsByClassName('js-sticky-header-content')

     $(window).on 'throttled-scroll', @onScroll
-    $(document).on 'turbolinks:load osu:page:change', => Timeout.set 0, @onScroll
+    $(document).on 'turbolinks:load osu:page:change', @debouncedOnScroll
     $(window).on 'throttled-resize', @stickOrUnstick


Use 'onRun' ('load') instead of 'onBeforeAction' for some things

- Works better for c9d7894 (#44)
- https://github.com/EventedMind/iron-router/issues/624

--- router.coffee
+++ router.coffee
@@ -27,17 +27,17 @@
     path: '/blog'
     controller: 'BlogController'

-    onBeforeAction: ->
+    onRun: ->
       if Blog.settings.blogIndexTemplate
         @template = Blog.settings.blogIndexTemplate

       if not Session.get('postLimit') and Blog.settings.pageSize
         Session.set 'postLimit', Blog.settings.pageSize

-      @subscribe('posts', Session.get('postLimit')).wait()
-
-    waitOn: ->
+    waitOn: -> [
+      Meteor.subscribe 'posts', Session.get('postLimit')
       Meteor.subscribe 'authors'
+    ]

     fastRender: true

@@ -56,7 +56,7 @@
     path: '/blog/tag/:tag'
     controller: 'BlogController'

-    onBeforeAction: ->
+    onRun: ->
       if Blog.settings.blogIndexTemplate
         @template = Blog.settings.blogIndexTemplate

@@ -84,7 +84,7 @@
     controller: 'BlogController'
     notFoundTemplate: 'blogNotFound'

-    onBeforeAction: ->
+    onRun: ->
       if Blog.settings.blogShowTemplate
         @template = Blog.settings.blogShowTemplate

@@ -117,11 +117,11 @@
     path: '/admin/blog'
     controller: 'BlogController'

-    onBeforeAction: (pause) ->
-
+    onRun: ->
       if Blog.settings.blogAdminTemplate
         @template = Blog.settings.blogAdminTemplate

+    onBeforeAction: (pause) ->
       if Meteor.loggingIn()
         return pause()

@@ -141,10 +141,11 @@
     path: '/admin/blog/edit/:id'
     controller: 'BlogController'

-    onBeforeAction: (pause) ->
+    onRun: ->
       if Blog.settings.blogAdminEditTemplate
         @template = Blog.settings.blogAdminEditTemplate

+    onBeforeAction: (pause) ->
       if Meteor.loggingIn()
         return pause()


Forcing a Programmaticon II upgrade when you get it.

--- app/lib/LevelOptions.coffee
+++ app/lib/LevelOptions.coffee
@@ -210,7 +210,7 @@
     hidesRealTimePlayback: true
     hidesCodeToolbar: true
     requiredGear: {feet: 'leather-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-ii', eyes: 'crude-glasses'}
-    restrictedGear: {feet: 'simple-boots', 'right-hand': 'crude-builders-hammer'}
+    restrictedGear: {feet: 'simple-boots', 'right-hand': 'crude-builders-hammer', 'programming-book': 'programmaticon-i'}
   'village-guard':
     hidesCodeToolbar: true
     requiredGear: {feet: 'leather-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-ii', eyes: 'crude-glasses'}
@@ -244,7 +244,7 @@

   # Ranger branch
   'munchkin-harvest':
-    requiredGear: {torso: 'tarnished-bronze-breastplate', waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'long-sword', 'left-hand': 'bronze-shield', wrists: 'sundial-wristwatch'}
+    requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'long-sword', 'left-hand': 'bronze-shield', wrists: 'sundial-wristwatch'}
     restrictedGear: {}
   'swift-dagger':
     requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'crude-crossbow', 'left-hand': 'crude-dagger', wrists: 'sundial-wristwatch'}
@@ -255,7 +255,7 @@

   # Wizard branch
   'arcane-ally':
-    requiredGear: {torso: 'tarnished-bronze-breastplate', waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'long-sword', 'left-hand': 'bronze-shield', wrists: 'sundial-wristwatch'}
+    requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'long-sword', 'left-hand': 'bronze-shield', wrists: 'sundial-wristwatch'}
     restrictedGear: {eyes: 'crude-glasses'}
   'touch-of-death':
     requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'enchanted-stick', 'left-hand': 'unholy-tome-i', wrists: 'sundial-wristwatch'}


Rename 'Add {File,Folder}' -> 'New {File,Folder}'

Signed-off-by: Jessica Lord <7acbd1498e1be10a1338a61df68672fe4daf35c1@github.com>

--- menus/tree-view.cson
+++ menus/tree-view.cson
@@ -21,8 +21,8 @@

 'context-menu':
   '.tree-view.full-menu': [
-    {'label': 'Add File', 'command': 'tree-view:add-file'}
-    {'label': 'Add Folder', 'command': 'tree-view:add-folder'}
+    {'label': 'New File', 'command': 'tree-view:add-file'}
+    {'label': 'New Folder', 'command': 'tree-view:add-folder'}
     {'type': 'separator'}

     {'label': 'Rename', 'command': 'tree-view:move'}


adding in way to set periods on builder spec

--- test/components/task-plan/builder.spec.coffee
+++ test/components/task-plan/builder.spec.coffee
@@ -5,9 +5,13 @@
 {Testing, sinon, expect, _, React} = require '../helpers/component-testing'
 {ExtendBasePlan, PlanRenderHelper} = require '../helpers/task-plan'

+{CourseListingActions, CourseListingStore} = require '../../../src/flux/course-listing'
+{CourseStore} = require '../../../src/flux/course'
+
 yesterday = (new Date(Date.now() - 1000 * 3600 * 24)).toString()
 tomorrow = (new Date(Date.now() + 1000 * 3600 * 24)).toString()

+COURSES = require '../../../api/user/courses.json'
 NEW_READING = ExtendBasePlan({id: "_CREATING_1", settings: {page_ids: []}})
 PUBLISHED_MODEL = ExtendBasePlan({
   title: 'hello',
@@ -20,13 +24,13 @@
 describe 'Task Plan Builder', ->
   beforeEach ->
     TaskPlanActions.reset()
+    CourseListingActions.loaded(COURSES)

   it 'should load expected plan', ->
     helper(PUBLISHED_MODEL).then ({dom}) ->
       expect(dom.querySelector('#reading-title').value).to.equal(PUBLISHED_MODEL.title)
       descriptionValue = dom.querySelector('.assignment-description textarea').value
       expect(descriptionValue).to.equal(PUBLISHED_MODEL.description)
-

   it 'should allow editable periods radio if plan is not visible', ->
     helper(NEW_READING).then ({dom}) ->
@@ -42,10 +46,17 @@
   it 'should not allow editable open date if plan is visible', ->
     helper(PUBLISHED_MODEL).then ({dom, element}) ->
       expect(element.refs.openDate.props.disabled).to.be.true
-
+
+
   it 'hides periods by default', ->
     helper(NEW_READING).then ({dom, element}) ->
       expect(dom.querySelector('.tasking-plan.tutor-date-input')).to.be.null
+
+  it 'can show individual periods', ->
+    helper(NEW_READING).then ({dom}) ->
+      element.setIndividualPeriods()
+      expect(dom.querySelectorAll('.tasking-plan.tutor-date-input').length).to.equal(COURSES.periods.length)
+

   it 'does not load a default due at for all periods', ->
     helper(NEW_READING).then ({dom, element}) ->


Only cancel focus events if the editor is or contains the related target

--- src/text-editor-element.coffee
+++ src/text-editor-element.coffee
@@ -90,7 +90,7 @@
     @component = null

   focused: (event) ->
-    if @hasFocus()
+    if @contains(event.relatedTarget) or this is event.relatedTarget
       event.stopPropagation()
       return


Last Promise

Last Promise

--- src/client/component/component.coffee
+++ src/client/component/component.coffee
@@ -150,7 +150,7 @@
         catch e
           console.error("Caught error in buildContext", e.message, e)
           dfd.reject(e)
-        dfd.promise()
+        dfd.promise

       loggedIn: =>
         return false unless @sandbox.data.api.model('me').id?


Fixes template lookup

Fixes template lookup

--- src/client/component/templates.coffee
+++ src/client/component/templates.coffee
@@ -31,7 +31,7 @@
         _domTemplate($el)
     app:
       hullGlobal: (tplName)->
-        if module.global.Hull.templates[tplName]
+        if module.global.Hull.templates?[tplName]
           setupTemplate module.global.Hull.templates["#{tplName}"], tplName
       meteor: (tplName)->
         if module.global.Meteor? && module.global.Template?[tplName]?
@@ -40,7 +40,7 @@
         if module.global.HandlebarsTemplates? && module.global.HandlebarsTemplates?[tplName]?
           setupTemplate(module.global.HandlebarsTemplates[tplName], tplName, true)
       hullDefault: (tplName)->
-        if module.global.Hull.templates._default?[tplName]
+        if module.global.Hull.templates?._default?[tplName]
           setupTemplate(module.global.Hull.templates._default[tplName],  tplName)
     server:
       require: (tplName, path, format)->
@@ -51,7 +51,7 @@
         , (err)->
           console.error "Error loading template", tplName, err.message
           dfd.reject err
-        dfd
+        dfd.promise

   _execute = (type, args...)->
     for stratName in strategies[type]


Ao selecionar a dimensão única, trigger 'change'

--- src/coffee/portal-sku-selector.coffee
+++ src/coffee/portal-sku-selector.coffee
@@ -219,8 +219,8 @@
 			@selectDimensionValue(dimension.name, sku.dimensions[dimension.name])

 	selectDimensionValue: (dimensionName, valueName) =>
-		@finditemDimensionValueInput(dimensionName, valueName).prop('checked', true)
-		@finditemDimensionOption(dimensionName).val(valueName)
+		@finditemDimensionValueInput(dimensionName, valueName).prop('checked', true).trigger('change')
+		@finditemDimensionOption(dimensionName).val(valueName).trigger('change')

 	findSelectionStatus: (selection) =>
 		foundUnavailable = false


Fix fetchPatchesWithStatus for non-versioned models

--- app/models/CocoModel.coffee
+++ app/models/CocoModel.coffee
@@ -426,25 +426,25 @@
     # language codes that are covered for every i18n object are fully covered
     overallCoverage = _.intersection(langCodeArrays...)
     @set('i18nCoverage', overallCoverage)
-
+
   saveNewMinorVersion: (attrs, options={}) ->
     options.url = @url() + '/new-version'
     options.type = 'POST'
     return @save(attrs, options)
-
+
   saveNewMajorVersion: (attrs, options={}) ->
     attrs = attrs or _.omit(@attributes, 'version')
     options.url = @url() + '/new-version'
     options.type = 'POST'
     options.patch = true # do not let version get sent along
     return @save(attrs, options)
-
+
   fetchPatchesWithStatus: (status='pending', options={}) ->
     Patches = require '../collections/Patches'
     patches = new Patches()
     options.data ?= {}
     options.data.status = status
-    options.url = @urlRoot + '/' + @get('original') + '/patches'
+    options.url = @urlRoot + '/' + (@get('original') or @id) + '/patches'
     patches.fetch(options)
     return patches


little cleanup, now indent-parsing is ready for a real-world test

--- test/tests/babel_bridge/indent_blocks2.coffee
+++ test/tests/babel_bridge/indent_blocks2.coffee
@@ -237,7 +237,7 @@
   class MyParser extends Parser
     @nodeBaseClass: IndentBlocksNode

-    @rule body: 'expression*'
+    @rule body: 'statement*'

     blockStartRegExp = /\n( +)/y

@@ -260,7 +260,9 @@
             p

     @rule _: / */
-    @rule expression: '/[a-z0-9A-Z]+/ end'
+
+    @rule statement: 'expression end'
+    @rule expression: '/[a-z0-9A-Z]+/'

     @rule end: 'blocks end'
     @rule end: '/\n|$/'


splitview: rewrite '_calculateSizes' method for sanity

Credit goes to @sinan

--- src/components/split/splitview.coffee
+++ src/components/split/splitview.coffee
@@ -124,23 +124,25 @@

     @_setMinsAndMaxs()

-    {sizes} = @getOptions()
-    ss      = @_getSize()
-    s       = []
-    s[0]    = @_getLegitPanelSize @_sanitizeSize(sizes[0]), 0
-    s[1]    = @_getLegitPanelSize @_sanitizeSize(sizes[1]), 1
-    st      = s[0] + s[1]
-
-    if st > ss
-      s[1] = ss - s[0]
-    else if st < ss
-      if sizes[0] and (not sizes[1] or sizes[1] is 'auto')
-        s[1] = ss - s[0]
-      else if sizes[1] and (not sizes[0] or sizes[0] is 'auto')
-        s[0] = ss - s[1]
-
-    @size  = ss
-    @sizes = s
+    {sizes}       = @getOptions()
+    givenSizes    = sizes
+    splitSize     = @_getSize()
+    legitSizes    = []
+    legitSizes[0] = @_getLegitPanelSize @_sanitizeSize(givenSizes[0]), 0
+    legitSizes[1] = @_getLegitPanelSize @_sanitizeSize(givenSizes[1]), 1
+
+    totalSize     = legitSizes[0] + legitSizes[1]
+
+    if totalSize > splitSize
+      legitSizes[1] = splitSize - legitSizes[0]
+    else if totalSize < splitSize
+      if givenSizes[0] and (not givenSizes[1] or givenSizes[1] is 'auto')
+        legitSizes[1] = splitSize - legitSizes[0]
+      else if givenSizes[1] and (not givenSizes[0] or givenSizes[0] is 'auto')
+        legitSizes[0] = splitSize - legitSizes[1]
+
+    @size  = splitSize
+    @sizes = legitSizes

   _sanitizeSize: (size) ->


Improve logging

Improve logging

--- server-ce/migrations/5_remove_holding_accounts.coffee
+++ server-ce/migrations/5_remove_holding_accounts.coffee
@@ -60,16 +60,17 @@
 			callback()

 	run: (done = () ->) ->
+		console.log "[Getting list of holding accounts]"
 		HoldingAccountMigration.findHoldingAccounts (error, users) ->
 			throw error if error?
-			console.log "[Got list of holding accounts]", users.map (u) -> u._id
+			console.log "[Got #{users.length} holding accounts]"
 			jobs = users.map (u) ->
 				(cb) ->
 					HoldingAccountMigration.deleteUser u._id, (error) ->
 						return cb(error) if error?
 						HoldingAccountMigration.deleteUserProjects u._id, (error) ->
 							return cb(error) if error?
-							setTimeout cb, 200 # Small delay to not hammer DB
+							setTimeout cb, 50 # Small delay to not hammer DB
 			async.series jobs, (error) ->
 				throw error if error?
 				console.log "[FINISHED]"


Firing and throwing now uses position prompt

--- src/player.coffee
+++ src/player.coffee
@@ -5,6 +5,7 @@

 {Stairs} = require './entities'
 direction = require './direction'
+vectorMath = require './vector-math'
 {whilst, arrayRemove} = require './util'
 prompts = game.prompts

@@ -136,9 +137,9 @@
 						3 * choices.length

 			when 'fire'
-				prompts.direction 'Fire in what direction?'
-
-				.then (dir) =>
+				prompts.position 'Fire where?', default: @creature
+				.then (pos) =>
+					offset = vectorMath.sub pos, @creature
 					item = @creature.equipment['right hand']

 					if not item?
@@ -151,7 +152,7 @@
 						"
 						2
 					else
-						Q item.fire @creature, dir
+						Q item.fire @creature, offset
 						.thenResolve 6

 			when 'attack'
@@ -169,11 +170,13 @@

 					{value: item} = choice

-					prompts.direction 'Throw in what direction?'
-					.then (dir) =>
-						return game.message 'Never mind.' if not dir?
-
-						Q @creature.throw item, dir
+					prompts.position 'Throw where?', default: @creature
+					.then (pos) =>
+						return game.message 'Never mind.' if not pos?
+
+						offset = vectorMath.sub pos, @creature
+
+						Q @creature.throw item, offset
 						.thenResolve 6

 			when 'test-dir'
@@ -226,7 +229,7 @@
 				game.renderer.effectLine @creature, target, time: 500, symbol: '*'

 			when 'test-pos'
-				prompts.position 'Test position!', default: {x: @creature.x, y: @creature.y}
+				prompts.position 'Test position!', default: @creature
 				.then (pos) ->
 					game.message "You picked position: #{pos.x},#{pos.y}" if pos?
 					game.message "Never mind." if not pos?


Cleaned up heartbeat.

Cleaned up heartbeat.

--- ext/user_heartbeat.coffee
+++ ext/user_heartbeat.coffee
@@ -15,30 +15,27 @@
   callback(message)

 refreshPresence = (token,client_id) ->
-  mongodb.collection 'users', (err, collection) =>
-    collection.findOne { auth_token: token }, (err,user) =>
+  mongodb.collection 'users', (err, collection) ->
+    collection.findOne { auth_token: token }, (err,user) ->
       if user
         setPresenceKeys(user,client_id,Date.now())

-        fayengine.clientExists client_id, (connected,score) =>
+        fayengine.clientExists client_id, (connected,score) ->
           if connected
             setTimeout ->
               msg = { status: "online" }
-              broadcastOnClouds(user,msg,user.cloud_ids) if user
-              if token
-                setTimeout ->
-                  refreshPresence(token,client_id)
-                , 24000
+              broadcastOnClouds(user,msg,user.cloud_ids)
+              setTimeout ->
+                refreshPresence(token,client_id)
+              , 24000
             , 6000
           else
-            if user
-              last_seen = rediscli.get "cloudsdale/users/#{user._id}"
-              last_seen = 0 unless last_seen
-              if Date.now() > last_seen
-
-                deletePresenceKeys(user,client_id)
-                msg = { status: "offline" }
-                broadcastOnClouds(user,msg,user.cloud_ids)
+            last_seen = rediscli.get "cloudsdale/users/#{user._id}"
+            last_seen = 0 unless last_seen
+            if Date.now() > last_seen
+              deletePresenceKeys(user,client_id)
+              msg = { status: "offline" }
+              broadcastOnClouds(user,msg,user.cloud_ids)

 setPresenceKeys = (user,client_id,time) ->
   user_id   = user._id


Added "valueChanged" trigger for FieldView.

--- lib/ST/FieldView.coffee
+++ lib/ST/FieldView.coffee
@@ -54,4 +54,7 @@
       @_changed 'value', oldValue, newValue

   @method '_idChanged', (oldValue, newValue) ->
-    @_inputElement.attr 'id', newValue if @_loaded+    @_inputElement.attr 'id', newValue if @_loaded
+
+  @method '_valueChanged', (oldValue, newValue) ->
+    @trigger 'valueChanged', newValue


Run all the lights, not just one

Run all the lights, not just one

--- bin/simulatePortals.coffee
+++ bin/simulatePortals.coffee
@@ -2,6 +2,8 @@
 #// vim: set filetype=coffee:
 noflo = require 'noflo'
 path = require 'path'
+fs = require 'fs'
+portals = JSON.parse fs.readFileSync path.resolve(__dirname, '../portals.json'), 'utf-8'

 loader = new noflo.ComponentLoader path.resolve __dirname, '../'
 rgb2hex = ([red, green, blue]) ->
@@ -41,19 +43,24 @@
     val = "[#{id}, \"#{rgb2hex(color)}\"]"
     pixel.send val
     console.log "Sending #{val}"
+
+  setPortals = (color) ->
+    for portal,idx in portals.portals
+      continue unless portal
+      setPortal idx, color
     show.send true

-  console.log 'c-base blue'
-  setPortal 37, [0, 0, 64]
+  console.log 'portals blue'
+  setPortals [0, 0, 64]
   setTimeout ->
-    console.log 'c-base green'
-    setPortal 37, [0, 64, 0]
+    console.log 'portals green'
+    setPortals [0, 64, 0]
     setTimeout ->
-      console.log 'c-base red'
-      setPortal 37, [96, 0, 0]
+      console.log 'portals red'
+      setPortals [96, 0, 0]
       setTimeout ->
-        console.log 'c-base white'
-        setPortal 37, [64, 64, 64]
+        console.log 'portals white'
+        setPortals [64, 64, 64]
         setTimeout ->
           console.log "DONE"
           process.exit 0


Passing specs

Passing specs

--- components/flash/test/index.coffee
+++ components/flash/test/index.coffee
@@ -12,12 +12,13 @@
       Backbone.$ = $
       $.support.transition = end: 'transitionend'
       $.fn.emulateTransitionEnd = -> @trigger $.support.transition.end
-      @startTimerStub = sinon.stub FlashMessage::, 'startTimer'
+      @sandbox = sinon.sandbox.create()
+      @startTimerStub = @sandbox.stub FlashMessage::, 'startTimer'
       benv.render resolve(__dirname, '../template.jade'), {}, =>
         done()

   afterEach ->
-    @startTimerStub.restore()
+    @sandbox.restore()
     benv.teardown()

   describe '#initialize', ->
@@ -60,7 +61,7 @@
       @flash = new FlashMessage message: 'Goodbye world.'

     it 'removes the flash message when clicked, leaving the container empty', (done) ->
-      @flash.$el.on 'transitionend', =>
+      @flash.$el.on 'transitionend', ->
         _.defer ->
           $('body').html().should.equal '

'
           done()
@@ -75,17 +76,18 @@
       @flash.update 'Hello world.'
       $('.fullscreen-flash-message').text().should.equal 'Hello world.'

+  describe 'XSS', ->
+    it 'escapes HTML', ->
+      flash = new FlashMessage message: '>'
+      $('body').text().should.equal '>'
+
   describe '#open', ->
     it 'checks to see if the container is empty before starting the timer', ->
       firstFlash = new FlashMessage message: 'Goodbye world.'
       $('body').text().should.equal 'Goodbye world.'
       @startTimerStub.restore()
-      @startTimerStub = sinon.stub FlashMessage::, 'startTimer'
+      @startTimerStub = @sandbox.stub FlashMessage::, 'startTimer'
       secondFlash = new FlashMessage message: 'A caesura', autoclose: true
       secondFlash.startTimer.called.should.be.false
       $('body').text().should.equal 'Goodbye world.'

-  describe 'XSS', ->
-    it 'escapes HTML', ->
-      flash = new FlashMessage message: '>'
-      $('body').text().should.equal '>'


Item list/Menu/Select: Added mouse over the buttons to pre-activate them and also added some usability improvements. See #50925

--- src/elements/ItemList/ItemList.coffee
+++ src/elements/ItemList/ItemList.coffee
@@ -15,7 +15,7 @@
 		@append(@__body, "center")

 		if @_keyboardControl
-			@__initKeydownListener()
+			@__initListeners()

 	initOpts: ->
 		super()
@@ -284,7 +284,7 @@
 		super()
 		@__body?.destroy()

-	__initKeydownListener: ->
+	__initListeners: ->
 		@__keyboardKeys = []
 		preSelectByKeyword = =>
 			@__preSelectByKeyword(@__keyboardKeys.join(""))
@@ -302,11 +302,11 @@
 				switch keyboardKey
 					when "Down"
 						event.preventDefault()
-						@__preActivateNextItem()
+						@__preActivateNextButton()
 						break
 					when "Up"
 						event.preventDefault()
-						@__preActivatePreviousItem()
+						@__preActivatePreviousButton()
 						break
 					when "Return"
 						@__activatePreSelected(event)
@@ -317,59 +317,90 @@
 							CUI.scheduleCallback(ms: 200, call: preSelectByKeyword)
 				return

+		CUI.Events.listen
+			type: "mouseover"
+			node: @DOM
+			call: (ev) =>
+				item = CUI.dom.parents(ev.getTarget(), ".cui-button")[0]
+				if CUI.dom.hasClass(item, CUI.defaults.class.Button.defaults.active_css_class)
+					return
+
+				index = @__getButtonIndex(item)
+				if not CUI.util.isNull(index)
+					@__preActivateButtonByIndex(index)
+				return
+
+		return
+
 	__preSelectByKeyword: (keyword) ->
 		elementMatches = (element) =>
 			(element instanceof CUI.Button or element instanceof CUI.Label) and element.getText()?.startsWithIgnoreCase(keyword)

 		nextIndex = @__preActiveIndex + 1
-		nextElement = @__getItemByIndex(nextIndex)
+		nextElement = @__getButtonByIndex(nextIndex)
 		if elementMatches(nextElement)
-			@__preActivateItemByIndex(nextIndex)
+			@__preActivateButtonByIndex(nextIndex)
 			return

 		for item, index in @__body?.DOM.children
 			element = CUI.dom.data(item, "element")
 			if elementMatches(element)
-				@__preActivateItemByIndex(index)
+				@__preActivateButtonByIndex(index)
 				break
 		return

 	__activatePreSelected: (event) ->
-		itemToActivate = @__getItemByIndex(@__preActiveIndex)
+		itemToActivate = @__getButtonByIndex(@__preActiveIndex)
 		if itemToActivate instanceof CUI.Button
 			itemToActivate.onClickAction(event)
 			CUI.dom.removeClass(itemToActivate, CUI.defaults.class.Button.defaults.active_css_class)

-	__preActivateNextItem: ->
-		@__preActivateItemByIndex(@__preActiveIndex + 1)
-
-	__preActivatePreviousItem: ->
-		@__preActivateItemByIndex(@__preActiveIndex - 1)
-
-	__preActivateItemByIndex: (newPreActiveIndex) ->
-		itemToPreActivate = @__getItemByIndex(newPreActiveIndex)
-		if itemToPreActivate instanceof CUI.Button
+	__preActivateNextButton: ->
+		if CUI.util.isNull(@__preActiveIndex)
+			index = 0
+		else
+			index = @__preActiveIndex + 1
+		@__preActivateButtonByIndex(index)
+
+	__preActivatePreviousButton: ->
+		if CUI.util.isNull(@__preActiveIndex)
+			items = @__getButtonItems()
+			index = items.length - 1
+		else
+			index = @__preActiveIndex - 1
+		@__preActivateButtonByIndex(index)
+
+	__preActivateButtonByIndex: (newPreActiveIndex) ->
+		buttonToPreActivate = @__getButtonByIndex(newPreActiveIndex)
+		if buttonToPreActivate instanceof CUI.Button
 			@__deselectPreActivated()
 			@__preActiveIndex = newPreActiveIndex
-			CUI.dom.addClass(itemToPreActivate, CUI.defaults.class.Button.defaults.active_css_class)
-			CUI.dom.scrollIntoView(itemToPreActivate)
+			CUI.dom.addClass(buttonToPreActivate, CUI.defaults.class.Button.defaults.active_css_class)
+			CUI.dom.scrollIntoView(buttonToPreActivate)

 	__deselectPreActivated: ->
-		previousItemSelected = @__getItemByIndex(@__preActiveIndex)
+		previousItemSelected = @__getButtonByIndex(@__preActiveIndex)
 		if previousItemSelected instanceof CUI.Button
 			CUI.dom.removeClass(previousItemSelected, CUI.defaults.class.Button.defaults.active_css_class)

-	__getItemByIndex: (index) ->
+	__getButtonByIndex: (index) ->
 		if not @__body or CUI.util.isNull(index) or index < 0
 			return

-		children = Array::filter.call(@__body.DOM.children, (item) =>
+		item = @__getButtonItems()[index]
+		return CUI.dom.data(item, "element")
+
+	__getButtonIndex: (item) ->
+		for itemButton, index in @__getButtonItems()
+			if item == itemButton
+				return index
+		return
+
+	__getButtonItems: ->
+		return Array::filter.call(@__body.DOM.children, (item) =>
 			element = CUI.dom.data(item, "element")
-			return element instanceof CUI.Button or element instanceof CUI.DataField or element instanceof CUI.Label
+			return element instanceof CUI.Button
 		)
-
-		item = children[index]
-		return CUI.dom.data(item, "element")

 	__initPreActiveIndex: ->
 		if @__active_idx
@@ -378,8 +409,6 @@
 		for item, index in @__body.DOM.children
 			if CUI.dom.hasClass(item, CUI.defaults.class.Button.defaults.active_css_class)
 				return index
-
-		return -1

 CUI.ready =>
 	CUI.Events.registerEvent


Leave do_classification.default_value untouched so that it is passed through correctly

--- src/ext/components/model-input.coffee
+++ src/ext/components/model-input.coffee
@@ -32,20 +32,6 @@
   control = createControl 'dropdown', parameter
   control.values = signals parameter.values
   control.value = _value
-  control.defaultValue = parameter.default_value
-  control
-
-createListControl__old = (parameter) ->
-  _value = signal parameter.actual_value or []
-  _selection = lift _value, (items) ->
-    caption = "#{Flow.Util.describeCount items.length, 'column'} selected"
-    caption += ": #{items.join ', '}" if items.length > 0
-    "(#{caption})"
-
-  control = createControl 'list', parameter
-  control.values = signals parameter.values
-  control.value = _value
-  control.selection = _selection
   control.defaultValue = parameter.default_value
   control

@@ -267,7 +253,7 @@
     #
     classificationParameter = findParameter parameters, 'do_classification'
     if classificationParameter
-      classificationParameter.actual_value = classificationParameter.default_value = "true"
+      classificationParameter.actual_value = "true"

     # Fetch frame list; pick column names from training frame
     _.requestFrames (error, frames) ->


Disable colour highlighting

Disable colour highlighting

--- lib/app.coffee
+++ lib/app.coffee
@@ -39,6 +39,14 @@
 	description: 'project path'
 	alias: 'p'

+# We don't do anything in response to this options
+# explicitly. We use InquirerJS to provide CLI widgets,
+# and that module understands --no-color automatically.
+capitano.globalOption
+	signature: 'no-color'
+	description: 'disable colour highlighting'
+	boolean: true
+
 yesOption =
 	signature: 'yes'
 	description: 'confirm non interactively'


Improve overall experience during formatting.

This commit introduces several changes (Yeah, I know...):

1. Use child_process.execSync to call clang-format.
   This ensures that save waits for us.

2. Use TextBuffer#setTextViaDiff to set the formatted text.
   This reduces re-rendering and thus avoids flickering.

3. Format text from buffer not from file.
   This allows to format unsaved files.

--- lib/clang-format.coffee
+++ lib/clang-format.coffee
@@ -1,57 +1,67 @@
-exec = require('child_process').exec
-path = require('path')
+{CompositeDisposable} = require 'atom'
+{execSync} = require 'child_process'

 module.exports =
 class ClangFormat
   constructor: (state) ->
-    atom.workspace.observeTextEditors (editor) =>
+    @subscriptions = new CompositeDisposable
+    @subscriptions.add atom.workspace.observeTextEditors (editor) =>
       @handleBufferEvents(editor)

-    @commands = atom.commands.add 'atom-workspace',
-     'clang-format:format', =>
+    @subscriptions.add atom.commands.add 'atom-workspace',
+      'clang-format:format', =>
         editor = atom.workspace.getActiveTextEditor()
         if editor
           @format(editor)

   destroy: ->
-    @commands.dispose()
-    atom.unsubscribe(atom.project)
+    @subscriptions.dispose()

   handleBufferEvents: (editor) ->
-    editor.onDidSave =>
+    buffer = editor.getBuffer()
+    bufferSavedSubscription = buffer.onWillSave =>
       scope = editor.getRootScopeDescriptor().scopes[0]
       if @shouldFormatOnSaveForScope scope
-        @format(editor)
+        buffer.transact => @format(editor)

+    editorDestroyedSubscription = editor.onDidDestroy =>
+      bufferSavedSubscription.dispose()
+      editorDestroyedSubscription.dispose()
+
+      @subscriptions.remove(bufferSavedSubscription)
+      @subscriptions.remove(editorDestroyedSubscription)
+
+    @subscriptions.add(bufferSavedSubscription)
+    @subscriptions.add(editorDestroyedSubscription)

   format: (editor) ->
-    if editor and editor.getPath()
-      exe = atom.config.get('clang-format.executable')
-      style = atom.config.get('clang-format.style')
-      cursor = @getCurrentCursorPosition(editor)
+    buffer = editor.getBuffer()

-      command = exe + ' -cursor=' + cursor.toString() +
-                       ' -style=' + style
+    exe = atom.config.get('clang-format.executable')
+    options =
+      style: atom.config.get('clang-format.style')
+      cursor: @getCurrentCursorPosition(editor).toString()

-      if (@textSelected(editor))
-        command += ' -lines=' + @getTargetLineNums(editor)
+    # Format only selection
+    if @textSelected(editor)
+      options.lines = @getTargetLineNums(editor)

-      file_path = editor.getPath()
-      working_dir = path.dirname(file_path)
-      child = exec command, {cwd: working_dir}, (err, stdout, stderr) =>
-        if err
-          console.log(err)
-          console.log(stdout)
-          console.log(stderr)
-        else
-          editor.setText(@getReturnedFormattedText(stdout))
-          returnedCursorPos = @getReturnedCursorPosition(stdout)
-          convertedCursorPos = editor.getBuffer().positionForCharacterIndex(returnedCursorPos)
-          editor.setCursorBufferPosition(convertedCursorPos)
+    # Pass file path to clang-format so it can look for .clang-format files
+    if file_path = editor.getPath()
+      options['assume-filename'] = file_path

-      child.stdin.write(editor.getText())
-      child.stdin.end()
+    # Call clang-format synchronously to ensure that save waits for us
+    # Don't catch errors to make them visible to users via atom's UI
+    args = ("-#{k}=#{v}" for k, v of options).join ' '
+    stdout = execSync("#{exe} #{args}", input: editor.getText()).toString()

+    # Update buffer with formatted text. setTextViaDiff minimizes re-rendering
+    buffer.setTextViaDiff @getReturnedFormattedText(stdout)
+
+    # Restore cursor position
+    returnedCursorPos = @getReturnedCursorPosition(stdout)
+    convertedCursorPos = buffer.positionForCharacterIndex(returnedCursorPos)
+    editor.setCursorBufferPosition(convertedCursorPos)

   shouldFormatOnSaveForScope: (scope) ->
     if atom.config.get('clang-format.formatCPlusPlusOnSave') and scope in ['source.c++', 'source.cpp']


Define list of methods to debug

Define list of methods to debug

--- src/trix/core/utilities/debugger.coffee
+++ src/trix/core/utilities/debugger.coffee
@@ -1,13 +1,47 @@
 # This file is not included in the main Trix bundle and
 # should be explicitly required to enable the debugger.

-{tagName, findClosestElementFromNode, handleEvent} = Trix
+DEBUG_METHODS =
+  "Trix.AttachmentEditorController": "
+    didClickRemoveButton
+    uninstall
+  "

-buttonSelector = "button[data-action=debug]"
+  "Trix.DocumentController": "
+    didClickAttachment
+  "

-handleEvent "click", onElement: document, matchingSelector: buttonSelector, withCallback: (event) ->
-  setDebugInfo(element: event.target, focus: true)
-  event.preventDefault()
+  "Trix.EditorController": "
+    setEditor
+    loadDocument
+  "
+
+  "Trix.ImageAttachmentEditorController": "
+    startResize
+    resize
+    endResize
+  "
+
+  "Trix.InputController": "
+    elementDidMutate
+    events.keydown
+    events.keypress
+    events.dragstart
+    events.dragover
+    events.dragend
+    events.drop
+    events.cut
+    events.paste
+    events.compositionstart
+    events.compositionend
+  "
+
+  "Trix.ToolbarController": "
+    didClickActionButton
+    didClickAttributeButton
+    didClickDialogButton
+    didKeyDownDialogInput
+  "

 TrixToolbarElement::constructor.defaultHTML += """
   <span class="button_group">
@@ -46,6 +80,15 @@
     border: none;
   }
 """
+
+
+{tagName, findClosestElementFromNode, handleEvent} = Trix
+
+buttonSelector = "button[data-action=debug]"
+
+handleEvent "click", onElement: document, matchingSelector: buttonSelector, withCallback: (event) ->
+  setDebugInfo(element: event.target, focus: true)
+  event.preventDefault()

 setDebugInfo = ({element, error, focus}) ->
   editorElement = findClosestElementFromNode(element, matchingSelector: "trix-editor")
@@ -98,9 +141,31 @@
       reportError(error)
   trixDebugWrapper

-wrapTrixMethodsWithErrorHandler = ->
-  for className, constructor of Trix when constructor.prototype instanceof Trix.BasicObject
-    for methodName, method of constructor.prototype when typeof method is "function" and methodName isnt "constructor"
-      constructor::[methodName] = wrapFunctionWithErrorHandler(method)
+installMethodDebugger = (className, methodName) ->
+  [objectName, constructorNames...] = className.split(".")
+  [propertyNames..., methodName] = methodName.split(".")

-wrapTrixMethodsWithErrorHandler()
+  object = @[objectName]
+  object = object[constructorName] for constructorName in constructorNames
+  object = object.prototype
+  object = object[propertyName] for propertyName in propertyNames
+
+  if typeof object?[methodName] is "function"
+    object[methodName] = wrapFunctionWithErrorHandler(object[methodName])
+  else
+    throw new Error "Can't install on non-function"
+
+install = ->
+  console.groupCollapsed("Trix debugger")
+
+  for className, methodNames of DEBUG_METHODS
+    for methodName in methodNames.split(/\s/)
+      try
+        installMethodDebugger(className, methodName)
+        console.log "✓ #{className}##{methodName}"
+      catch error
+        console.warn "✗ #{className}##{methodName}:", error.message
+
+  console.groupEnd()
+
+install()


move chat input state to store so that it is preserved on tab hide/show

--- salvus/page/editor_chat.cjsx
+++ salvus/page/editor_chat.cjsx
@@ -73,12 +73,16 @@
                 date: new Date()
         @syncdb.save()

+    set_input: (input) =>
+        @_set_to(input:input)
+
 class ChatStore extends Store
     _init: (flux) =>
         ActionIds = flux.getActionIds(@name)
         @register(ActionIds._set_to, @setState)
         @state =
             messages : immutable.fromJS({})
+            input    : ''

 # boilerplate setting up actions, stores, sync'd file, etc.
 syncdbs = {}
@@ -211,6 +215,7 @@
         flux        : rtypes.object
         name        : rtypes.string.isRequired
         account_id  : rtypes.string
+        input       : rtypes.string
         project_id  : rtypes.string.isRequired
         file_use_id : rtypes.string.isRequired
         file_use    : rtypes.object
@@ -230,7 +235,7 @@
             e.preventDefault()

     clear_input: ->
-        React.findDOMNode(@refs.input).children[0].value = ""
+        @props.flux.getActions(@props.name).set_input('')

     render_input: ->
         tip =
@@ -243,7 +248,10 @@
                 rows      = 4
                 type      = 'textarea'
                 ref       = 'input'
-                onKeyDown = {@keydown} />
+                onKeyDown = {@keydown}
+                value     = {@props.input}
+                onChange  = {(value)=>@props.flux.getActions(@props.name).set_input(@refs.input.getValue())}
+                />

                 <Tip title='Use Markdown' tip={tip}>
                     Shift+Enter for newline.
@@ -337,6 +345,7 @@
     file_use_id = require('schema').client_db.sha1(project_id, path)
     connect_to =
         messages   : name
+        input      : name
         user_map   :'users'
         account_id : 'account'
         file_use   : 'file_use'


project_server -- make it so containing path is automatically created when writing file.

--- salvus/project_server.coffee
+++ salvus/project_server.coffee
@@ -896,6 +896,29 @@
             socket.write_mesg 'json', message.error(id:mesg.id, error:err)
     )

+# Make sure that that the directory containing the file indicated by the path exists
+# and has the right permissions.
+ensure_containing_directory_exists = (project_id, uid, path, cb) ->   # cb(err)
+    dir = misc.path_split(path).head
+
+    fs.exists dir, (exists) ->
+        if exists
+            cb()
+        else
+            async.series([
+                (cb) ->
+                    # Some extra paranoia...
+                    p = userpath(project_id)
+                    if dir.slice(0, p.length) != p
+                        cb("Path '#{dir}' must be in home directory '#{p}'")
+                    else
+                        cb()
+                (cb) ->
+                    fs.mkdir(dir, 0o700, cb)
+                (cb) ->
+                    fs.chown(dir, uid, uid, cb)
+            ], cb)
+
 # Write a file to the project
 events.write_file_to_project = (socket, mesg) ->
     data_uuid = mesg.data_uuid
@@ -914,10 +937,8 @@
                         if err
                             c(err)
                         else
-                            mesg.path = realpath
+                            path = realpath
                             c()
-                (c) ->
-                    fs.writeFile(path, value.blob, c)
                 (c) ->
                     getuid user, (err, id) ->
                         if err
@@ -925,6 +946,10 @@
                         else
                             uid = id
                             c()
+                (c) ->
+                    ensure_containing_directory_exists(mesg.project_id, uid, path, c)
+                (c) ->
+                    fs.writeFile(path, value.blob, c)
                 # Set the permissions on the file to the correct user (instead of root)
                 (c) ->
                     fs.chown(path, uid, uid, c)


database defaults: working

database defaults: working

--- salvus/rethink_shared.coffee
+++ salvus/rethink_shared.coffee
@@ -24,15 +24,15 @@
         primary_key: 'id'
         anonymous : true   # allow user access, even if not signed in
         fields:
-            id : true
-            timestamp : true
-            accounts : true
-            projects : true
-            active_projects : true
-            last_day_projects : true
-            last_week_projects : true
+            id                  : true
+            timestamp           : true
+            accounts            : true
+            projects            : true
+            active_projects     : true
+            last_day_projects   : true
+            last_week_projects  : true
             last_month_projects : true
-            hub_servers : true
+            hub_servers         : true
         indexes:
             timestamp : []
         user_query:
@@ -106,22 +106,60 @@
                     description : true
     accounts:
         primary_key : 'account_id'
+        fields :
+            account_id      : null
+            email_address   : null
+            editor_settings :
+                strip_trailing_whitespace : false
+                show_trailing_whitespace  : true
+                line_wrapping             : true
+                line_numbers              : true
+                smart_indent              : true
+                electric_chars            : true
+                match_brackets            : true
+                auto_close_brackets       : true
+                code_folding              : true
+                match_xml_tags            : true
+                auto_close_xml_tags       : true
+                spaces_instead_of_tabs    : true
+                multiple_cursors          : true
+                track_revisions           : true
+                extra_button_bar          : true
+                first_line_number         : 1
+                indent_unit               : 4
+                tab_size                  : 4
+                bindings                  : "standard"
+                theme                     : "standard"
+                undo_depth                : 300
+            other_settings  :
+                confirm_close             : false
+                mask_files                : true
+                default_file_sort         : 'time'
+            first_name      : ''
+            last_name       : ''
+            terminal        :
+                font_size                 : 14
+                color_scheme              : 'default'
+                font                      : 'monospace'
+            autosave        : 45
+            evaluate_key    : 'Shift-Enter'
+            passports       : []
         user_query :
             get :
                 all :
                     cmd  : 'getAll'
                     args : ['account_id']
                 fields :
-                    account_id : true
-                    email_address : true
+                    account_id      : true
+                    email_address   : true
                     editor_settings : true
-                    other_settings : true
-                    first_name : true
-                    last_name : true
-                    terminal  : true
-                    autosave  : true
-                    evaluate_key : true
-                    passports : true
+                    other_settings  : true
+                    first_name      : true
+                    last_name       : true
+                    terminal        : true
+                    autosave        : true
+                    evaluate_key    : true
+                    passports       : true
             set :
                 all :
                     cmd  : 'getAll'
@@ -135,3 +173,4 @@
                     autosave  : true
                     evaluate_key : true

+


Update fias-calculator.coffee

Update fias-calculator.coffee

--- src/fias-calculator.coffee
+++ src/fias-calculator.coffee
@@ -103,8 +103,8 @@
   messages = [
     [
       "Equivalent to a single partial",
-        "A small handful of views",
-        "It's everywhere, like the plague",
+      "A small handful of views",
+      "It's everywhere, like the plague",
     ]
     [
       "Nothing - it's straight forward",
@@ -112,7 +112,7 @@
       "It's freaking greek",
     ]
     [
-      "Dude, it's a copy change",
+      "C'mon - it's a copy change",
       "I could see some potential for problems, but they seem unlikely to have humongous impact",
       "I can imagine about 400 ways this blows up",
     ]


Add possibility to setup custom align

Add possibility to setup custom align

--- lib/Default.coffee
+++ lib/Default.coffee
@@ -27,7 +27,7 @@

   outputMessage: (message) -> message
   outputType: (type) ->
-    align = if @align then "\t" else " "
+    align = if @align then @align else " "
     if @keyword
       type = if @keyword is CONST.SYMBOL_KEYWORD then @types[type].symbol else @keyword
     "#{type}#{align}"
@@ -81,7 +81,7 @@

   keyword: null
   diff: false
-  align: true
+  align: "\t"
   color: true

   level: CONST.UNMUTED


esl CallServer

esl CallServer

--- lib/esl.coffee
+++ lib/esl.coffee
@@ -400,3 +400,68 @@
 #
 #  to start receiving event notifications.

+#### CallServer: a higher-level interface
+#
+# This interface is based on my
+# [prepaid code](http://stephane.shimaore.net/git/?p=ccnq3.git;a=blob;f=applications/prepaid/)
+# and handles the nitty-gritty of setting up the server properly.
+
+exports.CallServer = class CallServer extends require('events').EventEmitter
+
+  constructor: (port): ->
+
+    Unique_ID = 'Unique-ID'
+
+    server = new eslServer (res) ->
+      res.connect (req,res) =>
+        # Channel data
+        channel_data = req.body
+        # UUID
+        unique_id = channel_data[Unique_ID]
+
+        # Clean-up at the end of the connection.
+        res.on 'esl_disconnect_notice', (req,res) =>
+           util.log "Receiving disconnection"
+           switch req.headers['Content-Disposition']
+             when 'linger'      then res.exit()
+             when 'disconnect'  then res.end()
+
+        # Use this from your code to force a disconnection.
+        server.force_disconnect = (res) ->
+          util.log 'Hangup call'
+          res.bgapi "uuid_kill #{unique_id}"
+
+        # Translate channel events into server events.
+        res.on 'esl_event', (req,res) =>
+          req.channel_data = channel_data
+          req.unique_id = unique_id
+          server.emit req.body['Event-Name'], req, res
+
+        # Handle the incoming connection
+        res.linger (req,res) ->
+          res.filter Unique_ID, unique_id, (req,res) ->
+            res.event_json 'ALL', (req,res) ->
+              req.channel_data = channel_data
+              req.unique_id = unique_id
+              server.emit 'CONNECT', req, res
+
+    # Start the server.
+    server.listen port
+    return server
+
+#### Usage:
+#
+#     server = new esl.CallServer (port)
+#
+#     server.on 'CONNECT', (req,res) ->
+#       # Start processing the call
+#       # Channel data is available as req.channel_data
+#       # For example:
+#       uri = req.channel_data.variable_sip_req_uri
+#
+#     # Other FreeSwitch channel events are available as well:
+#     server.on 'CHANNEL_ANSWER', (req,res) ->
+#       util.log 'Call was answered'
+#     server.on 'CHANNEL_HANGUP_COMPLETE', (req,res) ->
+#       # Call was disconnected.
+#


more precise daysBetween()

more precise daysBetween()

--- src/github-calendar.coffee
+++ src/github-calendar.coffee
@@ -12,7 +12,15 @@
 	Math.ceil( ( date - firstDayOfThisYear ) / 1000 / 60 / 60 / 24 / 7 )

 daysBetween = (date1, date2)->
-	ms = Math.abs( date1 - date2 )
+	d1 = new Date( date1.getTime() )
+	d1.setHours 0
+	d1.setMinutes 0
+	d1.setSeconds 0
+	d2 = new Date( date2.getTime() )
+	d2.setHours 0
+	d2.setMinutes 0
+	d2.setSeconds 0
+	ms = Math.abs( d1 - d2 )
 	floor ms / 1000 / 60 / 60 / 24

 isSameDay = (date1, date2)->
@@ -40,11 +48,13 @@

 $.get( GITHUB_USER_URL + '/' + user + '/events?page=' + page).done( (events)->
 	events.forEach (e)->
+		daysBetween
 		# console.log e
 		commitsLength = if e.payload.commits then e.payload.commits.length else 0
 		created_at = new Date(e.created_at)

 		# model.push { created_at: created_at, commitsLength: commitsLength }
+
 		grid = $('<div>').html( e.type+' '+e.created_at+' '+e.repo.name+' commits:'+commitsLength)
 		$('#github-calendar > .content').append grid


Fix sorting bug in API

Fix sorting bug in API

--- lib/nbi.coffee
+++ lib/nbi.coffee
@@ -385,9 +385,16 @@
           sort = {}
           for k,v of s
             if aliases[k]?
-              sort[a] = v for a in aliases[k]
+              for a in aliases[k]
+                if a[a.lastIndexOf('.')+1] != '_' and collectionName is 'devices'
+                  sort["#{a}._value"] = v
+                else
+                  sort[a] = v
             else
-              sort[k] = v
+              if k[k.lastIndexOf('.') + 1] != '_' and collectionName is 'devices'
+                sort["#{k}._value"] = v
+              else
+                sort[k] = v
           cur.sort(sort)

         cur.skip(parseInt(urlParts.query.skip)) if urlParts.query.skip?


update out of date coffe version of simple_sample

--- samples/simple_sample.coffee
+++ samples/simple_sample.coffee
@@ -2,25 +2,28 @@
 {$div, $a} = Appify.templates

 Appify (app) ->
-	app.route 'index', (details, render) ->
-		render 'index'
+	app.route (next, details) ->
+		next details

-	app.route 'other', (details, render) ->
-		render 'other'
+	app.route 'index', (render, details) ->
+		render 'index', details

-	app.renderer ($outlet) ->
+	app.route 'other', (render, details) ->
+		render 'other', details
+
+	app.renderer ($outlet, details) ->
 		$div(
-			$outlet()
+			$outlet details
 		).class 'application'

-	app.renderer 'index', ->
+	app.renderer 'index', (details) ->
 		$div(
 			$div 'Home Page'
 			$a('Go to Other Page').on 'click', ->
 				app.transitionTo 'other'
 		).class 'home'

-	app.renderer 'other', ->
+	app.renderer 'other', (details) ->
 		$div(
 			$div 'Other Page'
 			$a('Go to Home Page').on 'click', ->


Switch to map

Switch to map

--- lib/formats.coffee
+++ lib/formats.coffee
@@ -286,9 +286,9 @@

 postprocessAtMentions = (text) ->
   users = Meteor.users.find {}, fields: username: 1
-  .fetch()
+  .map (user) -> user.username
   return text unless 0 < users.length
-  users = (escapeRe user.username for user in users)
+  users = (escapeRe user for user in users)
   text.replace ///@(#{users.join '|'})\b///, (match, user) ->
     "@#{linkToAuthor (routeGroup?() ? wildGroup), user}"


Rewrote original flash loading code

Rewrote original flash loading code

--- lib/flash-loader.coffee
+++ lib/flash-loader.coffee
@@ -1,22 +1,18 @@
+{app} = require 'electron'
+path = require 'path-extra'
+fs = require 'fs-extra'

-pepperFlashData =
-  linux:
-    filename: 'libpepflashplayer.so'
-    version: '20.0.0.248'
-  win32:
-    filename: 'pepflashplayer.dll'
-    version: '20.0.0.248'
-  darwin:
-    filename: 'PepperFlashPlayer.plugin'
-    version: '20.0.0.248'
-
-pepperFlashData = pepperFlashData[process.platform]
-innerFlashPath = path.join 'PepperFlash', process.platform, pepperFlashData.filename
-defaultFlashPath = path.join EXECROOT, innerFlashPath
+platform = process.platform
+filename = switch platform
+  when 'darwin' then 'PepperFlashPlayer.plugin'
+  when 'linux'  then 'libpepflashplayer.so'
+  when 'win32'  then 'pepflashplayer.dll'
+innerPath = path.join 'PepperFlash', platform, filename
 try
-  fs.accessSync defaultFlashPath
-  app.commandLine.appendSwitch 'ppapi-flash-path', defaultFlashPath
-  app.commandLine.appendSwitch 'ppapi-flash-version', pepperFlashData.version
+  flashPath = path.join EXECROOT, innerPath
+  fs.accessSync flashPath
 catch
-  app.commandLine.appendSwitch 'ppapi-flash-path', path.join(ROOT, innerFlashPath)
-  app.commandLine.appendSwitch 'ppapi-flash-version', pepperFlashData.version
+  flashPath = path.join ROOT, innerPath
+dbg.extra('flashLoader').log "Loading flash player from:"
+dbg.extra('flashLoader').log flashPath
+app.commandLine.appendSwitch 'ppapi-flash-path', flashPath


Ensure Chocolatey-Installed Go On Windows Is Found By Default

--- lib/goexecutable.coffee
+++ lib/goexecutable.coffee
@@ -69,6 +69,9 @@

         # Binary Distribution
         executables.push path.normalize(path.join('C:','go', 'bin', 'go.exe'))
+
+        # Chocolatey
+        executables.push path.normalize(path.join('C:', 'tools', 'go', 'bin', 'go.exe'))

     # De-duplicate entries
     executables = _.uniq(executables)


Search markup added

Search markup added

--- src/server/views/index.coffee
+++ src/server/views/index.coffee
@@ -7,11 +7,24 @@
         link rel: 'stylesheet', href: '/css/app.css'
         script src: '/js/jquery.js'
         script src: '/js/bootstrap.js'
+        script src: '/js/moments.js'
         script src: '/js/app.js'
         ie 'lt IE9', ->
             script src: 'https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js'
             script src: 'https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js'
     body ->
-        div '.container', ->
-           'welcome to app'
-        a href: '/auth', 'Vkontakte'
+
+      div class: 'logo', ->
+        img src: './img/logo.png'
+
+      a class: 'auth', href: '/auth', title: 'Войти через ВКонтакте', -> 'Войти'
+
+      form ->
+        div class: 'search', ->
+          input type: 'search'
+          input type: 'submit', value: 'Найти'
+
+      form ->
+        div class: 'fromDate', ->
+          input type: 'date'
+


whitespace

whitespace

--- lib/julia-client.coffee
+++ lib/julia-client.coffee
@@ -9,7 +9,7 @@
   runtime:    require './runtime'

   activate: (state) ->
-  	@requireInk =>
+    @requireInk =>
       commands.activate @
       x.activate() for x in [menu, @connection, @runtime]
       @ui.activate @connection.client
@@ -19,7 +19,7 @@
     else
       require('atom-package-deps').install('julia-client')
         .then  -> fn()
-        .catch ->
+        .catch ->
           atom.notifications.addError 'Installing the Ink package failed.',
             detail: 'Julia Client requires the Ink package to run.
                      Please install it manually from the settings view.'


:art: Respect statusIconPosition config while adding a status icon

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -118,7 +118,8 @@
     @_statusTiles.push statusBar.addLeftTile
       item: @_bottomTabProject,
       priority: -1000
-    @_statusTiles.push statusBar.addRightTile
+    statusIconPosition = atom.config.get('linter.statusIconPosition')
+    @_statusTiles.push statusBar["add#{statusIconPosition}Tile"]
       item: @_bottomStatus,
       priority: 999


object create bug fix

object create bug fix

--- lib/main.enchant.coffee
+++ lib/main.enchant.coffee
@@ -590,6 +590,10 @@
   worldview = if (param['worldview']?) then param['worldview'] else false
   touchEnabled = if (param['touchEnabled']?) then param['touchEnabled'] else true

+  tmp = __getNullObject()
+  if (tmp == -1)
+      return undefined
+
   if (motionObj == null)
     motionObj = undefined


I was too quick when refactoring that part

The mouse wheel events have to be send to the editor view.

--- lib/minimap-view.coffee
+++ lib/minimap-view.coffee
@@ -151,8 +151,11 @@

   mouseWheel: (e) =>
     return if @isClicked
-
-    @editorView.updateScroll()
+    {wheelDeltaX, wheelDeltaY} = e.originalEvent
+    if wheelDeltaX
+      @editorView.scrollLeft(@editorView.scrollLeft() - wheelDeltaX)
+    if wheelDeltaY
+      @editorView.scrollTop(@editorView.scrollTop() - wheelDeltaY)

   updateScroll: =>
     minimapHeight = @miniScrollView.outerHeight()


add activateOperatorPendingMode for consistency

--- lib/mode-manager.coffee
+++ lib/mode-manager.coffee
@@ -30,6 +30,8 @@
     else
       @mode is mode

+  # Event
+  # -------------------------
   onWillActivateMode: (fn) -> @emitter.on('will-activate-mode', fn)
   onDidActivateMode: (fn) -> @emitter.on('did-activate-mode', fn)
   onWillDeactivateMode: (fn) -> @emitter.on('will-deactivate-mode', fn)
@@ -52,6 +54,7 @@

     @deactivator = switch mode
       when 'normal' then @activateNormalMode()
+      when 'operator-pending' then @activateOperatorPendingMode()
       when 'insert' then @activateInsertMode(submode)
       when 'visual' then @activateVisualMode(submode)

@@ -81,7 +84,12 @@
     @editorElement.component?.setInputEnabled(false)
     new Disposable

-  # ActivateInsertMode
+  # Operator Pending
+  # -------------------------
+  activateOperatorPendingMode: ->
+    new Disposable
+
+  # Insert
   # -------------------------
   activateInsertMode: (submode=null) ->
     @editorElement.component.setInputEnabled(true)


Avoid odd state(=visual-mode but selection is empty) #282

--- lib/mode-manager.coffee
+++ lib/mode-manager.coffee
@@ -40,6 +40,9 @@
   #  Use this method to change mode, DONT use other direct method.
   # -------------------------
   activate: (mode, submode=null) ->
+    # Avoid odd state(=visual-mode but selection is empty)
+    return if (mode is 'visual') and @editor.isEmpty()
+
     @emitter.emit 'will-activate-mode', {mode, submode}

     if (mode is 'visual') and (submode is @submode)


Fix wrong regex

Fix wrong regex

--- lib/wanigui.coffee
+++ lib/wanigui.coffee
@@ -20,9 +20,9 @@
     rules = profile.stylesheet.split(/}/).reverse()
     for rule,i in rules
       rule = rule.replace /^[\s\n]+/g, ''
-      rule = rule.replace /^[\s\n]$/g, ''
+      rule = rule.replace /^[\s\n]+$/g, ''
       continue unless rule
-      rule += '}' unless i == rules.length - 1
+      rule += '}' unless rule.match /}$/
       sheet.insertRule rule,0

 Wanigui::Defaults = {}


new menu item to truncate log

new menu item to truncate log

--- library.coffee
+++ library.coffee
@@ -72,6 +72,10 @@
   flushLog()
   return

+truncateLog = ->
+  rows = logSheet.getMaxRows()
+  logSheet.deleteRows 1, rows - 1
+
 dump = ->
   Logger.log.apply Logger, arguments if verbose
   return
@@ -132,8 +136,10 @@
 initializeMenus = (e) ->
   namespace = properties.getProperty 'namespace'
   ui = SpreadsheetApp.getUi()
-  ui.createAddonMenu()
-  .addItem 'Log State', "#{namespace}.logState"
+  menu = ui.createAddonMenu()
+  menu.addItem 'Log State', "#{namespace}.logState"
+  .addToUi()
+  menu.addItem 'Truncate Log', "#{namespace}.truncateLog"
   .addToUi()


The drop marker should disappear when drag is done.

Previously we make drop marker disappear when drop is done, it would
case the marker to live forever if the drop is not done in Atom.

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -98,6 +98,8 @@

   onDragEnd: (event) =>
     @find(".is-dragging").removeClass 'is-dragging'
+    @children('.is-drop-target').removeClass 'is-drop-target'
+    @children('.drop-target-is-after').removeClass 'drop-target-is-after'

   onDragOver: (event) =>
     unless event.originalEvent.dataTransfer.getData('atom-event') is 'true'
@@ -125,8 +127,6 @@
       return

     event.stopPropagation()
-    @children('.is-drop-target').removeClass 'is-drop-target'
-    @children('.drop-target-is-after').removeClass 'drop-target-is-after'

     dataTransfer  = event.originalEvent.dataTransfer
     fromIndex     = parseInt(dataTransfer.getData('sortable-index'))


:bug: Shift + Scroll shouldn't switch tabs. Fixes https://github.com/atom/tabs/issues/139

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -299,6 +299,8 @@
       atom.focus()

   onMouseWheel: ({originalEvent}) =>
+    return if originalEvent.shiftKey
+
     @wheelDelta ?= 0
     @wheelDelta += originalEvent.wheelDelta


Remove try/catches now that Electron 0.36.8 is on stable

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -1,6 +1,5 @@
 BrowserWindow = null # Defer require until actually used
-# TODO: Remove the catch once Electron 0.35.0 is bundled in Atom
-try {ipcRenderer} = require 'electron' catch then ipcRenderer = require 'ipc'
+{ipcRenderer} = require 'electron'

 {matches, closest, indexOf} = require './html-helpers'
 {CompositeDisposable} = require 'atom'
@@ -389,10 +388,7 @@
       @removeEventListener 'mousewheel', @onMouseWheel

   browserWindowForId: (id) ->
-    try
-      BrowserWindow ?= require('electron').remote.BrowserWindow
-    catch # TODO: Remove once Electron 0.35.0 is bundled in Atom
-      BrowserWindow ?= require('remote').require('browser-window')
+    {BrowserWindow} ?= require('electron').remote

     BrowserWindow.fromId id


add a sample context menu to the ticker toolbar

--- lib/view_manager.coffee
+++ lib/view_manager.coffee
@@ -3,8 +3,11 @@
 { Panel } = require("sdk/panel")
 tabs = require("sdk/tabs")
 preferences = require("sdk/simple-prefs").prefs
+windowUtils = require('sdk/window/utils')
+{ identify } = require('sdk/ui/id')

 { Templater } = require("lib/templater")
+{ ContextMenu, MenuItem, SubMenu } = require('lib/contextmenu')

 # Manages the displayed items.
 # Communicates with @see View instances running in the context of the UI's frame.
@@ -63,6 +66,18 @@
   # Helper method to create the toolbar UI
   # @private
   createUI : =>
+    @menu = new ContextMenu([
+      new MenuItem('menu item 1'),
+      new SubMenu('menu item 2', [
+        new MenuItem('submenu 1 item 1', { checked: true }),
+        new MenuItem('submenu 1 item 2', { disabled: true }),
+        new MenuItem('submenu 1 item 3', { action: => console.log('item 3') })
+      ]),
+      new MenuItem('menu item 3 before separator'),
+      ContextMenu.Separator,
+      new MenuItem('menu item 4 after separator')
+    ]) unless @menu?
+
     @frame = Frame({
       url: './ticker.html'
       onMessage: @onReceiveMessage
@@ -103,6 +118,8 @@
   # Helper method to handle messages from a view.
   # @private
   onViewReady : (view, origin) =>
+    window = windowUtils.windows().find((window) => windowUtils.getOuterId(window) == view.ownerID)
+    @menu.attach('inner-' + identify(@toolbar), window)
     @update(view, origin)

   # Helper method to handle messages from a view.


fincm3.coffee: Set yocto version to thud

Signed-off-by: Andrei Gherzan <8dc7c09356db0b1762fb140617d7375c0a5a4131@balena.io>

--- fincm3.coffee
+++ fincm3.coffee
@@ -34,7 +34,7 @@
 		machine: 'fincm3'
 		image: 'resin-image'
 		fstype: 'resinos-img'
-		version: 'yocto-sumo'
+		version: 'yocto-thud'
 		deployArtifact: 'resin-image-fincm3.resinos-img'
 		compressed: true


Remove debug messages

Remove debug messages

--- src/server.coffee
+++ src/server.coffee
@@ -80,8 +80,6 @@
                     # Create user if not existing
                     user = new User({ name: senderEmail, email: senderEmail, subscriptions: [] }) unless user?
                     user.subscriptions.push label for label in subscribeLabels
-                    console.log subscribeLabels
-                    console.log user.subscriptions
                     user.save()

         if unsubscribeLabels?


Audit TCP/TLS silent failure fix

Audit TCP/TLS silent failure fix

--- src/server.coffee
+++ src/server.coffee
@@ -431,6 +431,10 @@
           # update message to remove length - add one extra character to remove the space
           message = message.substr(lengthIndex + 1)

+        if isNaN(length)
+          logger.info "[Auditing #{type}] No length supplied"
+          sock.destroy()
+
         # if sourced length equals message length then full message received
         if length == message.length
           logger.info "[Auditing #{type}] Received message from #{sock.remoteAddress}"


server save symbol tables

server save symbol tables

--- src/server.coffee
+++ src/server.coffee
@@ -19,8 +19,14 @@
         s.scan cls.Name, cls.Body
       cb s

+handleErr = (cb) ->
+  (err) ->
+    if err then cb err else cb 'success'
+
 module.exports = (client, creds, cb) ->
   s = new Server client, creds, () ->
+    s.fetcher.getSymbolTables (t) ->
+      m = { client: client, classes: t }
+      db.saveDoc m, handleErr(cb)
     s.scanAll (s) ->
-      db.saveDoc s, (err) ->
-        if err then cb err else cb 'success'
+      db.saveDoc s, handleErr(cb)