fail gracefully on Insufficient funds, or other deploy failures

--- lib/token.coffee
+++ lib/token.coffee
@@ -22,15 +22,16 @@
   CONTRACT_NAME = 'DynamicToken'

   @deployContract: ->
-    quiet = nodeEnv is 'test'
-    {output} = run "node_modules/.bin/truffle migrate --network #{nodeEnv} --reset --verbose-rpc", {quiet}
+    {output} = run "node_modules/.bin/truffle migrate --network #{nodeEnv}
+      --reset --verbose-rpc", relaxed: true, quiet: nodeEnv is 'test'
     pattern = ///#{CONTRACT_NAME}:\s(0x[0-9a-f]{40})///
     contractAddress = pattern.exec(output)?[1]
-    unless contractAddress
-      throw Promise.OperationalError "No contract address found in
-        output [[ #{output} ]] -- searched with pattern [[ #{pattern} ]]"
-    d {contractAddress}
-    contractAddress
+    return contractAddress if contractAddress
+    if output.search /Error: Insufficient funds/
+      throw Promise.OperationalError "Insufficient funds in account
+        #{config.from} -- full output: [[ #{output} ]]"
+    throw Promise.OperationalError "No contract address
+      pattern [[ #{pattern} ]] found in output [[ #{output} ]]"

   @loadContract: (contractAddress) ->
     TokenContract = require "../build/contracts/#{CONTRACT_NAME}.sol.js"


:art: extract getTokenizedLines as utility function

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -369,16 +369,23 @@
     editor.bufferRangeForBufferRow(row, includeNewline: true)
   rangeStart.union(rangeEnd)

-getScopesForBufferRow = (editor, row) ->
-  tokenizedLines = editor.displayBuffer.getTokenizedLines()
+getTokenizedLines = (editor) ->
+  editor.displayBuffer.getTokenizedLines()
+
+getScopesForTokenizedLine = (line) ->
+  for tag in line.tags when tag < 0 and (tag % 2 is -1)
+    atom.grammars.scopeForId(tag)
+
+isIncludeFunctionScopeForRow = (editor, row) ->
   # [FIXME] Bug of upstream?
   # Sometime tokenizedLines length is less than last buffer row.
   # So tokenizedLine is not accessible even if valid row.
   # In that case I simply return empty Array.
-  return [] unless tokenizedLine = tokenizedLines[row]
-
-  for tag in tokenizedLine.tags when tag < 0 and (tag % 2 is -1)
-    atom.grammars.scopeForId(tag)
+  if tokenizedLine = getTokenizedLines(editor)[row]
+    getScopesForTokenizedLine(tokenizedLine).some (scope) ->
+      isFunctionScope(editor, scope)
+  else
+    false

 # [FIXME] very rough state, need improvement.
 isFunctionScope = (editor, scope) ->
@@ -388,11 +395,6 @@
       /^entity\.name\.function/.test(scope)
     else
       /^meta\.function\./.test(scope)
-
-isIncludeFunctionScopeForRow = (editor, row) ->
-  scopes = getScopesForBufferRow(editor, row)
-  scopes.some (scope) ->
-    isFunctionScope(editor, scope)

 # Debugging purpose
 # -------------------------
@@ -466,7 +468,7 @@
   getBufferRangeForRowRange
   getFirstCharacterColumForBufferRow
   cursorIsAtFirstCharacter
-  getScopesForBufferRow
+  getTokenizedLines
   isFunctionScope
   isIncludeFunctionScopeForRow


tidied code

tidied code

--- src/core/Renderer.coffee
+++ src/core/Renderer.coffee
@@ -12,11 +12,9 @@
 		@tickCount = 0
 		@isRunning = yes
 		@pixelRatio = Bu.global.devicePixelRatio or 1
-		@dom = document.createElement 'canvas'
-		@context = @dom.getContext '2d'
 		@clipMeter = new ClipMeter() if ClipMeter?

-		# Recieve options
+		# Receive options
 		options = Bu.combineOptions arguments,
 			container: 'body'
 			background: '#eee'
@@ -25,31 +23,37 @@
 			showBounds: no
 			originAtCenter: no
 			imageSmoothing: yes
-		@[k] = options[k] for k in ['container', 'width', 'height', 'fps', 'showKeyPoints', 'showBounds']
-
-		# Set DOM styles
+
+		# Copy options
+		for name in ['container', 'width', 'height', 'fps', 'showKeyPoints', 'showBounds', 'originAtCenter']
+			@[name] = options[name]
+
+		# If options.width is not given, then fillParent is true
 		@fillParent = not Bu.isNumber options.width
-		if not @fillParent
-			@dom.style.width = @width + 'px'
-			@dom.style.height = @height + 'px'
-			@dom.width = @width * @pixelRatio
-			@dom.height = @height * @pixelRatio
+
+		# Convert width and height from dip(device independent pixels) to physical pixels
+		@width *= @pixelRatio
+		@height *= @pixelRatio
+
+		# Set canvas dom
+		@dom = document.createElement 'canvas'
 		@dom.style.cursor = options.cursor or 'default'
 		@dom.style.boxSizing = 'content-box'
 		@dom.style.background = options.background
 		@dom.oncontextmenu = -> false

-
+		# Set context
+		@context = @dom.getContext '2d'
+		@context.textBaseline = 'top'
+		@context.imageSmoothingEnabled = options.imageSmoothing
+
+		# Set container dom
 		@container = document.querySelector @container if Bu.isString @container
 		if @fillParent and @container == document.body
-			$('body').style('margin', 0).style('overflow', 'hidden');
+			$('body').style('margin', 0).style('overflow', 'hidden')
 			$('html, body').style('width', '100%').style('height', '100%')

-		@context.textBaseline = 'top'
-		@originAtCenter = options.originAtCenter
-		@context.imageSmoothingEnabled = options.imageSmoothing
-
-
+		# Set sizes for renderer property, dom attribute and dom style
 		onResize = =>
 			canvasRatio = @dom.height / @dom.width
 			containerRatio = @container.clientHeight / @container.clientWidth
@@ -65,31 +69,37 @@
 			@dom.style.height = height + 'px'
 			@render()

-		if @fillParent
+		if not @fillParent
+			@dom.style.width = (@width / @pixelRatio) + 'px'
+			@dom.style.height = (@height / @pixelRatio) + 'px'
+			@dom.width = @width
+			@dom.height = @height
+		else
 			@width = @container.clientWidth
 			@height = @container.clientHeight
 			Bu.global.window.addEventListener 'resize', onResize
 			@dom.addEventListener 'DOMNodeInserted', onResize

-
+		# Run the loop
 		tick = =>
 			if @isRunning
 				@clipMeter.start() if @clipMeter?
 				@render()
-				@trigger 'update', {'tickCount': @tickCount}
+				@trigger 'update', @
 				@tickCount += 1
 				@clipMeter.tick() if @clipMeter?
-
 			requestAnimationFrame tick
-
 		tick()

+		# Append <canvas> dom into the container
 		appendDom = =>
 			@container.appendChild @dom
 		setTimeout appendDom, 1

-		Bu.animationRunner?.hookUp @
-		Bu.dashFlowManager?.hookUp @
+		# Hook up with running components
+		Bu.animationRunner.hookUp @
+		Bu.dashFlowManager.hookUp @
+

 	# Pause/continue/toggle the rendering loop
 	pause: -> @isRunning = false


Quick fix, prevent logging be default

Quick fix, prevent logging be default

--- src/fiddles/sites.coffee
+++ src/fiddles/sites.coffee
@@ -46,7 +46,7 @@

 exports.middleware = () ->
   loadMiddlewares()
-  middlewares = [Mf.live_logger()]
+  middlewares = []
   if defMiddleware = siteMiddlewares['default']
     middlewares.push defMiddleware(Mf)


PremiumSupport: send client into homepage renderine

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -115,7 +115,7 @@
   JSession.fetchSession clientId, (err, session)->
     return next() if err or not session
     { maxAge, secure } = KONFIG.sessionCookie
-    res.cookie "clientId", session.clientId, { maxAge, secure }
+    res.cookie "clientId", session.clientId, { maxAge, secure }
     next()

 app.use (req, res, next) ->
@@ -334,11 +334,12 @@
           if models.last.bongo_?.constructorName isnt "JGroup" and not loggedIn
             return Crawler.crawl koding, req, res, name

-          homePageOptions = {section, account, bongoModels, isCustomPreview}
-          models.last.fetchHomepageView homePageOptions, (err, view)->
-            if err then next err
-            else if view? then res.send view
-            else res.send 404, error_404()
+          generateFakeClient req, res, (err, client)->
+            homePageOptions = {section, account, bongoModels, isCustomPreview, client}
+            models.last.fetchHomepageView homePageOptions, (err, view)->
+              if err then next err
+              else if view? then res.send view
+              else res.send 404, error_404()
         else next()

 # Main Handler for Koding.com


removed junk logs

removed junk logs

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -1,5 +1,3 @@
-console.log 'afafafa'
-
 {argv} = require 'optimist'

 console.log argv
@@ -8,8 +6,6 @@
 {webserver, mongo, mq, projectRoot, kites, uploads, basicAuth} = KONFIG

 webPort = argv.p ? webserver.port
-
-console.log 'sfsfsfsfsfs', webPort

 # processMonitor = (require 'processes-monitor').start
 #   name : "webServer on port #{webPort}"


Replace Q using method with Bluebird.promise

--- server/models/indicator.coffee
+++ server/models/indicator.coffee
@@ -150,17 +150,16 @@
     callback(err, rows)
   )

+
 indicatorSchema.methods.hasData = ->
-  deferred = Q.defer()
-
-  @getIndicatorData( (err, data) ->
-    if err?
-      deferred.reject(err)
-    else
-      deferred.resolve(data.length > 0)
-  )
-
-  return deferred.promise
+  new Promise((resolve, reject) =>
+    @getIndicatorData( (err, data) ->
+      if err?
+        reject(err)
+      else
+        resolve(data.length > 0)
+    )
+  )

 indicatorSchema.methods.getIndicatorData = (filters, callback) ->
   if arguments.length == 1


s/stop/stop_id/

--- src/hubot-nextbus.coffee
+++ src/hubot-nextbus.coffee
@@ -33,7 +33,7 @@
   robot.respond /(?:bus|nextbus) stop ([A-Z0-9]+)$/i, (msg) ->
     stop_id = msg.match[1]
     robot.logger.debug stop_id
-    queryStopById stop, msg
+    queryStopById stop_id, msg

   # get a list of nearby stops
   robot.respond /(?:bus|nextbus) stops$/i, (msg) ->


fixed title

fixed title

--- src/components/learning-guide-chart.coffee
+++ src/components/learning-guide-chart.coffee
@@ -101,8 +101,8 @@
       mainTitle = "Your Flight Path | #{guide.title} | All Topics | "
       backButtonText = "Back to Dashboard"
     else
-      mainTitle = "Your Flight Path | #{guide.children[0].title} | "
-      backButtonText = "Show All #{guide.title}"
+      mainTitle = "Your Flight Path | #{guide.children[chapter].title} | "
+      backButtonText = "Show All Chapters"
     wrap = container.append('g')
       .append('svg:text')
       .attr('text-anchor', 'middle')


not different calendar for before or past due

--- src/components/navbar/center-controls.cjsx
+++ src/components/navbar/center-controls.cjsx
@@ -74,7 +74,6 @@
     show: true
     assignment: task.title
     due: @reformatTaskDue(task.due_at)
-    isPastDue: TaskStore.isTaskPastDue(task.id)

   reformatTaskDue: (due_at) ->
     moment(due_at).calendar()
@@ -99,11 +98,11 @@
       to: 'viewTaskStepMilestones'

   render: ->
-    {show, assignment, due, isPastDue, hasMilestones} = @state
+    {show, assignment, due, hasMilestones} = @state
     return null unless show

     linkProps = _.pick @state, 'to', 'params'
-    calendarIconType = if isPastDue then 'calendar-times-o' else 'calendar-check-0'
+
     milestonesToggleClasses = classnames 'milestones-toggle',
       'active': hasMilestones

@@ -113,7 +112,7 @@
           {assignment}
         </span>

-        <Icon type={calendarIconType}
+        <Icon type='calendar-check-o'
           tooltipProps={placement: 'bottom'}
           tooltip={due} />
         <Router.Link


remove unnecessary .coffee exts

remove unnecessary .coffee exts

--- server/api/index.coffee
+++ server/api/index.coffee
@@ -5,7 +5,7 @@
 {filter, zip, merge} = require 'lodash'
 {makeExecutableSchema} = require 'graphql-tools'

-config = require '../../config.coffee'
+config = require '../../config'

 schemadir = (args...) -> path.join config.schema, args...

@@ -17,7 +17,7 @@
 # Programmatically create API.
 [schemas, resolvers] = zip apis.map((k) -> [
   readFileSync (schemadir "#{k}.gql"), 'UTF8'
-  require "./#{k}.coffee"
+  require "./#{k}"
 ])...

 module.exports = makeExecutableSchema


activity: pass `SocialChannel` like object to `SocialApiController::onChannelReady` method

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -47,8 +47,8 @@
       @openedChannels[name] = brokerChannel
       @emit "ChannelRegistered-#{channelName}", this

-  onChannelReady: (name, type, callback) ->
-    channelName = "socialapi.#{KD.getGroup().slug}-#{type}-#{name}"
+  onChannelReady: (channel, callback) ->
+    channelName = generateChannelName channel
     channel = @openedChannels[channelName]
     if channel instanceof KD.remote.api.SocialChannel
     then callback channel


Sort and rename state property

Sort and rename state property

--- client/lanes/components/grid/Grid.cjsx
+++ client/lanes/components/grid/Grid.cjsx
@@ -64,24 +64,22 @@
         for sortConfig in sortInfo
             sort = @query.fields.at(sortConfig.name).sortBy
             sortConfig.fn = sort if sort
-        @refs.grid.data = _.sorty(sortInfo, @refs.grid.data)
-        @setState(sortInfo: sortInfo, selIndex: undefined)

-    getSelected: ->
-        @query.results.modelAt(@state.selIndex) if @state.selIndex?
+        @setState(sortInfo: sortInfo, selectedIndex: undefined, selectedModel: undefined)

-    rowGetter: (rowIndex) ->
-        @query.results.rowAt(rowIndex, visibleOnly:true)
-
-    hideEditor: -> @setState(selIndex: null)
+    hideEditor: -> @setState(selectedIndex: null)

     onEditCancel: (model) ->
         if model.isNew()
-            @query.results.removeRow(@state.selIndex)
+            @query.results.removeRow(@state.selectedIndex)
         this.hideEditor()

     onEditSave: (model) ->
-        @query.results.saveModelChanges(model, @state.selIndex)
+        @query.results.saveModelChanges(model, @state.selectedIndex)
+
+        this.refs.grid.data[this.state.selectedIndex] =
+            @query.results.rowAt( @state.selectedIndex, visibleOnly: true )
+
         this.hideEditor()

     renderEditor: ->
@@ -93,11 +91,11 @@
             topOffset  : @headerHeight()
             grid       : this
             query      : @query
-            model      : @getSelected().clone()
+            model      : @state.selectedModel
             onCancel   : @onEditCancel
             onSave     : @onEditSave
             editors    : @props.columEditors
-            rowIndex   : @state.selIndex
+            rowIndex   : @state.selectedIndex
             rowHeight  : @props.rowHeight
             allowDelete: @props.allowDelete and @props.commands?.isEditing()
         }, @props.editorProps))
@@ -119,8 +117,8 @@
         props = _.extend {}, @props,
             position: 'top',
             onAddRecord: =>
-                @query.results.addBlankRow(0)
-                @setState(selIndex: 0)
+                model = @query.results.addBlankRow(0)
+                @setState(selectedIndex: 0, selectedModel: model)
         <Lanes.Components.Grid.Toolbar key="toolbar" {...props} />

     dataSource: (q) ->
@@ -135,7 +133,7 @@

             <div className='wrapper'>

-                {@renderEditor() if @canEdit() and @state.selIndex?}
+                {@renderEditor() if @canEdit() and @state.selectedIndex?}

                 <Lanes.Vendor.Grid
                     ref="grid"
@@ -143,7 +141,7 @@
                     rowHeight={@props.rowHeight}
                     dataSource={@dataSource}
                     paginationFactory = {@makeToolBar}
-                    selected={@getSelected()?.id}
+                    selected={@state.selectedModel?.id}
                     sortInfo={@state.sortInfo}
                     onSortChange={@onSortChange}
                     onSelectionChange={@onRowClick}


Pwm: fix numbers to match writing on the board

--- components/Pwm.coffee
+++ components/Pwm.coffee
@@ -1,12 +1,12 @@
 noflo = require 'noflo'

 pins =
-  pwm1: 1
   pwm3: 3
-  pwm4: 4
   pwm5: 5
   pwm6: 6
-  pwm7: 7
+  pwm9: 1
+  pwm10: 7
+  pwm11: 4

 class Pwm extends noflo.Component
   description: 'Pwm pin definitions'


add position to VBD

add position to VBD

--- src/spec.coffee
+++ src/spec.coffee
@@ -702,6 +702,8 @@

       is_cd_drive: -> @genval.type is 'CD'

+      position: -> @genval.userdevice
+
       # null if empty.
       #
       # TODO: Is it really equivalent?


mattuliser transcription

mattuliser transcription

--- jarkle/client/synth.coffee
+++ jarkle/client/synth.coffee
@@ -6,20 +6,23 @@
 PROXIMITY_PAIRS =
   leftHand: [
     pairPoint: 'rightHand'
+    note: 56
+  ,
+    pairPoint: 'leftShoulder'
+    note: 58
+  ,
+    pairPoint: 'leftHip'
+    note: 65
+  ]
+  rightHand: [
+    pairPoint: 'rightShoulder'
+    note: 63
+  ,
+    pairPoint: 'rightHip'
     note: 62
   ,
     pairPoint: 'leftShoulder'
-    note: 64
-  ,
-    pairPoint: 'leftHip'
-    note: 67
-  ]
-  rightHand: [
-    pairPoint: 'rightShoulder'
-    note: 66
-  ,
-    pairPoint: 'rightHip'
-    note: 69
+    note: 60
   ]

 class @Synth


Small code layout improvement.

Small code layout improvement.

--- app/assets/javascripts/app/controllers/_application_controller_table.coffee
+++ app/assets/javascripts/app/controllers/_application_controller_table.coffee
@@ -355,7 +355,7 @@

         if unit is '%'
           percentage = parseInt col.width, 10
-          col.width = percentage / 100 * @el.width() + "px"
+          col.width = percentage / 100 * @el.width() + 'px'

         return col

@@ -366,14 +366,14 @@
       headers = _.map headers, (col) =>
         if !col.unresizable
           value = parseInt col.width, 10
-          col.width = Math.max(@minColWidth, value - shrinkBy)  + "px"
+          col.width = Math.max(@minColWidth, value - shrinkBy) + 'px'
         return col

       # give left-over space from rounding to last column to get to 100%
       roundingLeftOver = @el.width() - @getHeaderWidths headers
       # but only if there is something left over (will get negative when there are too many columns for each column to stay in their min width)
       if roundingLeftOver > 0
-        headers[headers.length - 1].width = parseInt(headers[headers.length - 1].width, 10) + roundingLeftOver + "px"
+        headers[headers.length - 1].width = parseInt(headers[headers.length - 1].width, 10) + roundingLeftOver + 'px'

     return headers

@@ -383,16 +383,16 @@
     , 0

   getHeaderWidths: (headers) ->
-    widths = _.reduce headers, (memo, col, i) =>
+    widths = _.reduce headers, (memo, col, i) =>
       if col.width
         value = parseInt col.width, 10
         unit = col.width.match(/[px|%]+/)[0]
       else
         # !!! sets the width to default width if not set
-        headers[i].width = @baseColWidth+'px'
+        headers[i].width = @baseColWidth + 'px'
         value = @baseColWidth
         unit = 'px'
-
+
       return if unit is 'px' then memo + value else memo
     , 0


Check if timeago was loaded on using relativetime

--- src/tell.coffee
+++ src/tell.coffee
@@ -69,7 +69,8 @@
         if username.match(new RegExp("^#{recipient}"), "i")
           tellmessage = "#{username}: "
           for message in localstorage[room][recipient]
-            if config.relativeTime
+            # Also check that we have successfully loaded timeago
+            if config.relativeTime && timeago !== null
               timestr = timeago(message[1])
             else
               timestr = "at #{message[1].toLocaleString()}"


app publishing

app publishing

--- components/buttons/KDButtonView.coffee
+++ components/buttons/KDButtonView.coffee
@@ -1,35 +1,25 @@
 class KDButtonView extends KDView

-  @styles = [
-    "minimal","clean-gray","cupid-green","cupid-blue","blue-pill"
-    "dribbble","slick-black","thoughtbot","blue-candy","purple-candy"
-    "shiny-blue","small-blue","skip","clean-red","small-gray","transparent"
-  ]
+  constructor:(options = {},data)->

-  constructor:(options = {},data)->
-    options = $.extend
-      callback     : noop          # a Function
-      title        : ""            # a String
-      style        : "clean-gray"  # a String of one of button styles
-      type         : "button"      # a String of submit, reset, button
-      cssClass     : ""            # a String
-      icon         : no            # a Boolean value
-      iconOnly     : no            # a Boolean value
-      iconClass    : ""            # a String
-      disabled     : no            # a Boolean value
-      hint         : null          # a String of HTML ---> not yet implemented
-      loader       : no
-      # name         : ""
-      # value        : ""
-    ,options
+    options.callback  or= noop          # a Function
+    options.title     or= ""            # a String
+    # options.style     or= "clean-gray"  # a String of one of button styles ==> DEPRECATE THIS
+    options.type      or= "button"      # a String of submit, reset, button
+    options.cssClass  or= options.style or= ""            # a String
+    options.icon      or= no            # a Boolean value
+    options.iconOnly  or= no            # a Boolean value
+    options.iconClass or= ""            # a String
+    options.disabled  or= no            # a Boolean value
+    options.hint      or= null          # a String of HTML ---> not yet implemented
+    options.loader    or= no

     super options,data
+
+    @setClass options.style

     @setCallback options.callback
     @setTitle options.title
-    @setButtonStyle options.style
-    # @setName options.name           if options.name
-    # @setValue options.value         if options.value
     @setIconClass options.iconClass if options.iconClass
     @unhideIcon()                   if options.icon
     @setIconOnly options.iconOnly   if options.iconOnly
@@ -39,13 +29,12 @@
       @listenTo
         KDEventTypes       : "viewAppended"
         listenedToInstance : @
-        callback           : ->
-          @setLoader()
+        callback           : -> @setLoader()

-  setDomElement:()->
+  setDomElement:(cssClass)->
     @domElement = $ """
-      <button type='#{@getOptions().type}' class='kdbutton clean-gray' id='#{@getId()}'>
+      <button type='#{@getOptions().type}' class='kdbutton #{cssClass}' id='#{@getId()}'>

         Title
       </button>
@@ -61,25 +50,11 @@

   getCallback:()-> @buttonCallback

-  # setName:(name)->
-  #   @$().attr {name}
-  #
-  # setValue:(value)->
-  #   @$().attr {value}
-
   unhideIcon:()->
     @setClass "with-icon"
     @$('span.icon').removeClass 'hidden'

-  # refactor this
-  # this seems unnecessary just use cssClass maybe
-  setButtonStyle:(newStyle)->
-    {styles} = @constructor
-    for style in styles
-      @getDomElement().removeClass style
-    @getDomElement().addClass newStyle
-
   setIconClass:(iconClass)->
     @$('.icon').attr 'class','icon'
     @$('.icon').addClass iconClass


Updated for GitHub integration

Updated for GitHub integration

--- grammars/fstar.cson
+++ grammars/fstar.cson
@@ -1,79 +1,328 @@
-'scopeName': 'source.fstar'
+scopeName: 'source.fstar'

-'name': 'F*'
+name: 'F*'

-'fileTypes': [ 'fst', 'fsti', 'fs7' ]
+fileTypes: [ 'fst', 'fsti', 'fs7' ]

-'patterns': [
-  {
-    'name'  : 'keyword.control.fstar'
-    'match' : '\\b(assume|admit|assert|let|in|if|then|else|fun|rec|function)\\b'
-  }
-  {
-    'name'  : 'keyword.other.fstar'
-    'match' : '\\b(opaque|noeq|inline_for_extraction|logic|requires|ensures|modifies|match|with|and|kind|\
-                  type|val|open|module|effect|default|new_effect|sub_effect|\
-                  total|private|begin|end|decreases)\\b'
-  }
-  {
-    'name'  : 'constant.numeric.integer.decimal.fstar'
-    'match' : '\\b([0-9]+)'
-  }
-  {
-    'name'  : 'constant.language.empty-list.fstar'
-    'match' : '\\[\\]'
-  }
-  {
-    'name'  : 'constant.language.unit.fstar'
-    'match' : '\\(\\)'
-  }
-  {
-    'name'  : 'constant.language.Type.fstar'
-    'match' : '\\b(Type)\\b'
-  }
-  {
-      'name'  : 'constant.language.monad.fstar'
-      'match' : '\\b(Pure|PURE|Tot|STATE|ST|St|Stack|ALL|All|EXN|Exn|Ex|\
-                     DIV|Div|GHOST|Ghost|GTot|Lemma)\\b'
-  }
-  {
-    'name'  : 'keyword.other.arrow.fstar'
-    'match' : '->'
-  }
-  {
-    'name'  : 'keyword.logical-connective.fstar'
-    'match' : '\\b(==>|<==>|\\\\/|/\\\\|forall|exists)\\b'
-  }
-  {
-    'name'  : 'variable.other.generic-type.fstar'
-    'match' : '\'[a-zA-Z0-9_]+\\b'
-  }
-  {
-    'include': '#comments'
-  }
+patterns: [
+  { include: '#comments' }
+  { include: '#modules' }
+  { include: '#options' }
+  { include: '#expr' }
 ]

-'repository':
-  'comments':
-    'patterns': [
-      {
-        'name': 'comment.block.fstar'
-        'begin': '\\(\\*'
+repository:
+  expr:
+    patterns: [
+      { include: '#comments' }
+      {
+        name: 'variable.other.generic-type.fstar'
+        match: '\'[a-zA-Z0-9_]+\\b'
+      }
+      { include: '#strings' }
+      {
+        match: '\\b(int|nat|pos|bool|unit|string|Type|Type0|eqtype)\\b'
+        name: 'support.class.base.fstar'
+      }
+      {
+        match: '(/\\\\|\\\\/|<:|<@|[(][|]|[|][)]|u#|~>|->|<--|<-|<==>|==>|[?][.]|[.]\\[|[.]\\(|[{][:]pattern|::|:=|;;|[!][{]|\\[[|]|[|]>|[|]\\]|[~+&?$|#,.:;=\\[\\](){}-])'
+        name: 'support.class.base.fstar'
+      }
+      {
+        match : '\\b(Pure|PURE|Tot|STATE|ST|St|Stack|StackInline|HST|ALL|All|EXN|Exn|Ex|DIV|Div|GHOST|Ghost|GTot|Lemma)\\b'
+        name: 'constant.language.monad.fstar'
+      }
+      {
+        match: '\\b(_|[(]\\s*[)]|\\[\\s*\\])\\b'
+        name: 'constant.language.fstar'
+      }
+      {
+        match: '\\b(let|in|type|kind|val|rec|and|assume|admit|assert|assert_norm|squash|failwith|SMTPat|SMTPatOr|hasEq|fun|function|forall|exists|exception|by|new_effect|reify|try|synth|with|when)\\b'
+        name: 'keyword.other.fstar'
+      }
+      {
+        match: '\\b(abstract|attributes|noeq|unopteq|inline|inline_for_extraction|irreducible|logic|mutable|new|noextract|private|reifiable|reflectable|total|unfold|unfoldable)\\b'
+        name: 'storage.modifier.fstar'
+      }
+      {
+        match: '\\b([tT]rue|[fF]alse)\\b'
+        name: 'constant.language.boolean.fstar'
+      }
+      {
+        match: '\\b0[xX][0-9a-fA-F]+[uU]?[zyslL]?\\b'
+        name: 'constant.numeric.hex.js'
+      }
+      {
+        match: '\\b[0-9]+[uU]?[zyslL]?\\b'
+        name: 'constant.numeric.decimal.js'
+      }
+      {
+        match: '''(?x)
+          (?:
+            (?:\\b[0-9]+(\\.)[0-9]+[eE][+-]?[0-9]+\\b)| # 1.1E+3
+            (?:\\b[0-9]+(\\.)[eE][+-]?[0-9]+\\b)|       # 1.E+3
+            (?:\\B(\\.)[0-9]+[eE][+-]?[0-9]+\\b)|       # .1E+3
+            (?:\\b[0-9]+[eE][+-]?[0-9]+\\b)|            # 1E+3
+            (?:\\b[0-9]+(\\.)[0-9]+\\b)|                # 1.1
+            (?:\\b[0-9]+(\\.)\\B)|                      # 1.
+            (?:\\B(\\.)[0-9]+\\b)|                      # .1
+            (?:\\b[0-9]+\\b(?!\\.))                     # 1
+          )[L]?[fF]?
+        '''
+        captures:
+          0: name: 'constant.numeric.decimal.fstar'
+          1: name: 'meta.delimiter.decimal.period.fstar'
+          2: name: 'meta.delimiter.decimal.period.fstar'
+          3: name: 'meta.delimiter.decimal.period.fstar'
+          4: name: 'meta.delimiter.decimal.period.fstar'
+          5: name: 'meta.delimiter.decimal.period.fstar'
+          6: name: 'meta.delimiter.decimal.period.fstar'
+      }
+      {
+        begin: '([(])\\s*(requires|ensures|decreases)?'
+        beginCaptures:
+          1: name: 'punctuation.definition.scope.begin.bracket.round.fstar'
+          2: name: 'keyword.other.fstar'
+        end: '([)])'
+        endCaptures:
+          1: name: 'punctuation.definition.scope.end.bracket.round.fstar'
+        patterns: [
+          { include: '#expr' }
+        ]
+      }
+      {
+        begin: '([(])'
+        beginCaptures:
+          1: name: 'punctuation.definition.clause.begin.bracket.round.fstar'
+        end: '([)])'
+        endCaptures:
+          1: name: 'punctuation.definition.clause.end.bracket.round.fstar'
+        patterns: [
+          { include: '#expr' }
+        ]
+      }
+      {
+        begin: '([%]\\[)'
+        beginCaptures:
+          1: name: 'constant.language.termorder.begin.fstar'
+        end: '(\\])'
+        endCaptures:
+          1: name: 'constant.language.termorder.end.fstar'
+        patterns: [
+          { include: '#expr' }
+        ]
+      }
+      {
+        begin: '(\\[[@])'
+        beginCaptures:
+          1: name: 'storage.modifier.attributes.begin.fstar'
+        end: '(\\])'
+        endCaptures:
+          1: name: 'storage.modifier.attributes.end.fstar'
+        patterns: [
+          { include: '#expr' }
+        ]
+      }
+      {
+        begin: '\\b(match)\\b'
+        beginCaptures:
+          1: name: 'keyword.control.match.fstar'
+        end: '\\b(with)\\b'
+        endCaptures:
+          1: name: 'keyword.control.with.fstar'
+        patterns: [
+          { include: '#expr' }
+        ]
+      }
+      {
+        begin: '\\b(begin)\\b'
+        beginCaptures:
+          1: name: 'keyword.control.begin.fstar'
+        end: '\\b(end)\\b'
+        endCaptures:
+          1: name: 'keyword.control.end.fstar'
+        patterns: [
+          { include: '#expr' }
+        ]
+      }
+      {
+        begin: '\\b(if)\\b'
+        beginCaptures:
+          1: name: 'keyword.control.if.fstar'
+        end: '\\b(else)\\b'
+        endCaptures:
+          1: name: 'keyword.control.else.fstar'
+        patterns: [
+          { include: '#expr' }
+          {
+            match: '\\b(then)\\b'
+            captures:
+              1: name: 'keyword.control.then.fstar'
+          }
+        ]
+      }
+      {
+        begin: '\\b(if)\\b'
+        beginCaptures:
+          1: name: 'keyword.control.if.fstar'
+        end: '\\b(then)\\b'
+        endCaptures:
+          1: name: 'keyword.control.then.fstar'
+        patterns: [
+          { include: '#expr' }
+        ]
+      }
+      {
+        match: '\\b([\\p{Lu}\\p{Lt}][\\p{Lu}\\p{Lt}\\p{Ll}\\p{Lo}\\p{Lm}\\d\'_]*)\\b(?!\\s*[.])'
+        captures:
+          1: name: 'support.function.constructor.fstar'
+      }
+      {
+        match: '\\b((?:[\\p{Lu}\\p{Lt}][\\p{Lu}\\p{Lt}\\p{Ll}\\p{Lo}\\p{Lm}\\d\'_]*[.])+)'
+        captures:
+          1: name: 'variable.other.module.fstar'
+      }
+      {
+        match: '\\b([\\p{Ll}_][\\p{Lu}\\p{Lt}\\p{Ll}\\p{Lo}\\p{Lm}\\d\'_]*)'
+        captures:
+          1: name: 'entity.name.function.fstar'
+      }
+    ]
+  options:
+    patterns: [
+      {
+        match: '(#(?:re)?set-options)\\s*(["])((?:[^"]|\\\\")*)(["])'
+        captures:
+          1: name: 'keyword.control.setoption.fstar'
+          2: name: 'punctuation.definition.options.begin.fstar'
+          3: name: 'string.quoted.double.fstar'
+          4: name: 'punctuation.definition.options.end.fstar'
+      }
+    ]
+  modules:
+    patterns: [
+      {
+        begin: '\\b(module)\\s+([\\p{Lu}\\p{Lt}][\\p{Lu}\\p{Lt}\\p{Ll}\\p{Lo}\\p{Lm}\\d\'_]*)\\s*(=)'
+        beginCaptures:
+          1: name: 'keyword.control.module.fstar'
+          2: name: 'variable.other.module-alias.fstar'
+          3: name: 'keyword.operator.assignment.fstar'
+        end: '\\b((?<name>[\\p{Lu}\\p{Lt}][\\p{Lu}\\p{Lt}\\p{Ll}\\p{Lo}\\p{Lm}\\d\'_]*)(?:[.]\\g<name>)*)\\b'
+        endCaptures:
+          1: name: 'variable.other.module.fstar'
+      }
+      {
+        begin: '\\b(module|open|include)\\b'
+        beginCaptures:
+          1: name: 'keyword.control.module.fstar'
+        end: '\\b((?<name>[\\p{Lu}\\p{Lt}][\\p{Lu}\\p{Lt}\\p{Ll}\\p{Lo}\\p{Lm}\\d\'_]*)(?:[.]\\g<name>)*)\\b'
+        endCaptures:
+          1: name: 'variable.other.module.fstar'
+      }
+    ]
+  # Comment blocks. Note that single-line // comments are ignored inside comment blocks
+  # Comment blocks can also be nested
+  commentblock:
+    patterns: [
+      {
+        name: 'comment.block.fstar'
+        begin: '\\(\\*'
+        beginCaptures:
+          0: name: 'punctuation.definition.comment.begin.fstar'
+        end: '\\*\\)'
+        endCaptures:
+          0: name: 'punctuation.definition.comment.end.fstar'
+        patterns: [
+          { include: '#commentblock' }
+        ]
+      }
+    ]
+  comments:
+    patterns: [
+      {
+        name: 'comment.line.fstar'
+        match: '//.*'
+      }
+      { include: '#commentblock' }
+    ]
+  strings:
+    patterns: [
+      {
+        begin: '"'
+        beginCaptures:
+          0: name: 'punctuation.definition.string.begin.fstar'
+        end: '"'
+        endCaptures:
+          0: name: 'punctuation.definition.string.end.fstar'
+        name: 'string.quoted.double.fstar'
+        patterns: [
+          { include: '#string_escapes' }
+        ]
+      }
+      {
+        begin: '\''
+        beginCaptures:
+          0: name: 'punctuation.definition.char.begin.fstar'
+        end: '\''
+        endCaptures:
+          0: name: 'punctuation.definition.char.end.fstar'
+        name: 'string.quoted.single.fstar'
+        patterns: [
+          { include: '#string_escapes' }
+          {
+            match: '.{2,}'
+            name: 'invalid.illegal.string.js'
+          }
+        ]
+      }
+      {
+        'begin': '`'
         'beginCaptures':
           '0':
-            'name': 'punctuation.definition.comment.begin.fstar'
-        'end': '\\*\\)'
+            'name': 'punctuation.definition.operator.begin.fstar'
+        'end': '`'
         'endCaptures':
           '0':
-            'name': 'punctuation.definition.comment.end.fstar'
+            'name': 'punctuation.definition.operator.end.fstar'
+        'name': 'string.quoted.template.fstar'
         'patterns': [
           {
-            'include': '#comments'
+            'include': '#op_names'
           }
         ]
       }
-      {
-        'name': 'comment.line.fstar'
-        'match': '//.*'
-      }
-    ]
+    ]
+  'string_escapes':
+    'patterns': [
+      {
+        'match': '\\\\u(?![A-Fa-f0-9]{4}|{[A-Fa-f0-9]+})[^\'"]*'
+        'name': 'invalid.illegal.unicode-escape.fstar'
+      }
+      {
+        'match': '\\\\u(?:[A-Fa-f0-9]{4}|({)([A-Fa-f0-9]+)(}))'
+        'name': 'constant.character.escape.js'
+        'captures':
+          '1':
+            'name': 'punctuation.definition.unicode-escape.begin.bracket.curly.fstar'
+          '2':
+            'patterns': [
+              {
+                'match': '[A-Fa-f\\d]{7,}|(?!10)[A-Fa-f\\d]{6}'
+                'name': 'invalid.illegal.unicode-escape.fstar'
+              }
+            ]
+          '3':
+            'name': 'punctuation.definition.unicode-escape.end.bracket.curly.fstar'
+      }
+      {
+        'match': '\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)'
+        'name': 'constant.character.escape.fstar'
+      }
+    ]
+  'op_names':
+    'patterns': [
+      {
+        'match': '\\bop(?:_(?:Multiply|Star|Slash|Percent|Plus|Substraction|Equals|Less|Greater|Bang|Dollar|Amp|Dot|Hat|Colon|Pipe|Question))+\\b'
+        'name': 'entity.name.tag.fstar'
+      }
+    ]


Made remaining unimplemented cCS potential additions

--- grammars/latex.cson
+++ grammars/latex.cson
@@ -140,13 +140,15 @@
       { include: '#item' }
       { include: '#metaHyperref' }
       { include: '#text' } # Seems to work well!
-      # { include: '#commandDefinitions' }
       { include: '#metaFontStyling' } # eg. \emph
-      # { include: '#marginpar' }
       { include: '#metaFootnote' }
       { include: '#metaReferences' } # maybe include \ref \label as well?
+      { include: '#constants' } # words that represent constants
+
+      # Potential additions:
+      # { include: '#commandDefinitions' }
+      # { include: '#marginpar' }
       # { include: '#verbScantokens' } # not sure what this is for
-      { include: '#constants' } # words that represent constants
     ]
   }
   metaEnvironment: {


correct global macros

correct global macros


fix following facets, remove some of old cache stuff

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -108,17 +108,19 @@
     KD.time "Activity fetch took - "
     options = to : options.to or Date.now()

-    @fetchCachedActivity options, (err, cache)=>
+    @fetchActivity options, (err, teasers)=>
       @isLoading = no
-      if err or cache.length is 0
-        warn err  if err
-        @listController.hideLazyLoader()
+      @listController.hideLazyLoader()
+      KD.timeEnd "Activity fetch took"
+
+      if err or teasers.length is 0
+        warn "An error occured:", err  if err
         @listController.showNoItemWidget()
       else
-        @extractCacheTimeStamps cache
-        @sanitizeCache cache, (err, cache)=>
-          @listController.hideLazyLoader()
-          @listController.listActivitiesFromCache cache
+        @extractTeasersTimeStamps(teasers)
+        @listController.listActivities teasers
+
+      callback? err, teasers

   fetchActivitiesFromCache:(options = {})->
     @fetchCachedActivity options, (err, cache)=>
@@ -214,38 +216,17 @@
         createdAt : -1

     {CActivity}   = KD.remote.api
-    if KD.config.useNeo4j
-      options.feedType = @feedType
-
-      if options.facets is activityTypes
-        options.facets = ['Everything']
-
-      if @feedType is "Public"
-        options.groupId = KD.getSingleton("groupsController").getCurrentGroup().getId()
-        CActivity.fetchPublicContents options, callback
-      else
-        CActivity.fetchFolloweeContents options, callback
-
+    options.feedType = @feedType
+
+    if options.facets is activityTypes
+      options.facets = ['Everything']
+
+    if @feedType is "Public"
+      options.groupId = KD.getSingleton("groupsController").getCurrentGroup().getId()
+
+      @fetchActivitiesFromCache options, callback
     else
-      @isExempt (exempt)->
-        options.lowQuality = exempt
-        CActivity.fetchFacets options, (err, activities)->
-          if err
-            callback err
-          else if not exempt
-            KD.remote.reviveFromSnapshots clearQuotes(activities), callback
-          else
-            # trolls and admins in show troll mode will load data on request
-            # as the snapshots do not include troll comments
-            stack = activities.map (activity)-> (cb)->
-              activity.fetchTeaser (err, teaser)->
-                if err then console.warn 'could not fetch teaser'
-                else cb err, teaser
-                stack.fin()
-              , yes
-
-            dash stack, (err, res)->
-              callback null, res
+      CActivity.fetchFolloweeContents options, callback

   # Fetches activities that occured after the first entry in user feed,
   # used for minor disruptions.
@@ -293,23 +274,13 @@
         log "fetchSomeActivities timeout reached"

   fetchCachedActivity:(options = {}, callback)->
-    if KD.config.useNeo4j
-      options.timestamp or= options.to
-      options.groupName   = KD.getSingleton("groupsController").getCurrentGroup()?.slug or "koding"
-      options.facets      = @getFilter()
-
-      KD.remote.api.CStatusActivity.fetchPublicActivityFeed options, (err, result)=>
-        result.overview.reverse() if result?.overview
-        return callback err, result
-
-    else
-      $.ajax
-        url     : "/-/cache/#{options.slug or 'latest'}"
-        cache   : no
-        error   : (err)->   callback? err
-        success : (cache)->
-          cache.overview.reverse()  if cache?.overview
-          callback null, cache
+    options.timestamp or= options.to
+    options.groupName   = KD.getSingleton("groupsController").getCurrentGroup()?.slug or "koding"
+    options.facets      = @getFilter()
+
+    KD.remote.api.CStatusActivity.fetchPublicActivityFeed options, (err, result)=>
+      result.overview.reverse() if result?.overview
+      return callback err, result

   continueLoadingTeasers:->
     # fix me


avatar uploading functionality & setting

--- app/coffee/avatar.coffee
+++ app/coffee/avatar.coffee
@@ -48,15 +48,15 @@
         avatar_url = '/images/uploads/' + response.image.id + '-avatar_main.' + response.image.format
         avatar_thumb = '/images/uploads/' + response.image.id + '-32x32.' + response.image.format

-        # $.ajax {
-        #           type: 'PUT'
-        #           url: '/user.json'
-        #           dataType: 'json'
-        #           data:
-        #             "user[image_id]": response.image.id
-        #           success: (data, status, xhr) =>
-        #             alert 'updated avatar!  '
-        #         }
+        $.ajax {
+          type: 'PUT'
+          url: '/user.json'
+          dataType: 'json'
+          data:
+            "user[image_id]": response.image.id
+          success: (data, status, xhr) =>
+            alert 'updated avatar!  '
+        }

         @uploaderDisplayed = false
         @$avatarView.removeUploader(avatar_url, avatar_thumb)


Fix it so that the controllerObject handle is being piped from the first promise instead of carried from a global object.

--- guts/routing.coffee
+++ guts/routing.coffee
@@ -4,9 +4,6 @@

 module.exports = (app) ->
-        #This is quick handle on the controllers.
-        controllerObject = {}
-
         # For performance reasons, we're going to try and read these files asynchronously
         # utilizing the auto-generated bluebird conversion function
         fs.readdirAsync('./controllers/').then (controllers) ->
@@ -17,20 +14,27 @@
                         return new Promise (resolve, reject) ->
                                 if file.match(/.+\.js/g) != null || file.match(/.+\.coffee/g) != null
                                         # When requiring the module, we don't really want to specify an extension.
-                                        # Let's get rid of it.
+                                        # Let's get rid of it.
+                                        myObject = {}
                                         name = file.replace('.js', '').replace('.coffee', '')

                                         # Require the controller, feed it into the controllers
-                                        controllerObject[name] = require "../controllers/#{name}"
-                                        resolve()
+                                        myObject[name] = require "../controllers/#{name}"
+                                        resolve(myObject)

                 # Loop through all the controllers, and map all the returned promises to a new array
-                # so that we can yield when the entire thing is done.
-                listOfPromises = _.map controllers, makeAsyncControllerParse
+                # so that we can yield when the entire thing is done.
+                listOfPromises
+                listOfPromises =
+                        do
+                                _.chain controllers
+                                .map makeAsyncControllerParse
+                                .reduce (bigObject, piece) -> _.extend bigObject, piece
+                                .value

                 # Once everything is done from above, we return back the collective promise
-                return Promise.all listOfPromises
-        .then () ->
+                return Promise.props listOfPromises
+        .then (controllerObject) ->
                 #Let's load in the routes file
                 configuredRoutes = require '../config/routes'


Get rid of the do and add parentheses

Get rid of the do and add parentheses

--- guts/routing.coffee
+++ guts/routing.coffee
@@ -57,12 +57,11 @@
                                         # we want these to run sequentially, so we use the reduce function
                                         # to run them, then converge into a single, final promise.
                                         finalPromise =
-                                                do
-                                                        _.chain [tempPromise]
-                                                        .concat actionHandles
-                                                        .reduce (cur, next) ->
-                                                                cur.then next
-                                                        .value
+                                            _.chain [tempPromise]
+                                            .concat actionHandles
+                                            .reduce (cur, next) ->
+                                                cur.then next
+                                            .value()

                                         # Everything should be done.  We can finally render a template
                                         # or return back JSON depending on what they did on that last function


Fixed 'Show advanced commands' link on the options page.

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -830,7 +830,7 @@
   findMode = false
   HUD.hide()

-showHelpDialog = (html, fid) ->
+window.showHelpDialog = (html, fid) ->
   return if (isShowingHelpDialog || !document.body || fid != frameId)
   isShowingHelpDialog = true
   container = document.createElement("div")


Modes; minor changes.

Modes; minor changes.

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -371,10 +371,9 @@

       hint

-    hints[selectedInputIndex].classList.add 'internalVimiumSelectedInputHint'
-
-    hintContainingDiv = DomUtils.addElementList(hints,
-      { id: "vimiumInputMarkerContainer", className: "vimiumReset" })
+    hintContainingDiv = DomUtils.addElementList hints,
+      id: "vimiumInputMarkerContainer"
+      className: "vimiumReset"

     new class FocusSelector extends InsertModeBlocker
       constructor: ->
@@ -394,11 +393,8 @@
               @exit()
               @continueBubbling

-        # TODO. InsertModeBlocker is no longer a singleton.  Need to make this a singleton.  Fix once class
-        # hierarchy is removed.
-
-        visibleInputs[selectedInputIndex].element.focus()
         @exit() if visibleInputs.length == 1
+        hints[selectedInputIndex].classList.add 'internalVimiumSelectedInputHint'

       exit: ->
         DomUtils.removeElement hintContainingDiv
@@ -991,12 +987,10 @@

 window.enterFindMode = ->
   findModeQuery = { rawQuery: "" }
-  # window.findMode = true # Same hack, see comment at window.findMode definition.
   HUD.show("/")
   new FindMode()

 exitFindMode = ->
-  window.findMode = false # Same hack, see comment at window.findMode definition.
   HUD.hide()

 window.showHelpDialog = (html, fid) ->


Replace settings.addEventListener with Settings.addEventListener in the frontend

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -70,8 +70,6 @@

   load: -> @init() unless @port

-  addEventListener: Settings.addEventListener.bind Settings
-
 #
 # Give this frame a unique (non-zero) id.
 #
@@ -102,7 +100,7 @@
       # An input may already be focused. If so, grab back the focus.
       @grabBackFocus document.activeElement if document.activeElement

-    if settings.isLoaded then activate() else settings.addEventListener "load", activate
+    if settings.isLoaded then activate() else Settings.addEventListener "load", activate

   grabBackFocus: (element) ->
     return @continueBubbling unless DomUtils.isEditable element
@@ -157,7 +155,7 @@
 # Complete initialization work that sould be done prior to DOMReady.
 #
 initializePreDomReady = ->
-  settings.addEventListener("load", LinkHints.init.bind(LinkHints))
+  Settings.addEventListener "load", LinkHints.init.bind LinkHints
   settings.load()

   initializeModes()


Add support for busy queues partitioning

--- src/vent.coffee
+++ src/vent.coffee
@@ -1,3 +1,4 @@
+os              = require 'os'
 _               = require 'lodash'
 assert          = require 'assert'
 amqp            = require 'amqp'
@@ -232,7 +233,13 @@
         assert _.isString(options.topic),   "topic required"
         assert _.isString(options.group),   "group required"

-        "#{options.channel}:#{options.topic}:#{options.group}"
+        name = "#{options.channel}:#{options.topic}:#{options.group}"
+        if options.partition?
+            partiotion_key = 0
+            for l in "#{os.hostname()}:#{process.env.PORT || '0'}"
+                partiotion_key ^= l.charCodeAt(0)
+            name += ":p#{partiotion_key % 4}"
+        name

     _create_queue: (options) ->
         logger.trace("create queue", {options})
@@ -279,10 +286,10 @@
         assert _.isObject(exchange), "exchange required"
         assert _.isString(options.topic), "topic option require"

-        binding_key = options.topic
+        binding_key = if options.partition then "10" else options.topic
         bound_queue_deferred = Q.defer()

-        logger.trace("binding queue to exchange", {binding_key})
+        logger.trace("binding queue to exchange", {queue: queue.name, exchange: exchange.name, binding_key})
         queue.bind(exchange, binding_key)
         queue.on 'queueBindOk', ->
             logger.trace("queue and exchange bound", {binding_key})
@@ -335,17 +342,42 @@
         logger.trace("create exchange: %s", options.exchange)

         exch_deferred = Q.defer()
+        promise = exch_deferred.promise
         exch_name = options.channel
-
         exch_options =
             type: options.type or 'topic'
             autoDelete: false
             durable: options.durable

+        if options.partition?
+            exch_name = "#{exch_name}.#{options.group}-splitter"
+            _.extend(exch_options,
+                type: 'x-consistent-hash'
+                autoDelete: true
+            )
+            exchange = null
+            promise = promise.then @_create_bound_exchange.bind(@, options, connection)
+
         logger.trace("create exchange instance", {exch_name, exch_options})
-        connection.exchange(options.channel, exch_options, exch_deferred.resolve)
-
-        exch_deferred.promise
+        connection.exchange(exch_name, exch_options, exch_deferred.resolve)
+        promise
+
+    _create_bound_exchange: (options, connection, exchange) ->
+        @_create_exchange_instance(_.omit(options, 'partition'), connection)
+            .then @_bind_exchange.bind(@, exchange, options)
+
+    _bind_exchange: (exchange, options, source) ->
+        assert _.isString(options.topic), "topic option require"
+
+        binding_key = options.topic
+        exchange_deferred = Q.defer()
+
+        logger.trace("binding exchange to other exchange", {exchange: exchange.name, source: source.name, binding_key})
+        exchange.bind(source, binding_key, ->
+            exchange_deferred.resolve(exchange)
+        )
+        exchange_deferred.promise
+

 module.exports = (setup, options) ->
     setup = {server: setup} if _.isString(setup)


Center text vertically

Center text vertically

--- src/view.coffee
+++ src/view.coffee
@@ -456,7 +456,7 @@

       if parenttype is 'block' and @model.type is 'indent'
         @margins =
-          top: 0 #padding
+          top: 0
           bottom: if @lineLength > 1 then @view.opts.indentTongueHeight else padding

           firstLeft: 0
@@ -482,12 +482,12 @@

       else if @model.type is 'text' and parenttype is 'block'
         if @model.prev?.type is 'newline' and @model.next?.type in ['newline', 'indentStart'] or @model.prev?.prev?.type is 'indentEnd'
-          textPadding = 0
+          textPadding = padding / 2
         else
           textPadding = padding
         @margins =
           top: textPadding
-          bottom: padding
+          bottom: textPadding #padding

           firstLeft: left
           midLeft: left
@@ -1086,7 +1086,7 @@
           # The first line of an indent is often empty; this is the desired behavior
           else if @model.type is 'indent'
             @minDistanceToBase[line].above = 0
-            @minDistanceToBase[line].below = @view.opts.padding
+            @minDistanceToBase[line].below = 0

           # Empty blocks should be the height of lines with text
           else


Accidentally a colon

Accidentally a colon

--- keymaps/script.cson
+++ keymaps/script.cson
@@ -10,7 +10,7 @@
   'shift-cmd-i': 'script:run-options'

-'.platform-win32 .editor, .platform-linux .editor'
+'.platform-win32 .editor, .platform-linux .editor':
   'shift-ctrl-b': 'script:run'
   'shift-enter': 'script:run'
   'ctrl-w': 'script:close-view'


Start of 'what was that' support

Start of 'what was that' support

--- src/scripts/hubot-factoid.coffee
+++ src/scripts/hubot-factoid.coffee
@@ -18,6 +18,7 @@
 #   something random - Outputs a random factoid
 #   hubot forget <factoid>#<num> - Deletes the <num>th response of <factoid>
 #   hubot forget that - Deletes the last used response
+#   hubot what was that - Outputs what the last factoid was
 #   <factoid> - Outputs a random reply that matches <factoid>
 #
 # Notes:
@@ -32,6 +33,8 @@
 util = require 'util'
 os = require 'os'

+# FIXME this should be per room
+last_factoid = null
 module.exports = (robot) ->
   class Factoid
     constructor: (name, data) ->
@@ -93,6 +96,11 @@
       return @get factoid
     output: (msg, factoid) ->
       tidbit = factoid.tidbit()
+      # FIXME this should be per room
+      last_factoid = {
+        factoid: factoid,
+        tidbit: tidbit
+      }

       output = tidbit.tidbit
       if robot.variables?
@@ -151,7 +159,7 @@
   robot.respond /forget #(\d+)$/, (msg) =>
     msg.reply "Sorry, syntax is now \"forget <factoid>#<index from 0>\" or \"forget that\""

-  robot.resond /forget that$/, (msg) =>
+  robot.respond /forget that$/, (msg) =>
     msg.reply "FIXME - not implemented yet"

   robot.respond /forget (.*)#(\d+)$/, (msg) =>
@@ -172,6 +180,30 @@
       return
     tidbit = factoid.tidbits.splice(tid, 1)
     msg.reply "Deleted tidbit: " + tidbit[0].verb + '|' + tidbit[0].tidbit
+    factoid.save()
+
+  robot.respond /what was that\??$/, (msg) ->
+    # FIXME this should be per room
+    return unless last_factoid
+    # FIXME - keep track of alias in last_factoid as well
+    # halkeye: That was 'rofl' (#315): <reply> I am also amused
+    # halkeye: That was 'that's what she said' => 'thats what she said' (#65): <reply> No, that's what HE said.
+    # halkeye: That was 'give me a weapon' (#863): <action> gives $weapon to $who;  vars used: { 'weapon' => [ 'a Biggoron Sword' ]};.
+    name = last_factoid.factoid.name
+    tidbit = last_factoid.tidbit
+    idx = last_factoid.factoid.tidbits.map((tid) -> tid.tidbit).indexOf(tidbit.tidbit)
+    response = []
+    response.push "That was"
+    response.push "'$last_alias' => " if last_factoid.alias # FIXME - if alias
+    response.push "'" + name + "'"
+    response.push "(#"+idx+") "
+    response.push tidbit.verb
+    response.push tidbit.tidbit
+    if last_factoid.last_vars # FIXME
+      response.push ";"
+      response.push "vars used:"
+      response.push util.inspect last_factoid.last_vars, { depth: null }
+    msg.reply response.join ' '

   robot.hear /^(.*)\??$/, (msg) =>
     factoid_name = msg.match[1].trim()


improve deactivate()

improve deactivate()

--- lib/atomizr.coffee
+++ lib/atomizr.coffee
@@ -37,7 +37,7 @@
     @subscriptions.add atom.commands.add 'atom-workspace', 'atomizr:toggle-atom-snippet-format': => @atomToAtom()

   deactivate: ->
-    @subscriptions.dispose()
+    @subscriptions?.dispose()
     @subscriptions = null

   # Automatic conversion, based on scope


Show LaTeX building time in logs

Show LaTeX building time in logs

--- lib/builder.coffee
+++ lib/builder.coffee
@@ -16,6 +16,7 @@
     @setCmds()
     if atom.config.get('atom-latex.save_on_build')
       @saveonBuild()
+    @buildTimer = Date.now()
     @latex.logger.log = []
     @latex.package.status.view.status = 'building'
     @latex.package.status.view.update()
@@ -86,7 +87,7 @@
     if @latex.parser.isLatexmkSkipped
       logText = 'latexmk skipped building process.'
     else
-      logText = 'Successfully built LaTeX.'
+      logText = "Successfully built LaTeX in #{Date.now() - @buildTimer} ms"
     @latex.logger.log.push({
       type: 'status',
       text: logText


Fix multiple options for latexmk

Fix multiple options for latexmk

--- lib/builder.coffee
+++ lib/builder.coffee
@@ -28,8 +28,10 @@
     user_program = 'pdf' if user_program is 'pdflatex'

     options =  ["-cd", "-e", "-f", "-#{user_program}",
-      "-interaction=nonstopmode", "-synctex=1"]\
-        .concat ["-latexoption=\"#{texopt}\"" for texopt in user_options]
+      "-interaction=nonstopmode", "-synctex=1"]
+
+    for texopt in user_options
+      options.push "-latexoption=#{texopt}"

     command = ["latexmk"].concat(options, "\"#{texfile}\"").join(' ')
     @ltConsole.addContent(command,br=true)


Admin/Logs: switch to All tab and reload logs on MemberWarning received

--- client/admin/lib/views/logs/index.coffee
+++ client/admin/lib/views/logs/index.coffee
@@ -26,6 +26,10 @@

       @tabView.showPaneByIndex index

+    kd.singletons.notificationController.on 'MemberWarning', =>
+      @tabView.showPaneByIndex 0
+      @handleReload()
+

   createTabView: ->

@@ -36,8 +40,7 @@
     @tabView.tabHandleContainer.addSubView new kd.ButtonView
       cssClass : 'solid compact green reload'
       title    : 'Reload'
-      callback : =>
-        @tabView.activePane.getMainView().reload()
+      callback : @bound 'handleReload'

     Object.keys(SCOPES).forEach (scope) =>
       @tabView.addPane new kd.TabPaneView
@@ -51,3 +54,6 @@

   setRoute: (route = '') ->
     kd.singletons.router.handleRoute "/Admin/Logs#{route}"
+
+  handleReload: ->
+    @tabView.activePane.getMainView().reload()


wording, comments...

wording, comments...

--- client/app/views/long-list-images.coffee
+++ client/app/views/long-list-images.coffee
@@ -19,6 +19,7 @@
 #       longList.resizeHandler()
 #
 # -- SEMANTIC / CONVENTIONS --
+#
 #
 # 1/ syntax
 #   . a variable name ending with a '$' is a reference to a node in the DOM
@@ -26,6 +27,7 @@
 #   . in a variable name, Pt means "Pointer". The safe zone is define by a
 #     start pointer and an end pointer, themselves defined
 #     by a rank, y monthRk and inMonthRk
+#
 #
 # 2/ @months
 #   [ {nPhotos:45, month:"201503"}, ... ]
@@ -43,18 +45,31 @@
 #   lastRk  : integer, rank of the last  photo of the month
 # }
 #
+#
 # 3/ @nPhotos
 #   Total number of images in the long list.
+#
 #
 # 4/ "rank"
 #   all images are indexed by their rank being their position in the ordered
 #   list of images.
 #   The most recent image rank is 0, the oldest rank is nPhoto - 1
 #
+#
 # 5/ inMonthRank
 #   The rank of the image in the chronological list of images of the same month
 #
-# 6/ buffer
+#
+# 6/ buffer :
+#
+#   The buffer lists all the created thumbs, keep a reference on the first (top
+#   most) and the last (bottom most) cells.
+#   The data structure of the buffer is a closed double linked chain.
+#   Each element of the chain is a "thumb" with a previous (prev) and next
+#   (next) element.
+#   "closed" means that buffer.last.prev == buffer.first
+#
+#   data structure :
 #      first   : {thumb}   # top most thumb
 #      firstRk : {integer} # rank of the first image of the buffer
 #      last    : {thumb}   # bottom most thumb
@@ -72,10 +87,7 @@
 #      nextFirstMonthRk : {integer}
 #      nextFirstRk      : {integer}
 #      nextFirstY       : {integer}
-#   Lists all the created thumbs, keep a reference on the first (top
-#   most) and the last (bottom most) cells. The data structure of the buffer
-#   is a doubled linked list.
-#   each element of the list is an object
+#
 #
 # 7/ thumb
 #      prev    : {thumb}   # previous thumb in the buffer    : older, lower in the list
@@ -86,6 +98,7 @@
 #      id      : {integer} # id of the corresponding image
 #   Element of the buffer, keeps a reference (el) to the thumb element inserted
 #   in the DOM.
+#
 #
 # 8/ safeZone
 #      firstCol             : {integer}
@@ -100,7 +113,6 @@
 #      endCol               : {integer}
 #      endMonthRk           : {integer}
 #      endY                 : {integer}
-
 #
 #
 ################################################################################
@@ -266,8 +278,6 @@
      * obvious choice.
      * Called only when both LongList.init() has been called and that we also
      * got from the server the month distribution (Photo.getMonthdistribution)
-     *
-     * @return {[type]} [description]
     ###
     _DOM_controlerInit: () ->

@@ -295,6 +305,7 @@
         thumbs$Height          = null
         monthLabelTop          = null
         lastOnScroll_Y         = null
+        current_scrollTop      = null
         safeZone =
             firstRk              : null
             firstMonthRk         : null
@@ -327,7 +338,12 @@

         @_scrollHandler = _scrollHandler

-
+        ###*
+         * returns the font-size in px of a given element (context) or of the
+         * root element of the document if no context is provided.
+         * @param  {element} context Optionnal: an elemment to get the font-size
+         * @return {integer}         the font-size
+        ###
         getElementFontSize=( context )->
             # Returns a number
             return parseFloat(
@@ -377,7 +393,7 @@
         ###*
          * Compute all the geometry after a resize or when the list in inserted
          * in the DOM.
-         * _adaptBuffer will be executed at the end if
+         * _adaptBuffer will be executed at the end except if
          *     1- the distribution array of photo has not been received.
          *     2- the geometry could not be computed (for instance if the widht
          *     of the list is null when the list is not visible)
@@ -462,7 +478,9 @@
                 label$.dataset.monthRk = rk
                 @index$.appendChild(label$)
             ##
-            #
+            # the first call of _resizeHandler it is not necessary to
+            # positionnate the thumbs. After the first call, the thumbs are
+            # always repositionated
             if bufferAlreadyAdapted
                 _rePositionThumbs()
             bufferAlreadyAdapted = true
@@ -471,7 +489,16 @@
         @resizeHandler = _resizeHandler

-
+        ###*
+         * Initialize the buffer.
+         * The buffer lists all the created thumbs, keep a reference on the
+         * first (top most) and the last (bottom most) thumb.
+         * The buffer is a closed double linked chain.
+         * Each element of the chain is a "thumb" with a previous (prev) and
+         * next (next) element.
+         * "closed" means that buffer.last.prev == buffer.first
+         * data structure : see the beginning of this file.
+        ###
         _initBuffer = ()=>

             thumb$ = document.createElement('img')
@@ -480,13 +507,11 @@
             thumb$.style.width  = thumbHeight + 'px'
             @thumbs$.appendChild(thumb$)
             thumb =
-                prev : null
-                next : null
+                prev : thumb
+                next : thumb
                 el   : thumb$
                 rank : null
                 id   : null
-            thumb.prev = thumb
-            thumb.next = thumb

             buffer =
                     first   : thumb  # top most thumb
@@ -550,7 +575,7 @@
             , 2000

         ###*
-         * Adapt the buffer when the viewport has moved
+         * Adapt the buffer when the viewport has moved.
          * Launched at init and by _scrollHandler
          * Steps :
         ###
@@ -560,7 +585,8 @@
             lazyHideIndex()

             # test speed, if too high, relaunch a _scrollHandler
-            speed = Math.abs(@viewPort$.scrollTop - lastOnScroll_Y) / viewPortHeight
+            current_scrollTop = @viewPort$.scrollTop
+            speed = Math.abs(current_scrollTop - lastOnScroll_Y) / viewPortHeight
             if speed > MAX_SPEED
                 _scrollHandler()
                 return
@@ -952,7 +978,7 @@

         _SZ_initStartPoint = ()=>
             SZ = safeZone
-            Y = @viewPort$.scrollTop
+            Y = current_scrollTop
             for month, monthRk in @months
                 if month.yBottom > Y
                     break


Improve log error

Improve log error

--- client/lib/cordova/facebook-login.coffee
+++ client/lib/cordova/facebook-login.coffee
@@ -16,12 +16,12 @@
 		facebookConnectPlugin.getLoginStatus (response) ->
 			if response.status isnt "connected"
 				facebookConnectPlugin.login ["public_profile", "email"], fbLoginSuccess, (error) ->
-					console.log("" + error)
+					console.log(JSON.stringidy(error), error)
 			else
 				fbLoginSuccess(response)

 		, (error) ->
-			console.log("" + error)
+			console.log(JSON.stringidy(error), error)

 	else
 		Facebook.requestCredential(options, credentialRequestCompleteCallback)


Fix callback not being called in install()

--- src/judge/install.coffee
+++ src/judge/install.coffee
@@ -42,5 +42,9 @@
 								callback null
 					, (err) ->
 						return callback err if err
+
+						callback null
 			, (err) ->
 				return callback err if err
+
+				callback null


Adds ng-cloak to empty ng-repeat workaround

--- lib/compile.coffee
+++ lib/compile.coffee
@@ -180,10 +180,12 @@
         if close
           """
           {{#forEach #{varName}#{filterStr}}}
-          #{close.before.replace /\sng-repeat/, ' data-ng-repeat'}
+            #{close.before.replace /\sng-repeat/, ' data-ng-repeat'}
           {{/forEach}}
           {{#unless #{_.last(varName.split /\s/)}.length}}
-          #{close.before.replace /\sng-repeat/, ' data-ng-repeat'}
+            <span ng-cloak>
+              #{close.before.replace /\sng-repeat/, ' data-ng-repeat'}
+            </span>
           {{/unless}}
           #{close.after}
           """


{'noWrap'} -> bare: yes (CoffeeScript 0.9.5).

--- bin/zappa.coffee
+++ bin/zappa.coffee
@@ -26,7 +26,7 @@

 compile = (coffee_path) ->
   fs.readFile coffee_path, (err, data) ->
-    js = coffee.compile String(data), {'noWrap'}
+    js = coffee.compile String(data), bare: yes
     js = "require('zappa').run(function(){#{js}});"
     js_path = path.basename(coffee_path, path.extname(coffee_path)) + '.js'
     dir = path.dirname coffee_path


little oops, fixing this

little oops, fixing this

--- lib/crudify.coffee
+++ lib/crudify.coffee
@@ -94,6 +94,8 @@
               limited.push datastores[x]

           return fn null, limited
+
+        else fn null, datastores

       # handle "POST" requests
       # `ds.insert` is used


Week supdated

Week supdated

--- lib/getWeeks.coffee
+++ lib/getWeeks.coffee
@@ -1,4 +1,4 @@
-Months = require('./getMonths')()
+Months = require('./getDays')()
 module.exports = ->
     obj = {}

@@ -8,7 +8,7 @@
         date

     obj.weekCollectionForMonth = (year, month) ->
-        size = Months.monthSize year, month
+        size = Months.dayCountForMonth year, month
         startDate = new Date year, month, 1
         endDate = new Date year, month, size
         @weekCollection startDate, endDate


Prep for windows

Prep for windows

Just need to change output file and location.

--- lib/grammars.coffee
+++ lib/grammars.coffee
@@ -68,6 +68,10 @@
       "File Based":
         command: "bash"
         args: (context) -> ["-c", "g++ -Wall -include stdio.h -include iostream '" + context.filepath + "' -o /tmp/cpp.out && /tmp/cpp.out"]
+    else if GrammarUtils.OperatingSystem.isWindows() and GrammarUtils.OperatingSystem.release().indexOf("14399") >= 0
+      "File Based:
+        command: "bash"
+        args: (context) -> ["-c", "g++ -Wall -include stdio.h -include iostream '" + context.filepath + "' -o /tmp/cpp.out && /tmp/cpp.out"]

   Clojure:
     "Selection Based":


removing this bit

removing this bit

--- site/coffee/main.coffee
+++ site/coffee/main.coffee
@@ -19,7 +19,6 @@
 # DEALINGS IN THE SOFTWARE.

 HyCodeMirror = CodeMirror.fromTextArea($('#hython-target')[0], {
-    value: "(print \"hello, world\")",
     mode:  "clojure",
     theme: "twilight",
     autofocus: true,


Don't Do Unnecessary Searching

Don't Do Unnecessary Searching

--- lib/gobuild.coffee
+++ lib/gobuild.coffee
@@ -103,8 +103,8 @@
     extract = (matchLine) =>
       return unless matchLine?
       if matchLine[2]? and matchLine[2] is '#'
-        # Found A Package Indicator
-        pkg = @absolutePathForPackage(matchLine[3], splitgopath)
+        # Found A Package Indicator, Skip For Now
+        # pkg = @absolutePathForPackage(matchLine[3], splitgopath)
       else
         file = null
         if matchLine[5]? and matchLine[5] isnt ''


keeps docs up to date

keeps docs up to date

--- lib/gotodef.coffee
+++ lib/gotodef.coffee
@@ -1,20 +1,20 @@
 {$$, SelectListView} = require 'atom-space-pen-views'
 fuzzaldrinPlus = require 'fuzzaldrin-plus'
-loading = require 'util/loading'

 # ## GoToDef-Panel
 #
 # `goto` takes a promise as its argument, which can either be fulfilled or rejected.
 # Both modes will be handled by `goto` as follows:
-# On fulfillment, a `goToView` will be shown and populated by the array `items`
-# returned by the promise.
+# On fulfillment, a `GotoView` will be shown and populated by the array `items`
+# returned by the promise if there's more than one item in it. Otherwise, Atom
+# jumps straight to the definition.
 #   `items` conatins objects with the fields:
 #     .text:      Displayed text, searchable.
 #     .file:      File in which this method is defined, not displayed.
 #     .line:      Line of definition.
 #     .dispfile:  Humanized file path, displayed.
 #
-# On rejection, the `GotoView` will only shown the error message provided.
+# On rejection, the `GotoView` will only show the error message provided.

 module.exports =
 goto: (promise) ->
@@ -30,6 +30,7 @@

     .catch (error) ->
       @view.setError error
+      @view.show()

 class GotoView extends SelectListView
   initialize: ->


add blurb about SMC

add blurb about SMC

--- src/smc-webapp/landing_page.cjsx
+++ src/smc-webapp/landing_page.cjsx
@@ -466,7 +466,7 @@

"SageMathCloud provides a user-friendly interface. Students don’t need to install any software at all.
         They just open up a web browser and go to cloud.sagemath.com and that’s it. They just type code directly
         in, hit shift+enter and it runs, and they can see if it works. It provides immediate feedback. The course
-        management features work really well."
+        management features work really well." - Will Conley, Math professor, University of California at Los Angeles

             Quotes {' | '}
@@ -662,6 +662,14 @@
                     </Col>
                     <Col sm=7 className="hidden-xs" style=marginTop:'10px'>
                         <Well style={'textAlign': 'center', 'float':'right'}>
+
For the next generation of innovative leaders

+
who teach courses using modern open
+                            source software, SageMathCloud is by far the most powerful and easiest way to get your
+                            class up and running. We completely eliminate frustrating installations, awkward and
+                            closed Matlab and Mathematica clouds, and the limited functionality of Overleaf and ShareLatex.
+                            Quick, expert technical support is offered via our ridiculously collaborative environment
+                            including LaTeX, R, Jupyter, Python, SageMath, Octave, and Julia. For all the bumps and
+                            bruises of open source software, we have your back.

                             <SMC_Commercial />

                             <SMC_Quote />


add a warning message to get around a stupid bug...

--- src/smc-webapp/landing_page.cjsx
+++ src/smc-webapp/landing_page.cjsx
@@ -343,6 +343,9 @@
                     check your spam folder

                 {s[1]}
+
+                    If you have trouble with the reset link you get via email, open it in Private Browser Mode.
+

     hide_forgot_password: ->


Fix more title consistency issues

Fix more title consistency issues

--- src/smc-webapp/projects_nav.cjsx
+++ src/smc-webapp/projects_nav.cjsx
@@ -5,6 +5,7 @@
 # SMC Libraries
 misc = require('smc-util/misc')
 {isMobile} = require('./feature')
+{set_window_title} = require('./browser')

 # SMC Components
 {React, ReactDOM, rclass, rtypes} = require('./smc-react')
@@ -30,10 +31,10 @@
             public_project_titles : rtypes.immutable.Map

     propTypes:
-        project_map           : rtypes.immutable.Map
-        index                 : rtypes.number
-        project_id            : rtypes.string
-        active_top_tab        : rtypes.string
+        project        : rtypes.immutable.Map
+        index          : rtypes.number
+        project_id     : rtypes.string
+        active_top_tab : rtypes.string

     getInitialState : ->
         x_hovered : false
@@ -54,13 +55,17 @@

     render : ->
         title = @props.get_title(@props.project_id)
+        title ?= @props.public_project_titles?.get(@props.project_id)
         if not title?
-            title = @props.public_project_titles?.get(@props.project_id)
-            if not title?
-                return <Loading key={@props.project_id} />
-
-        desc = misc.trunc(@props.project_map?.getIn([@props.project_id, 'description']) ? '', 128)
-        project_state = @props.project_map?.getIn([@props.project_id, 'state', 'state'])
+            if @props.active_top_tab == @props.project_id
+                set_window_title("Loading")
+            return <Loading key={@props.project_id} />
+
+        if @props.active_top_tab == @props.project_id
+            set_window_title(title)
+
+        desc = misc.trunc(@props.project?.get('description') ? '', 128)
+        project_state = @props.project?.getIn(['state', 'state'])
         icon = require('smc-util/schema').COMPUTE_STATES[project_state]?.icon ? 'bullhorn'

         project_name_styles =
@@ -136,7 +141,7 @@
             key            = {project_id}
             project_id     = {project_id}
             active_top_tab = {@props.active_top_tab}
-            project_map    = {@props.project_map}
+            project        = {@props.project_map?.get(project_id)}
             public_project_titles = {@props.public_project_titles}
         />

@@ -163,7 +168,7 @@

 OpenProjectMenuItem = rclass
     propTypes:
-        project_map           : rtypes.immutable.Map
+        project               : rtypes.immutable.Map
         open_projects         : rtypes.immutable.List
         public_project_titles : rtypes.immutable.Map
         index                 : rtypes.number
@@ -184,15 +189,15 @@
         @actions('page').set_active_tab(@props.project_id)

     render : ->
-        title = @props.project_map?.getIn([@props.project_id, 'title'])
+        title = @props.project?.get('title')
         title ?= @props.public_project_titles?.get(@props.project_id)
         if not title?
             # Ensure that at some point we'll have the title if possible (e.g., if public)
             @actions('projects').fetch_public_project_title(@props.project_id)
             return <Loading key={@props.project_id} />

-        desc = misc.trunc(@props.project_map?.getIn([@props.project_id, 'description']) ? '', 128)
-        project_state = @props.project_map?.getIn([@props.project_id, 'state', 'state'])
+        desc = misc.trunc(@props.project?.get('description') ? '', 128)
+        project_state = @props.project?.getIn(['state', 'state'])
         icon = require('smc-util/schema').COMPUTE_STATES[project_state]?.icon ? 'bullhorn'

         project_name_styles =
@@ -244,7 +249,7 @@
             key            = {project_id}
             project_id     = {project_id}
             active_top_tab = {@props.active_top_tab}
-            project_map    = {@props.project_map}
+            project        = {@props.project_map?.get(project_id)}
             open_projects  = {@props.open_projects}
             public_project_titles = {@props.public_project_titles}
         />


Make lint happy.

Make lint happy.

--- src/spec/integration.spec.coffee
+++ src/spec/integration.spec.coffee
@@ -193,7 +193,7 @@
         expect(result[0].statusCode).toBe 304
         expect(result[1].statusCode).toBe 304
         @client.inventoryEntries.fetch()
-      .then (result) =>
+      .then (result) ->
         stocks = result.body.results
         expect(stocks[0].sku).toBe 'myEAN'
         expect(stocks[0].quantityOnStock).toBe -1


documented Utils.formatDuration

documented Utils.formatDuration

--- web/js/utils.coffee
+++ web/js/utils.coffee
@@ -59,8 +59,9 @@
     @nextQuarterOfHour: (d) ->
       new Date(new Date().setMinutes(Math.floor((d.getMinutes() + 14) / 15) * 15))

+    # From a number (of seconds) to a formatted value
     @formatDuration: (d) ->
-      d = d / 60
+      d = d / 60 # Seconds to minutes
       hours = Math.floor d / 60
       mins = Math.round d % 60
       if hours > 0 and mins > 0


增加用户完成数量，并将用户数据分析单独拿出来

增加用户完成数量，并将用户数据分析单独拿出来

--- biz/issue.coffee
+++ biz/issue.coffee
@@ -267,6 +267,32 @@
       return _common.response500 res, err if err
       res.end()

+  #统计分析单个用户的数据
+  statisticOfMember: (project_id, member_id, cb)->
+    result = {}
+    self = @
+    queue = []
+
+    queue.push(
+      (done)-> self.countIssueOfProject project_id, member_id, (err, count)->
+        result.total = count
+        done err
+    )
+
+    queue.push(
+      (done)-> self.countDelayIssueOfProject project_id, member_id, (err, count)->
+        result.delay = count
+        done err
+    )
+
+    queue.push(
+      (done)-> self.countIssueByStatusOfProject project_id, member_id, 'done', (err, count)->
+        result.done = count
+        done err
+    )
+
+    _async.series queue, (err)-> cb err, result
+
   #获取用户的统计数据
   statisticOfMembers: (project_id, data, cb)->
     self = @
@@ -275,17 +301,21 @@
       (-> return index < data.members.length)
       ((done)->
         member = data.members[index++]
-        member_id = member.profile.id
-        member.stat = {}
-        #查询某个用户的issue数量
-        self.countIssueOfProject project_id, member_id, (err, total)->
-          member.stat.total = total
-          self.countDelayIssueOfProject project_id, member_id, (err, delay)->
-            member.stat.delay = delay
-            done err
+        self.statisticOfMember project_id, member.profile.id, (err, stat)->
+          member.stat = stat
+          done err
       )
       cb
     )
+
+  #汇总统计指定project下的issue总数量，不包括status=trash和tag=project的，member可以不指定
+  countIssueByStatusOfProject: (project_id, member_id, status, cb)->
+    sql = "SELECT COUNT(id) FROM issue WHERE tag <> 'project' AND project_id = #{project_id}"
+    status = [status] if typeof status is 'string'
+    sql += " AND status in ('#{status.join('\',\'')}')"
+    sql += " AND owner = #{member_id}" if member_id
+
+    @scalar sql, cb

   #汇总统计指定project下的issue总数量，不包括status=trash和tag=project的，member可以不指定
   countIssueOfProject: (project_id, member_id, cb)->


Fixed wrong argument order in Module._resolveFilename

--- Module.coffee
+++ Module.coffee
@@ -50,7 +50,7 @@
       self.require path

     require.resolve = (request) ->
-      BuiltinModule._resolveFilename self, request
+      BuiltinModule._resolveFilename request, self
     require.main = process.mainModule
     require.extensions = BuiltinModule._extensions
     require.cache = BuiltinModule._cache


Troubleshoot: checkSequence is taken under prepareCheckSequence method

--- client/Main/troubleshoot/troubleshoot.coffee
+++ client/Main/troubleshoot/troubleshoot.coffee
@@ -6,14 +6,21 @@
   [PENDING, STARTED] = [1..2]

   constructor: (options = {}) ->
+    options.timeout ?= 10000 #overall troubleshoot timeout
+    super options
+
     @items = {}
     @status = PENDING
-    options.timeout ?= 10000

-    super options
+    @prepareCheckSequence()
+    @registerItems()
+    # when a user stays idle for an hour we forward userIdle event
+    @idleUserDetector = new IdleUserDetector threshold: 3600000
+    @forwardEvent @idleUserDetector, "userIdle"

-    # this is a tree structured health check sequence.
-    # health check of leaves happens with its successor root
+  # prepareCheckSequence builds a tree structured health check sequence.
+  # With predecessor's successful health check, successors status is checked
+  prepareCheckSequence: ->
     @checkSequence =
       connection       :
         webServer      :
@@ -24,10 +31,6 @@
         bongo          :
           broker       :
             liveUpdate : 0
-
-    @registerItems()
-    @idleUserDetector = new IdleUserDetector threshold: 3600000
-    @forwardEvent @idleUserDetector, "userIdle"

   isSystemOK: ->
     for own name, item of @items


remove codesharewidget from widgetcontroller.

--- client/app/Applications/Activity.kdapplication/widgets/widgetcontroller.coffee
+++ client/app/Applications/Activity.kdapplication/widgets/widgetcontroller.coffee
@@ -42,33 +42,33 @@
             @codeSnippetWidgetSubmit data, stopSubmission
             mainView.resetWidgets()

-    codeSharePane = mainView.addWidgetPane
-      paneName    : "codeshare"
-      mainContent : codeShareWidget = new ActivityCodeShareWidget
-        delegate  : mainView
-        callback  : (data)=>
-          if submissionStopped
-            return notifySubmissionStopped()
-          else
-
-            # this forces the iframe to load the code and execute it
-            codeShareWidget.codeShareResultView.hide()
-            codeShareWidget.codeShareResultView.emit "CodeShareSourceHasChanges", data
-
-            # reset widget tab as if it was submitted
-            mainView.resetWidgets()
-
-            # notify the user
-            notifiy = new KDNotificationView
-              title: "Submitting your Code Share"
-              content: "This may take up to ten seconds. Thank you for your patience!"
-              duration: 5000
-
-            # then wait x seconds
-            window.setTimeout =>
-              #only if the browser/tab did not lock up due to script execution, this will run
-              @codeShareWidgetSubmit data, stopSubmission
-            , 5000
+#    codeSharePane = mainView.addWidgetPane
+#      paneName    : "codeshare"
+#      mainContent : codeShareWidget = new ActivityCodeShareWidget
+#        delegate  : mainView
+#        callback  : (data)=>
+#          if submissionStopped
+#            return notifySubmissionStopped()
+#          else
+#
+#            # this forces the iframe to load the code and execute it
+#            codeShareWidget.codeShareResultView.hide()
+#            codeShareWidget.codeShareResultView.emit "CodeShareSourceHasChanges", data
+#
+#            # reset widget tab as if it was submitted
+#            mainView.resetWidgets()
+#
+#            # notify the user
+#            notifiy = new KDNotificationView
+#              title: "Submitting your Code Share"
+#              content: "This may take up to ten seconds. Thank you for your patience!"
+#              duration: 5000
+#
+#            # then wait x seconds
+#            window.setTimeout =>
+#              #only if the browser/tab did not lock up due to script execution, this will run
+#              @codeShareWidgetSubmit data, stopSubmission
+#            , 5000

     mainView.addWidgetPane
       paneName    : "link"


Draggable scroller

Draggable scroller

--- client/publication.coffee
+++ client/publication.coffee
@@ -177,12 +177,21 @@
   $displayWrapper = $('#viewer .display-wrapper')
   100 * Math.max($(window).scrollTop() - $displayWrapper.offset().top, 0) / $displayWrapper.height()

+scrollToOffset = (offset) ->
+  $(window).scrollTop offset * $('#viewer .display-wrapper').height() / $('#scroller').height()
+
 Template.publicationScroller.created = ->
   $(window).on 'scroll.publicationScroller', (e) =>
     $(@find '.viewport').css 'top', "#{ viewportPositionPercentage() }%"

 Template.publicationScroller.rendered = ->
   if Session.equals 'currentPublicationRendered', true
+    $(@find '.viewport').draggable
+      containment: 'parent'
+      stop: (e) ->
+        e.stopPropagation()
+        console.log e
+        scrollToOffset e.pageY - $(e.target).parent().offset().top
     $(@find '.viewport').height "#{ viewportHeightPercentage() }%"

 Template.publicationScroller.destroyed = ->
@@ -200,9 +209,9 @@

 Template.publicationScroller.events
   'click': (e, template) ->
-    $src = $(e.srcElement)
-    offset = if $src.is('#scroller') then e.offsetY else e.pageY - $src.parent().offset().top
-    $(window).scrollTop offset * $('#viewer .display-wrapper').height() / $('#scroller').height()
+    $target = $(e.target)
+    offset = if $target.is('#scroller') then e.offsetY else e.pageY - $target.parent().offset().top
+    scrollToOffset offset

 Template.publicationAnnotations.annotations = ->
   Annotations.find


:fire: Unused method

:fire: Unused method

Signed-off-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- src/text-editor-component.coffee
+++ src/text-editor-component.coffee
@@ -926,9 +926,6 @@
     @linesYardstick.invalidateCache()
     @presenter.measurementsChanged()

-  setShowIndentGuide: (showIndentGuide) ->
-    @config.set("editor.showIndentGuide", showIndentGuide)
-
   setScrollSensitivity: (scrollSensitivity) =>
     if scrollSensitivity = parseInt(scrollSensitivity)
       @scrollSensitivity = Math.abs(scrollSensitivity) / 100


getInitialState for Peacemarker

getInitialState for Peacemarker

--- client/src/sidebar.coffee
+++ client/src/sidebar.coffee
@@ -263,6 +263,22 @@
 #
 #
 Peacemarker = React.createFactory React.createClass
+
+    # getInitialState: ->
+    #     return {
+    #         title: @props.peacemarker.title
+    #         lat: @props.peacemarker.lat
+    #         lng: @props.peacemarker.lng
+    #         zoom: @props.peacemarker.zoom
+    #         address: @props.peacemarker.address
+    #         postalcode: @props.peacemarker.postalcode
+    #         city: @props.peacemarker.city
+    #         state: @props.peacemarker.state
+    #         country: @props.peacemarker.country
+    #         phone: @props.peacemarker.phone
+    #         email: @props.peacemarker.email
+    #         website: @props.peacemarker.website
+    #     }

     render: -> null
         # div id: 'sidebar-places',


DELETE seems much faster

DELETE seems much faster

--- lib/locparse.coffee
+++ lib/locparse.coffee
@@ -200,7 +200,7 @@
 	save: (dbConnection) ->
 		throw new Error("Can't save with errors.") if @errors.length > 0

-		dbConnection.query.sync(dbConnection, "TRUNCATE areas", [])
+		dbConnection.query.sync(dbConnection, "DELETE FROM areas", [])
 		for area in @areas
 			dbConnection.query.sync(
 				dbConnection
@@ -211,7 +211,7 @@
 		locByLabel = {}
 		locByLabel[loc.label] = loc for loc in @locations

-		dbConnection.query.sync(dbConnection, "TRUNCATE locations", [])
+		dbConnection.query.sync(dbConnection, "DELETE FROM locations", [])
 		for loc in @locations
 			ways = ({target: locByLabel[k].id, text: v} for k,v of loc.actions)
 			dbConnection.query.sync(


host -> hostname

host -> hostname

--- models/badge.coffee
+++ models/badge.coffee
@@ -84,7 +84,7 @@
   @image.original.defaultUrl

 BadgeSchema.virtual('criteriaUrl').get ->
-  "http://#{configuration.get('host')}/badges/#{@slug}/criteria"
+  "http://#{configuration.get('hostname')}/badges/#{@slug}/criteria"

 BadgeSchema.pre 'save', (next)->
   if @criteria?


Update - D:09032021-H:200337

Update - D:09032021-H:200337

--- assets/js/contact.coffee
+++ assets/js/contact.coffee
@@ -4,36 +4,36 @@
 # Using Coffeescript
 # {% include liquid/data %}

-# {% if load_data.website.content.contact.formspree.plan != 'free' %}
-#     ($) ->
-#         $('form').submit (e) ->
-#             event.preventDefault()
-#             $.ajax
-#                 url: 'https://formspree.io/f/mvovezaq'
-#                 method: 'POST'
-#                 data: {
-#                     name: $('#name').val()
-#                     subject: $('#subject').val()
-#                     email: $('#email').val()
-#                     message: $('#message').val()
-#                 }
-#                 dataType: 'json'
-#             .done ->
-#                 $('#name').val('')
-#                 $('#subject').val('')
-#                 $('#email').val('')
-#                 $('#message').val('')
-#                 $('#email_successfully_sent').modal('show')
-#             .fail ->
-#                 $('#email_failed_sent').modal('show')
-#         false
-# {% else %}
-#     if location.href.indexOf("{{ '/contact/#email_successfully_sent' | prepend: site.baseurl | prepend: site.url }}") == 0
-#         $("#email_successfully_sent").modal "show"
-#         $('#email_successfully_sent').on 'hidden.bs.modal', ->
-#             document.location.href = String(document.location.href).replace '#email_successfully_sent', ''
-#             false
-# {% endif %}
+{% if load_data.website.content.contact.formspree.plan != 'free' %}
+    ($) ->
+        $('form').submit (e) ->
+            event.preventDefault()
+            $.ajax
+                url: 'https://formspree.io/f/mvovezaq'
+                method: 'POST'
+                data: {
+                    name: $('#name').val()
+                    subject: $('#subject').val()
+                    email: $('#email').val()
+                    message: $('#message').val()
+                }
+                dataType: 'json'
+            .done ->
+                $('#name').val('')
+                $('#subject').val('')
+                $('#email').val('')
+                $('#message').val('')
+                $('#email_successfully_sent').modal('show')
+            .fail ->
+                $('#email_failed_sent').modal('show')
+        false
+{% else %}
+    if location.href.indexOf("{{ '/contact/#email_successfully_sent' | prepend: site.baseurl | prepend: site.url }}") == 0
+        $("#email_successfully_sent").modal "show"
+        $('#email_successfully_sent').on 'hidden.bs.modal', ->
+            document.location.href = String(document.location.href).replace '#email_successfully_sent', ''
+            false
+{% endif %}


metric: enable d1 retention

metric: enable d1 retention

--- src/models/metric.coffee
+++ src/models/metric.coffee
@@ -34,22 +34,22 @@
         #     select: 'count(distinct(userId))'
         #     from: 'revenue'
         # }
-        # {
-        #   name: 'D1 Retention'
-        #   isRunningAverage: true
-        #   numerator:
-        #     select: 'count(distinct(userId))'
-        #     from: 'view'
-        #     where: (day) ->
-        #       "time >= #{day}d AND time < #{day + 1}d AND " +
-        #       "joinDay = '#{day - 1}'"
-        #   denominator:
-        #     select: 'count(distinct(userId))'
-        #     from: 'view'
-        #     where: (day) ->
-        #       "time >= #{day - 1}d AND time < #{day}d AND " +
-        #       "joinDay = '#{day - 1}'"
-        # }
+        {
+          name: 'D1 Retention'
+          isRunningAverage: true
+          numerator:
+            select: 'count(distinct(userId))'
+            from: 'view'
+            where: (day) ->
+              "time >= #{day}d AND time < #{day + 1}d AND " +
+              "joinDay = '#{day - 1}'"
+          denominator:
+            select: 'count(distinct(userId))'
+            from: 'view'
+            where: (day) ->
+              "time >= #{day - 1}d AND time < #{day}d AND " +
+              "joinDay = '#{day - 1}'"
+        }
         # {
         #   name: '3d LTV'
         #   isRunningAverage: true


Add spec coverage for deprecated ‘cursor:moved’ event

So we don’t accidentally drop it during this transition

--- spec/text-editor-component-spec.coffee
+++ spec/text-editor-component-spec.coffee
@@ -656,7 +656,7 @@
           expect(lineNumberHasClass(1, 'folded')).toBe false

   describe "cursor rendering", ->
-    it "renders the currently visible cursors, translated relative to the scroll position", ->
+    it "renders the currently visible cursors", ->
       cursor1 = editor.getLastCursor()
       cursor1.setScreenPosition([0, 5])

@@ -693,9 +693,16 @@
       expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{11 * charWidth}px, #{8 * lineHeightInPixels}px)"
       expect(cursorNodes[1].style['-webkit-transform']).toBe "translate(#{10 * charWidth}px, #{4 * lineHeightInPixels}px)"

+      wrapperView.on 'cursor:moved', cursorMovedListener = jasmine.createSpy('cursorMovedListener')
+      cursor3.setScreenPosition([4, 11], autoscroll: false)
+      nextAnimationFrame()
+      expect(cursorNodes[1].style['-webkit-transform']).toBe "translate(#{11 * charWidth}px, #{4 * lineHeightInPixels}px)"
+      expect(cursorMovedListener).toHaveBeenCalled()
+
       cursor3.destroy()
       nextAnimationFrame()
       cursorNodes = componentNode.querySelectorAll('.cursor')
+
       expect(cursorNodes.length).toBe 1
       expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{11 * charWidth}px, #{8 * lineHeightInPixels}px)"


full screen node mode

full screen node mode

--- assets/js/student.coffee
+++ assets/js/student.coffee
@@ -50,11 +50,11 @@
     data = $("#scratch").val()
     eval data
 $("#nerd").toggle (event) ->
-    $("#scratch").css({"color":"#0F0", "background-color":"#000", "font":"console"})
-    $("#console").css({"color":"#0F0", "background-color":"#000", "font":"console"})
+    $('body').css({"color":"#0F0", "background-color":"#000", "font":"console"})
+    $('body').css({"color":"#0F0", "background-color":"#000", "font":"console"})
     $(this).text("normal mode")
 , (event) ->
-    $("#scratch").css({"color":"#000", "background-color":"#fff", "font":"helvetica"})
-    $("#console").css({"color":"#000", "background-color":"#fff", "font":"helvetica"})
+    $('body').css({"color":"#000", "background-color":"#fff", "font":"helvetica"})
+    $('body').css({"color":"#000", "background-color":"#fff", "font":"helvetica"})
     $(this).text("nerd mode")


anonymous self calling function inside dynamically lexed scope block... it's so obvious

--- assets/js/teacher.coffee
+++ assets/js/teacher.coffee
@@ -51,12 +51,13 @@
             .each () ->
                     console.log "calling click toggle listener on id #{id}"
                     console.log $(this)
-                    ((closed_id) ->
-                      $("#nick-#{id}").toggle () ->
+                    nick = $(this).find "#nick-#{id}"
+                    ((closed_id, closed_nick) ->
+                      $(nick).toggle () ->
                         console.log "the closure id is #{closed_id}"
                         $("#text-container-#{closed_id}").show("explode", 1000);
                       , () ->
-                        $("#text-container-#{closed_id}").hide("explode", 1000);)(id);
+                        $("#text-container-#{closed_id}").hide("explode", 1000);)(id, nick);
     #also, delete any ids that are still client side but have disconnected from the server
     _.map $('.container').find('.student-box'),
           (el) ->