remove a method

remove a method

--- client/audio.coffee
+++ client/audio.coffee
@@ -1,36 +1,6 @@
 audioContext = null
 canvas = null
 canvasContext = null
-
-copyBuffers = (buffers, dest) ->
-  offset = 0
-  for buffer in buffers
-    dest.set buffer, offset
-    offset += buffer.length
-
-  dest
-
-connectRecorder = (stream) ->
-  audioContext = new AudioContext()
-
-  hzElement = document.getElementById("hz")
-  noteElement = document.getElementById("note")
-
-  bufferSize = 2048
-  recorder = audioContext.createScriptProcessor(bufferSize, 2, 2)
-  recorder.onaudioprocess = (e) ->
-    left = e.inputBuffer.getChannelData(0)
-    drawWave(left)
-    hz = Pitcher.pitch(left, audioContext.sampleRate)
-    return unless hz >= 30
-    note = new Note(hz)
-    hzElement.innerHTML = 'hz = ' + hz
-    noteElement.innerHTML = 'note = ' + note.name()
-
-  # connect the recorder
-  input = audioContext.createMediaStreamSource(stream)
-  input.connect recorder
-  recorder.connect audioContext.destination

 setPixel = (imageData, x, y, color) ->
   width = imageData.width
@@ -62,6 +32,28 @@

   canvasContext.putImageData imageData, 0, 0

+connectRecorder = (stream) ->
+  audioContext = new AudioContext()
+
+  hzElement = document.getElementById("hz")
+  noteElement = document.getElementById("note")
+
+  bufferSize = 2048
+  recorder = audioContext.createScriptProcessor(bufferSize, 2, 2)
+  recorder.onaudioprocess = (e) ->
+    left = e.inputBuffer.getChannelData(0)
+    drawWave(left)
+    hz = Pitcher.pitch(left, audioContext.sampleRate)
+    return unless hz >= 30
+    note = new Note(hz)
+    hzElement.innerHTML = 'hz = ' + hz
+    noteElement.innerHTML = 'note = ' + note.name()
+
+  # connect the recorder
+  input = audioContext.createMediaStreamSource(stream)
+  input.connect recorder
+  recorder.connect audioContext.destination
+
 @common.init = ->
   navigator.getUserMedia = navigator.getUserMedia or navigator.webkitGetUserMedia or navigator.mozGetUserMedia or navigator.msGetUserMedia unless navigator.getUserMedia
   window.AudioContext = window.webkitAudioContext unless window.AudioContext


Updated a copyright notice.

Updated a copyright notice.

--- app/assets/javascripts/jquery.multifile.js.coffee
+++ app/assets/javascripts/jquery.multifile.js.coffee
@@ -1,6 +1,6 @@
 ###
  Author: Olaf Delgado-Friedrichs (odf@github.com)
- Copyright (c) 2010 ANU
+ Copyright (c) 2011 The Australian National University

  Permission is hereby granted, free of charge, to any person obtaining
  a copy of this software and associated documentation files (the


add support for callbacks in parseString

--- src/xml2js.coffee
+++ src/xml2js.coffee
@@ -112,5 +112,12 @@
       if s
         s[charkey] += text

-  parseString: (str) =>
+  parseString: (str, cb) =>
+    if cb? and typeof cb is 'function'
+      @on 'end', (result) ->
+        cb null, result
+      @on 'error', (err) ->
+        cb err
+
     @saxParser.write str.toString()
+


Fix for #294, fix for remembering filter collapse state

--- app/assets/javascripts/modules/filter_form.coffee
+++ app/assets/javascripts/modules/filter_form.coffee
@@ -14,19 +14,19 @@
     expandLabel = I18n.t 'js.more_filter_options'
     collapseLabel = I18n.t 'js.less_filter_options'
     offer_filter_open = sessionStorage.getItem("offer_filter_open")
+    label = expandLabel

-    # Detect or set state: offer_filter_open
-    unless offer_filter_open == "true"
+    # Detect state, start expanded?
+    if offer_filter_open == "true"
       $filterForm
-              .addClass 'filter-form--isCollapsed'
-              .css 'height', startHeight
+              .removeClass 'filter-form--isCollapsed'
+              .css 'height', 'auto'
       $('.filter-form__expander').attr 'aria-expanded', true
-
-      sessionStorage.setItem("offer_filter_open", "false")
+      label = collapseLabel

     # Prepend expander button dynamically
     if !$('.filter-form__expander').length
-      $filterForm.prepend '
' + expandLabel + '
'
+      $filterForm.prepend '
' + label + '
'

       if offer_filter_open == "true"
         $('.filter-form__expander')


Naviation changes

Naviation changes

--- app/assets/javascripts/spina/navigation.js.coffee
+++ app/assets/javascripts/spina/navigation.js.coffee
@@ -4,11 +4,14 @@

 $(document).on 'click', 'nav#primary > ul > li > a', (e) ->
   link = $(this)
+  $('nav#primary > ul > li').removeClass('active')
+  link.parent().addClass('active')
+
   if link.parent().find('ul').length > 0
-    $('nav#primary > ul > li').removeClass('active')
-    link.parent().addClass('active')
     $('nav#primary').addClass('animated')
     e.preventDefault()
+  else
+    $('nav#primary').removeClass('animated')

 $(document).on 'click', 'nav#primary ul li a', (e) ->
   $('nav#primary ul li ul li').removeClass('active')


do not fix relative urls

do not fix relative urls

--- app/assets/javascripts/uploadcare/utils.js.coffee
+++ app/assets/javascripts/uploadcare/utils.js.coffee
@@ -112,7 +112,7 @@
     new RegExp("^" + parts.join('.+') + "$", flags)

   ns.normalizeUrl = (url) ->
-    # google.com/ → http://google.com
+    # google.com/ → google.com
     # /google.com/ → /google.com
     # //google.com/ → http://google.com
     # http://google.com/ → http://google.com
@@ -120,13 +120,7 @@
     if scheme != 'http:'
       scheme = 'https:'

-    parts = url.match(/^([^:/?#]+:)?\/?(\/)?/)
-    if not parts[1]  # if no scheme
-      if parts[2]  # if two slashes
-        url = "#{scheme}#{url}"
-      else if not parts[0] # if no one slash
-        url = "#{scheme}//#{url}"
-    url.replace(/\/+$/, '')
+    url.replace(/^\/\//, scheme + '//').replace(/\/+$/, '')

   ns.fitText = (text, max) ->
     if text.length > max


Removed test var

Removed test var

--- src/core/javascripts/models/company.js.coffee
+++ src/core/javascripts/models/company.js.coffee
@@ -7,13 +7,10 @@

     constructor: (data) ->
       super(data)
-      @test = 1

       # instantiate each child company as a hal resource
       if @companies
         @companies = _.map @companies, (c) -> new BBModel.Company(halClient.$parse(c))
-
-

     getCompanyByRef: (ref) ->
       defer = $q.defer()


Replace npmcdn.com with unpkg.com

Replace npmcdn.com with unpkg.com

--- serve-sketches.coffee
+++ serve-sketches.coffee
@@ -71,7 +71,7 @@
 ul li { margin-bottom: 0.5em }
 .links { font-size: x-large; margin-top: 1em; margin-bottom: 1em; clear: both }</style>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
-  <script src="https://npmcdn.com/imagesloaded@4.1/imagesloaded.pkgd.min.js"></script>
+  <script src="https://unpkg.com/imagesloaded@4.1/imagesloaded.pkgd.min.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/masonry/4.0.0/masonry.pkgd.min.js"></script>
   <script>
     $(document).ready(function() {


Tune health to prevent flying down too soon

--- app/scripts/lazerscripts/stage1/stage1boss.coffee
+++ app/scripts/lazerscripts/stage1/stage1boss.coffee
@@ -630,8 +630,8 @@

   spawn: ->
     Crafty.e('LargeDrone, Horizon').drone(
-      maxHealth: 60000
-      health: 12000
+      maxHealth: 180000
+      health: 36000
       x: Crafty.viewport.width + 40
       y: Crafty.viewport.height * .5
       defaultSpeed: 150


:bug: Fix a typo in EditorLinter::_lock

:bug: Fix a typo in EditorLinter::_lock

--- lib/editor-linter.coffee
+++ lib/editor-linter.coffee
@@ -63,7 +63,7 @@

   # This method sets or gets the lock status of given type
   _lock: (wasTriggeredOnChange, value) ->
-    key = wasTriggeredOnChange ? '_inProgressFly' : 'inProgress'
+    key = wasTriggeredOnChange ? '_inProgressFly' : '_inProgress'
     if typeof value is 'undefined'
       @[key]
     else


Limit num hit message to the max.

Limit num hit message to the max.

--- server/bw/move.coffee
+++ server/bw/move.coffee
@@ -64,7 +64,8 @@
       numHits = @calculateNumberOfHits(battle, user, targets)
       for hitNumber in [1..numHits]
         @hit(battle, user, target, hitNumber)
-      if numHits > 1 then battle.message @numHitsMessage(hitNumber)
+      if numHits > 1
+        battle.message @numHitsMessage Math.min(hitNumber, numHits)

     # If the move hit 1+ times, query the user's afterAllHits event.
     # If the user is affected by Sheer Force, these are all ignored.


Call the RadBus API with our API Key

Call the RadBus API with our API Key

Fixes #16

--- oauth2.coffee
+++ oauth2.coffee
@@ -8,7 +8,12 @@
   server.get "/oauth2callback", (req, res) ->
     console.log "#{LOG_PREFIX} Getting OAuth2 info from API..."

-    request.get "#{process.env.RADBUS_API_BASE_URL}/oauth2", (err, response, body) ->
+    options =
+      url: "#{process.env.RADBUS_API_BASE_URL}/oauth2"
+      headers:
+        'API-Key': process.env.RADBUS_API_KEY
+
+    request.get options, (err, response, body) ->
       if (err) then throw err

       oAuth2Info = JSON.parse body


removed bad stream parsing of agent response

--- server/cluster.coffee
+++ server/cluster.coffee
@@ -17,7 +17,7 @@
   agent

 @Cluster =
-  startApp: (app, version, instance, parameters, options = {}) ->
+startApp: (app, version, instance, parameters, options = {}) ->
     options = _.extend {"dataDir": Settings.findOne().dataDir}, options
     dir = "#{Settings.findOne().project}-#{instance}"
     console.log "Cluster.startApp #{app}, #{version}, #{instance}, #{EJSON.stringify options}, #{EJSON.stringify parameters} in project #{Settings.findOne().project}."
@@ -33,12 +33,8 @@
     console.log "Sending request to #{options.agentUrl}"

     HTTP.post "#{options.agentUrl}/app/install-and-run", callOpts, (err, result) ->
-      console.log "Sent request to start instance. Response from the agent is"
+      console.log "Sent request to start instance. Response from the agent is", result.content.toString()
       console.log err if err
-      s = JSONStream.parse()
-      s.on 'data', (data) -> console.log 'data', data
-      s.on 'error', (err) -> console.log 'err', err
-      str2stream("#{result.content}").pipe(s)
       Instances.update {name: instance, application: app},
         $set: {'logs.bootstrapLog': "#{result.content}"}
     ""


[Gutter] Make CustomGutterComponent::buildDecorationNode call into ::updateDecorationNode

--- src/custom-gutter-component.coffee
+++ src/custom-gutter-component.coffee
@@ -62,39 +62,32 @@

   # Builds and returns an HTMLElement to represent the specified decoration.
   buildDecorationNode: (decorationId, decorationInfo) ->
-    @oldDecorationPositionState[decorationId] = positionState = {};
+    @oldDecorationPositionState[decorationId] = {};
     newNode = document.createElement('div')
-    newNode.classList.add('decoration')
-    newNode.style.top = decorationInfo.top + 'px'
-    positionState.top = decorationInfo.top + 'px'
-    newNode.style.height = decorationInfo.height + 'px'
-    positionState.height = decorationInfo.height + 'px'
     newNode.style.position = 'absolute'
-    if decorationInfo.class
-      newNode.classList.add(decorationInfo.class)
-    @setDecorationItem(decorationInfo.item, decorationInfo.height, decorationId, newNode)
+    @updateDecorationNode(newNode, decorationId, decorationInfo)
     newNode

   # Updates the existing HTMLNode with the new decoration info. Attempts to
   # minimize changes to the DOM.
-  updateDecorationNode: (existingNode, decorationId, newDecorationInfo) ->
+  updateDecorationNode: (node, decorationId, newDecorationInfo) ->
     oldPositionState = @oldDecorationPositionState[decorationId];

     if oldPositionState.top isnt newDecorationInfo.top + 'px'
-      existingNode.style.top = newDecorationInfo.top + 'px'
+      node.style.top = newDecorationInfo.top + 'px'
       oldPositionState.top = newDecorationInfo.top + 'px'

     if oldPositionState.height isnt newDecorationInfo.height + 'px'
-      existingNode.style.height = newDecorationInfo.height + 'px'
+      node.style.height = newDecorationInfo.height + 'px'
       oldPositionState.height = newDecorationInfo.height + 'px'

-    if newDecorationInfo.class and not existingNode.classList.contains(newDecorationInfo.class)
-      existingNode.className = 'decoration'
-      existingNode.classList.add(newDecorationInfo.class)
+    if newDecorationInfo.class and not node.classList.contains(newDecorationInfo.class)
+      node.className = 'decoration'
+      node.classList.add(newDecorationInfo.class)
     else if not newDecorationInfo.class
-      existingNode.className = 'decoration'
+      node.className = 'decoration'

-    @setDecorationItem(newDecorationInfo.item, newDecorationInfo.height, decorationId, existingNode)
+    @setDecorationItem(newDecorationInfo.item, newDecorationInfo.height, decorationId, node)

   # Sets the decorationItem on the decorationNode.
   # If `decorationItem` is undefined, the decorationNode's child item will be cleared.


Mesh: Clone mesh members of object

Mesh: Clone mesh members of object

--- engine/mesh.coffee
+++ engine/mesh.coffee
@@ -447,4 +447,10 @@

         return amesh

+    clone: (scene, recursive) ->
+        clone = super scene, recursive
+        clone.uv_rect = @uv_rect[...]
+        clone.uv_right_eye_offset = @uv_right_eye_offset[...]
+        return clone
+
 module.exports = {Mesh}


Mesh: Skip checking lod_objects if it's empty.

--- engine/mesh.coffee
+++ engine/mesh.coffee
@@ -317,7 +317,7 @@
         amesh = @
         if @altmeshes.length
             amesh = @altmeshes[@active_mesh_index] or @
-        else if @lod_objects
+        else if @lod_objects.length != 0
             {camera} = viewport
             cam_pos = camera.world_matrix.subarray(12,15)
             # Approximation to nearest point to the surface:


Machine: Fat arrow

Machine: Fat arrow

--- workers/social/lib/social/models/computeproviders/machine.coffee
+++ workers/social/lib/social/models/computeproviders/machine.coffee
@@ -295,7 +295,7 @@
       callback new KodingError \
         "Machine sharing is limited up to 10 users."
     else
-      @update $set: { users }, (err)->
+      @update $set: { users }, (err) =>
         informAccounts targets, @getAt 'uid'
         callback err

@@ -309,7 +309,7 @@
     for user in targets
       users = excludeUser { users, user, permanent }

-    @update $set: { users }, (err)->
+    @update $set: { users }, (err) =>
       informAccounts targets, @getAt 'uid'  if inform
       callback err


SocialWorker: rename isValid to validate and return an object

--- workers/social/lib/social/models/computeproviders/managed.coffee
+++ workers/social/lib/social/models/computeproviders/managed.coffee
@@ -9,18 +9,18 @@
 KONFIG            = require('koding-config-manager').load("main.#{argv.c}")

-isValid = ({ ipAddress, queryString, storage }) ->
+validate = ({ ipAddress, queryString, storage }) ->

   if ipAddress? and (ipAddress.split '.').length isnt 4
-    return new KodingError 'Provided IP is not valid', 'WrongParameter'
+    return { err : new KodingError 'Provided IP is not valid', 'WrongParameter' }

   if queryString? and (queryString.split '/').length isnt 8
-    return new KodingError 'Provided queryString is not valid', 'WrongParameter'
+    return { err : new KodingError 'Provided queryString is not valid', 'WrongParameter' }

   if storage? and isNaN +storage
-    return new KodingError 'Provided storage is not valid', 'WrongParameter'
+    return { err : new KodingError 'Provided storage is not valid', 'WrongParameter' }

-  return null
+  return { err : null }

 getKiteIdOnly = (queryString) ->
   "///////#{queryString.split('/').reverse()[0]}"
@@ -40,8 +40,8 @@
     { label, queryString, ipAddress } = options
     { r: { group, user, account } } = client

-    if err = isValid { queryString, ipAddress }
-      return callback err
+    { err } = validate { queryString, ipAddress }
+    return callback err  if err

     queryString = getKiteIdOnly queryString
     provider    = @providerSlug
@@ -123,8 +123,8 @@
       return callback new KodingError \
         'A valid machineId and an update option is required.', 'WrongParameter'

-    if err = isValid { ipAddress, queryString, storage }
-      return callback err
+    { err } = validate { ipAddress, queryString, storage }
+    return callback err  if err

     fieldsToUpdate = {}


add last update time

add last update time

--- src/devtools/panel/app/data.coffee
+++ src/devtools/panel/app/data.coffee
@@ -102,6 +102,8 @@
       "vsteel":       null
       "vcoolant":     null
       "vfile":        null
+      # 上次更新时间
+      "last_update": null
     }
     # 刀剑
     sword: {
@@ -176,6 +178,8 @@
       }
       # 数据填充位置
       "data": {}
+      # 上次更新时间
+      "last_update": null
     }
     # 刀装
     equip: {
@@ -190,6 +194,8 @@
       }
       # 数据填充位置
       "data": {}
+      # 上次更新时间
+      "last_update": null
     }
     # 队伍
     party: {
@@ -228,6 +234,8 @@
       }
       # 数据填充位置
       "data": {}
+      # 上次更新时间
+      "last_update": null
     }
     # 锻造
     forge: {
@@ -236,6 +244,8 @@
       "open": null
       # 数据填充位置
       "data": {}
+      # 上次更新时间
+      "last_update": null
     }
     # 手入
     repair: {
@@ -244,6 +254,8 @@
       "open": null
       # 数据填充位置
       "data": {}
+      # 上次更新时间
+      "last_update": null
     }
     # 远征
     conquest: {
@@ -254,6 +266,8 @@
       }
       # 数据填充位置
       "data": {}
+      # 上次更新时间
+      "last_update": null
     }
     # 状态
     status: {


NEW: pageUp and pageDown animators for ScrollElement

--- source/Art/Engine/Elements/Widgets/ScrollElement.coffee
+++ source/Art/Engine/Elements/Widgets/ScrollElement.coffee
@@ -335,8 +335,9 @@

   ###################
   ###################
-  animateToValidScrollPosition: ->
-    {boundedScrollPosition, scrollPosition} = @
+  animateToValidScrollPosition: (desiredOffset = 0)->
+    {scrollPosition} = @
+    boundedScrollPosition = @boundSp scrollPosition + desiredOffset
     if boundedScrollPosition != scrollPosition
       @animators = merge originialAnimators = @animators,
         scrollPosition: on: done: => @animators = originialAnimators
@@ -397,6 +398,9 @@
     @_lastPointerEventTime = null
     @_flickSpeed = 0

+  pageDown: -> @animateToValidScrollPosition -@windowSize
+  pageUp:   -> @animateToValidScrollPosition @windowSize
+
   getMainCoordinate: (pnt) ->
     if @isVertical
       pnt.y


Add debug for iPad 1

Add debug for iPad 1

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

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

'
 	divOff = '

'
@@ -48,6 +49,7 @@

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

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


Clean up function comments

Clean up function comments

--- src/stdlib/fs-utils.coffee
+++ src/stdlib/fs-utils.coffee
@@ -23,12 +23,11 @@
     catch e
       relativePath

-  # Returns true if the file specified by path exists
+  # Returns true if a file or folder at the specified path exists.
   exists: (pathToCheck) ->
     pathToCheck? and fs.existsSync(pathToCheck)

-  # Returns true if the file specified by path exists and is a
-  # directory.
+  # Returns true if the specified path is a directory that exists.
   isDirectorySync: (directoryPath) ->
     return false unless directoryPath?.length > 0
     try
@@ -48,8 +47,7 @@
       else
         done(false)

-  # Returns true if the file specified by path exists and is a
-  # regular file.
+  # Returns true if the specified path is a regular file that exists.
   isFileSync: (filePath) ->
     return false unless filePath?.length > 0
     try
@@ -57,7 +55,7 @@
     catch e
       false

-  # Returns true if the specified path is exectuable.
+  # Returns true if the specified path is executable.
   isExecutableSync: (pathToCheck) ->
     return false unless pathToCheck?.length > 0
     try
@@ -65,8 +63,8 @@
     catch e
       false

-  # Returns an array with all the names of files contained
-  # in the directory path.
+  # Returns an array with the paths of the files and folders
+  # contained in the directory path.
   listSync: (rootPath, extensions) ->
     return [] unless @isDirectorySync(rootPath)
     paths = fs.readdirSync(rootPath)
@@ -145,7 +143,7 @@

       sourceStream.pipe(destinationStream)

-  # Creates the directory specified by "path" including any missing parent
+  # Create a directory at the specified path including any missing parent
   # directories.
   makeTree: (directoryPath) ->
     mkdirp.sync(directoryPath) if directoryPath and not @exists(directoryPath)


Mark (F)ailed errors as errors

Mark (F)ailed errors as errors

--- lib/linter-pylama.coffee
+++ lib/linter-pylama.coffee
@@ -7,7 +7,7 @@
   '(?<line>\\d+):' +
   '(?<col>\\d+):' +
   '\\s+' +
-  '((((?<type>E)|(?<type>[CDFNW]))(?<file>\\d+)(:\\s+|\\s+))|(.*?))' +
+  '(((?<type>[ECDFNW])(?<file>\\d+)(:\\s+|\\s+))|(.*?))' +
   '(?<message>.+)' +
   '(\r)?\n'

@@ -164,7 +164,7 @@
       console.log output if do atom.inDevMode
       helpers.parse(output, regex).map (message) ->
         code = "#{message.type}#{message.filePath}"
-        message.type = if message.type == 'E'
+        message.type = if message.type in ['E', 'F']
           'Error'
         else
           'Warning'


remove unused version

remove unused version

--- src/steroids/Config.coffee
+++ src/steroids/Config.coffee
@@ -9,10 +9,6 @@
 module.exports = class Config

   constructor: ->
-    @version = if fs.existsSync(paths.application.configs.app)
-      "supersonic"
-    else
-      "legacy"

   getCurrent: =>
     config = @eitherSupersonicOrLegacy().fold(


fix display（los

fix display（los

--- views/henseiItem.cjsx
+++ views/henseiItem.cjsx
@@ -151,7 +151,7 @@
       {
         for fleet, index in @props.deckItem.ships
           break if !fleet[0]?
-          if @props.deckItem.details.length > 3
+          if @props.deckItem.details[index].length > 3
             totalLv = @props.deckItem.details[index][0]
             fpTotal = @props.deckItem.details[index][2]
             fpBasic = fpAlv = null


Add date, Update default props, Edit CTA

--- extras/Studio.framer/app.coffee
+++ extras/Studio.framer/app.coffee
@@ -4,7 +4,8 @@
 	twitter: "jornvandijk"
 	title: "Android Tabs"
 	description: "Here's a new Framer example. It's a little grid of photos, which you can scroll, click and pinch to zoom. Made to highlight some of our latest features: scroll and click separation, pinchable layers, event shortcuts and more."
-	local: true
+# 	local: true
+	date: "Jun 14 2016"

 # Setup
 context = new Framer.Context({name: "Sharing"})
@@ -13,15 +14,16 @@
 class ShareLayer extends Layer
 	constructor: (options) ->
 		super options
-
+
 		defaultProps =
 			backgroundColor: null
-			width: options.parent.width if options.parent
+			width: options.parent.width if options and options.parent
 			style:
 				fontFamily: "Roboto, Helvetica Neue, Helvetica, Arial, sans-serif"
 				fontSize: "14px"
 				color: "#111"
 				webkitUserSelect: "text"
+				lineHeight: "1"

 		mergedProps = _.merge(defaultProps, options)
 		@props = mergedProps
@@ -33,7 +35,7 @@
 		# When this is set to true, the sheet won't hide on resize
 		# This is triggered by a manual open / close action
 		@fixed = false
-
+
 		@render()
 		@_startListening()

@@ -43,6 +45,7 @@
 		@_renderCTA()
 		@_renderInfo()
 		@_renderDescription() if @shareInfo.description
+		@_renderDate()
 		@_renderDownload() if !@shareInfo.local

 		# Evaluate content and set height accordingly
@@ -96,7 +99,7 @@
 			parent: @sheet
 			style:
 				borderBottom: "1px solid #E8E8E8"
-			height: 130
+			height: 125

 		ctaLogo = new Layer
 			parent: @cta
@@ -143,7 +146,6 @@
 			html: @shareInfo.title
 			style:
 				fontWeight: "500"
-				lineHeight: "1"

 		# Check what info is available and render layers accordingly
 		showAuthor = (content = @shareInfo.author) =>
@@ -157,7 +159,6 @@
 				y: @credentialsTitle.maxY
 				height: 18
 				style:
-					lineHeight: "1"
 					color: "#808080"

 			@_showPointer(@credentialsAuthor)
@@ -189,6 +190,21 @@
 		# If there's no Twitter handle, but there is an author. Just show author.
 		if @shareInfo.author and !@shareInfo.twitter
 			showAuthor(@shareInfo.author)
+
+	_renderDate: ->
+
+		verticalPosition = if @description then @description.maxY else @credentials.maxY
+
+		@date = new ShareLayer
+			parent: @info
+			html: "Shared on #{@shareInfo.date}"
+			height: 10
+			style:
+				textTransform: "uppercase"
+				fontSize: "11px"
+				color: "#999"
+				letterSpacing: ".2px"
+			y: verticalPosition + 16

 	_renderDescription: ->
 		@description = new ShareLayer
@@ -203,12 +219,9 @@
 		@description.height = descriptionTextSize.height

 	_renderDownload: ->
-
-		verticalPosition = if @description then @description.maxY else @credentials.maxY
-
 		@download = new ShareLayer
 			parent: @info
-			y: verticalPosition + 20
+			y: @date.maxY + 20
 			height: 33
 			borderRadius: 3
 			backgroundColor: "00AAFF"
@@ -216,7 +229,7 @@
 			style:
 				fontWeight: "500"
 				textAlign: "center"
-				paddingTop: "2px"
+				paddingTop: "8px"
 				color: "#FFF"

 		@_showPointer(@download)
@@ -232,8 +245,9 @@

 		# Toggle sheet
 		@close.onClick =>
+			@fixed = true
 			@_closeSheet()
-			@fixed = true
+
 		@framerButton.onClick =>
 			@fixed = true
 			@_openSheet()
@@ -242,8 +256,6 @@
 		Canvas.onResize =>
 			@_checkCanvasSize() if !@fixed

-
-
 	_showPointer: (layer) ->
 		layer.onMouseOver ->
 			@style =


Add active class to selected element

Add active class to selected element

Previously there was no indicate on which tab was selected
in the packages panel

--- lib/package-panel.coffee
+++ lib/package-panel.coffee
@@ -46,7 +46,7 @@
       return if el.hasClass('active')

       @find('.nav-tabs li.active').removeClass('active')
-      @.toggleClass('active')
+      el.addClass('active')
       @find('.package-container').hide()

       if el.hasClass('available-packages')


some low level mechanics

some low level mechanics

--- js/changes.coffee
+++ js/changes.coffee
@@ -5,10 +5,7 @@

 'use strict' # just like JavaScript

-### WebGL ###
-#loader = new THREE.TextureLoader();
-#jsonLoader = new THREE.JSONLoader()
-[geometry, material, mesh] = [null,null,null]
+container = null

 ### `Main`

@@ -21,7 +18,7 @@
     what it is that this one does!
 ###
 class Main
-    constructor: () ->
+    constructor: ->
         @scene = new THREE.Scene()
         @camera = new THREE.PerspectiveCamera(
             75, 768/512, 1, 1000)
@@ -30,9 +27,13 @@
         @renderer.setSize(768,512)
         @renderer.setClearColor(0xFFFFFF,0)

+        #@controls = new THREE.OrbitControls(
+        #    @camera,@renderer.domElement)
+
+        [@planet,@sun] = [null,null]
+
     init: ->
         #jsonLoader.load("/rsc/3D/rocket.js", addModelToScene)
-        geometry = new THREE.BoxGeometry(10,10,10)
         ###
         loader.load(
             '/rsc/rook-top.png'
@@ -41,19 +42,32 @@
                     { map: texture }))
         ###
         #texture = THREE.TextureLoader('/rsc/rook-top.png')
-        light = new THREE.PointLight(0xFFFFFF,3,100)
-        light.position.set(20,20,20)
+        sun_geo = new THREE.SphereGeometry(60,64,64)
+        sun_mat = new THREE.MeshBasicMaterial(
+            { map: THREE.ImageUtils.loadTexture(
+                "/rsc/sketch/sun.png") } )
+        @sun = new THREE.Mesh(sun_geo,sun_mat)
+        @scene.add(@sun)
+        light = new THREE.PointLight(0xFFFFFF,2,256)
         @scene.add(light)
-        material = new THREE.MeshLambertMaterial(
+        planet_geo = new THREE.SphereGeometry(16,16,16)
+        planet_geo.applyMatrix(
+            new THREE.Matrix4().makeTranslation(0,0,-150))
+        planet_mat = new THREE.MeshLambertMaterial(
             { map: THREE.ImageUtils.loadTexture(
-                "/rsc/rook-top.png") } );
-        mesh = new THREE.Mesh(geometry, material)
-        @scene.add(mesh)
-        @camera.position.z = 15
-        document.getElementById("CoffeeSketch").appendChild(
-            @renderer.domElement)
+                "/rsc/sketch/planet.png") })
+        @planet = new THREE.Mesh(planet_geo,planet_mat)
+        @sun.add(@planet)
+        #@planet.position.set(128,0,0)
+        @scene.add(@planet)
+        @camera.position.z = 256
+        container = document.getElementById("CoffeeSketch")
+        container.appendChild(@renderer.domElement)
         #floor_albedo = new THREE.
         #ImageUtils.loadTexture('/rsc/rook-top.png')
+
+    update: ->
+        #controls.update()

     ### `Main.render`

@@ -62,10 +76,11 @@
     e.g., calling render at the proper framerate, AFAIK.
     ###
     render: =>
+        @update()
         requestAnimationFrame(@render)
         @renderer.clear()
-        mesh.rotation.x += 0.01
-        mesh.rotation.y += 0.02
+        @sun.rotation.y += 0.005
+        @planet.rotation.y += 0.02
         @renderer.render(@scene,@camera)

     ###


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -17,7 +17,7 @@

   player.update()

-  equals player.
+  equals player.I.x, 8

 module()


test(test/e2e/cli-test.coffee): Reparent existing "--header" test

Added Accept-specific context and migrated existing tests there.

--- test/e2e/cli-test.coffee
+++ test/e2e/cli-test.coffee
@@ -457,82 +457,85 @@
         receivedRequest = {}
         producedMediaType = 'application/vnd.api+json'
         reqMediaType = undefined
-
-        runHeaderTestAsync = (done) ->
-          extraHeader = "Accept:#{reqMediaType}"
-          ramlFile = "#{RAML_DIR}/single-get.raml"
-          cmd = "#{ABAO_BIN} #{ramlFile} --server #{SERVER} --header #{extraHeader}"
-
-          app = express()
-
-          app.use (req, res, next) ->
-            receivedRequest = req
-            next()
-
-          app.use (req, res, next) ->
-            err = null
-            produces = ["#{producedMediaType}"]
-            if !req.accepts produces
-              err = new Error('Not Acceptable')
-              err.status = 406
-            next(err)
-
-          app.get '/machines', (req, res) ->
-            machine =
-              type: 'bulldozer'
-              name: 'willy'
-            res.setHeader 'Content-Type', "#{producedMediaType}"
-            res.status(200).send([machine])
-
-          app.use (err, req, res, next) ->
-            res.status(err.status || 500)
-              .json({
-                message: err.message,
-                stack: err.stack
-              })
-            return
-
-          server = app.listen PORT, () ->
-            execCommand cmd, () ->
-              server.close()
-
-          server.on 'close', done
-
-        context 'with "accept"-able request', () ->
-
-          before (done) ->
-            reqMediaType = "#{producedMediaType}"
-            runHeaderTestAsync done
-
-          it 'should have the additional header in the request', () ->
-            expect(receivedRequest.headers.accept).to.equal("#{reqMediaType}")
-
-          it 'should print count of passing tests run', () ->
-            expect(stdout).to.have.string('1 passing')
-
-          it 'should exit normally', () ->
-            expect(exitStatus).to.equal(0)
-
-
-        context 'with un-"accept"-able request', () ->
-
-          before (done) ->
-            reqMediaType = 'application/json'
-            runHeaderTestAsync done
-
-          it 'should have the additional header in the request', () ->
-            expect(receivedRequest.headers.accept).to.equal("#{reqMediaType}")
-
-          # Errors thrown by Mocha show up in stdout; those by Abao in stderr.
-          it 'Mocha should throw an error', () ->
-            detail = "Error: expected 406 to equal '200'"
-            expect(stdout).to.have.string(detail)
-
-          it 'should run test but not complete', () ->
-            expect(stdout).to.have.string('1 failing')
-
-          it 'should exit due to error', () ->
-            expect(exitStatus).to.equal(1)
+        extraHeader = undefined
+
+        describe 'with "Accept" header', () ->
+
+          runAcceptHeaderTestAsync = (done) ->
+            extraHeader = "Accept:#{reqMediaType}"
+            ramlFile = "#{RAML_DIR}/single-get.raml"
+            cmd = "#{ABAO_BIN} #{ramlFile} --server #{SERVER} --header #{extraHeader}"
+
+            app = express()
+
+            app.use (req, res, next) ->
+              receivedRequest = req
+              next()
+
+            app.use (req, res, next) ->
+              err = null
+              produces = ["#{producedMediaType}"]
+              if !req.accepts produces
+                err = new Error('Not Acceptable')
+                err.status = 406
+              next(err)
+
+            app.get '/machines', (req, res) ->
+              machine =
+                type: 'bulldozer'
+                name: 'willy'
+              res.setHeader 'Content-Type', "#{producedMediaType}"
+              res.status(200).send([machine])
+
+            app.use (err, req, res, next) ->
+              res.status(err.status || 500)
+                .json({
+                  message: err.message,
+                  stack: err.stack
+                })
+              return
+
+            server = app.listen PORT, () ->
+              execCommand cmd, () ->
+                server.close()
+
+            server.on 'close', done
+
+          context 'when expecting success', () ->
+
+            before (done) ->
+              reqMediaType = "#{producedMediaType}"
+              runAcceptHeaderTestAsync done
+
+            it 'should have the additional header in the request', () ->
+              expect(receivedRequest.headers.accept).to.equal("#{reqMediaType}")
+
+            it 'should print count of passing tests run', () ->
+              expect(stdout).to.have.string('1 passing')
+
+            it 'should exit normally', () ->
+              expect(exitStatus).to.equal(0)
+
+
+          context 'when expecting failure', () ->
+
+            before (done) ->
+              reqMediaType = 'application/json'
+              runAcceptHeaderTestAsync done
+
+            it 'should have the additional header in the request', () ->
+              expect(receivedRequest.headers.accept).to.equal("#{reqMediaType}")
+
+            # Errors thrown by Mocha show up in stdout; those by Abao in stderr.
+            it 'Mocha should throw an error', () ->
+              detail = "Error: expected 406 to equal '200'"
+              expect(stdout).to.have.string(detail)
+
+            it 'should run test but not complete', () ->
+              expect(stdout).to.have.string('1 failing')
+
+            it 'should exit due to error', () ->
+              expect(exitStatus).to.equal(1)

       describe 'when invoked with "--hookfiles" option', () ->


Fixed 2 COLORS-related highlighting issues

--- src/js/editor/furnace-rules.coffee
+++ src/js/editor/furnace-rules.coffee
@@ -43,10 +43,10 @@
             regex: /\/\/.*$/
           }, {
             token: BINDING
-            regex: /^\w/
-          }, {
-            token: RESERVED
-            regex: /rgb|rgba/
+            regex: /^[\w!@#$%^&*\-=+]/
+          }, {
+            token: RESERVED
+            regex: /rgba?/
           }, {
             token: PLAIN
             regex: /\d+/


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/approachable.coffee
+++ test/approachable.coffee
@@ -40,7 +40,7 @@

   equals obj.I.cooldowns.powerup.value, 0, "powerup should not cooldown past target value"

-test "#addCooldown", ->
+test "#addCooldown", 3, ->
   obj = GameObject()

   obj.include(Approachable)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/clamp_bounds.coffee
+++ test/clamp_bounds.coffee
@@ -9,6 +9,6 @@

   equals player.I.x, player.I.width / 2

-  play
+  player.I.x =

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -13,7 +13,7 @@
   window.keydown.left = true
   player.update()

-  equals player.I.x,
+  equals player.I.x, 9

 module()


Added new datetime format to tests

Added new datetime format to tests


Shift-tab in the commit editor retreats focus

--- keymaps/git.cson
+++ keymaps/git.cson
@@ -12,6 +12,7 @@
 '.github-CommitView-editor atom-text-editor:not([mini])':
   'cmd-enter': 'github:commit'
   'ctrl-enter': 'github:commit'
+  'shift-tab': 'github:retreat-focus'

 '.github-FilePatchView':
   '/': 'github:toggle-patch-selection-mode'


Set Router.options based on the passed options and defaults

--- src/coffee/cilantro/router.coffee
+++ src/coffee/cilantro/router.coffee
@@ -9,6 +9,7 @@
             el: 'body'

         initialize: (options) ->
+            @options = _.extend({}, _.result(@, 'options'), options)
             @_registered = {}
             @_loaded = []
             @_routes = {}


Use shift- form of keybindings

Use shift- form of keybindings

--- keymaps/git.cson
+++ keymaps/git.cson
@@ -5,9 +5,9 @@
   'ctrl-shift-9': 'github:toggle-git-tab'
   'alt-g enter': 'github:commit'
   'alt-g f': 'github:fetch'
-  'alt-g F': 'github:pull'
+  'alt-g shift-f': 'github:pull'
   'alt-g p': 'github:push'
-  'alt-g P': 'github:force-push'
+  'alt-g shift-p': 'github:force-push'
   'alt-g =': 'github:clone'

 'atom-text-editor':


fix keybindings for real

fix keybindings for real

--- keymaps/ink.cson
+++ keymaps/ink.cson
@@ -10,7 +10,6 @@

 'ink-console .editor':
   'enter': 'console:evaluate'
-  'alt-t': 'inline-results:toggle'

 'ink-console':
   'shift-space': 'unset!'
@@ -23,6 +22,7 @@

 'atom-text-editor:not([mini])':
   'escape': 'inline:clear-current'
+  'alt-t': 'inline-results:toggle'

 '.platform-darwin atom-text-editor:not([mini])':
   'cmd-i cmd-c': 'inline-results:clear-all'


Make cell visible after go to line modal confirmation

--- lib/table-element.coffee
+++ lib/table-element.coffee
@@ -729,6 +729,8 @@
       @tableEditor.setCursorAtScreenPosition([row - 1, column - 1])
     else if row?
       @tableEditor.setCursorAtScreenPosition([row - 1, 0])
+
+    @makeCellVisible(@tableEditor.getCursorPosition())

   openGoToLineModal: ->
     goToLineElement = new GoToLineElement()


:bug: Prevent scrolling when dragging a small table

--- lib/table-element.coffee
+++ lib/table-element.coffee
@@ -968,13 +968,21 @@
   scrollDuringDrag: (row, column) ->
     container = @getRowsScrollContainer()

-    if row >= @getLastVisibleRow() - 1
+    scrollTop = container.scrollTop
+    rowOffset = @tableEditor.getScreenRowOffsetAt(row)
+    rowHeight = @tableEditor.getScreenRowHeightAt(row)
+
+    if row >= @getLastVisibleRow() - 1 and rowOffset + rowHeight >= scrollTop + @height - @height / 5
       container.scrollTop += atom.config.get('table-edit.scrollSpeedDuringDrag')
     else if row <= @getFirstVisibleRow() + 1
       container.scrollTop -= atom.config.get('table-edit.scrollSpeedDuringDrag')

     if column?
-      if column >= @getLastVisibleColumn() - 1
+      scrollLeft = container.scrollLeft
+      columnOffset = @tableEditor.getScreenColumnOffsetAt(row)
+      columnWidth = @tableEditor.getScreenColumnWidthAt(row)
+
+      if column >= @getLastVisibleColumn() - 1  and columnOffset + columnWidth >= scrollLeft + @width - @width / 5
         container.scrollLeft += atom.config.get('table-edit.scrollSpeedDuringDrag')
       else if column <= @getFirstVisibleColumn() + 1
         container.scrollLeft -= atom.config.get('table-edit.scrollSpeedDuringDrag')


:bug: Fix selection drag allowing negative indices

--- lib/table-element.coffee
+++ lib/table-element.coffee
@@ -716,6 +716,9 @@
       cursorPosition = @tableEditor.getLastCursor().getPosition()
       selection = @tableEditor.getLastSelection()
       newRange = new Range
+
+      row = Math.max(0, row)
+      column = Math.max(0, column)

       if row < cursorPosition.row
         newRange.start.row = row


This was fixed.

This was fixed.

--- server/storage.coffee
+++ server/storage.coffee
@@ -6,7 +6,6 @@
     path = @_pdfPath()
     if !@_fs.existsSync path
       @_fs.mkdirSync path
-    # TODO: For some reason if file is saved, fiber is restarted, clients reconnects and whole process is restarted and then it succeds because file is already downloaded
     @_fs.writeFileSync path + @_path.sep + filename, data

   @open: (filename) ->


Remove ‘addLogo’ API

Remove ‘addLogo’ API

--- framer/Extras/Preloader.coffee
+++ framer/Extras/Preloader.coffee
@@ -151,7 +151,3 @@

 exports.addImage = (url) ->
 	Framer.Preloader?.addImage(url)
-
-exports.addLogo = (layer) ->
-	Framer.Preloader?.brand.addChild(layer)
-	Framer.Preloader?.brand.style["background-image"] = null


more basic testing

more basic testing

--- test/files/0_basics.iced
+++ test/files/0_basics.iced
@@ -1,5 +1,6 @@

-{run} = require '../../lib/main'
+{BufferInStream,run} = require '../../lib/main'
+

 exports.launch_true = (T,cb) ->
   await run { name : "true" }, defer err
@@ -24,3 +25,18 @@
   T.no_error err
   T.equal out.toString('utf8'), "hello world\n", "got the right output"
   cb()
+
+exports.check_stdin_1 = (T, cb) ->
+  msg = "Now is the time for all good men to come to the aid of the party."
+  await run { name : "cat", stdin : msg }, defer err, out
+  T.no_error err
+  T.equal out.toString('utf8'), msg, "the same message came out as went in"
+  cb()
+
+exports.check_stdin_2 = (T, cb) ->
+  msg = "Now is the time for all good men to come to the aid of the party."
+  stream = new BufferInStream(new Buffer(msg, "utf8"))
+  await run { name : "cat", stdin : stream }, defer err, out
+  T.no_error err
+  T.equal out.toString('utf8'), msg, "the same message came out as went in"
+  cb()


Added user is logged in check before fetching intro snippets.

--- client/app/CommonViews/introductiontooltip.coffee
+++ client/app/CommonViews/introductiontooltip.coffee
@@ -38,6 +38,7 @@
       @init()

   init: ->
+    return unless KD.whoami() instanceof KD.remote.api.JAccount
     KD.remote.api.JIntroSnippet.fetchAll (err, snippets) =>
       return log err if err # TODO: error handling


ComputeController: fetchStackReadme: check against template existence

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -865,7 +865,7 @@
     remote.cacheable 'JStackTemplate', baseStackId, (err, templates) ->
       return callback err  if err
       [template] = templates
-      return callback null, template.description ? ''
+      return callback null, template?.description ? ''

   ###*


the web editor internal canvases are new rebuilt when moving between character and sprite modes

--- web-editor/app.coffee
+++ web-editor/app.coffee
@@ -18,6 +18,8 @@
 # - copy and paste should use hover target rather than selected character
 # - bug: try noticing mouseOut in the editor and picking up the brush
 # - be able to export an array for lookup of changeable color to use per character
+# - performance: maybe instead of watching mouseMove events, watch for mouseEnter and paint if the button is held down
+# - be able to draw directly on to the macro, so that large assemblies of characters can be edited as if they were a single entity

 # FEATURES
@@ -201,9 +203,12 @@

   constructor: ( @code ) ->
     @canvas = elm 'canvas', width:8*scale, height:8*scale
-    @internal_canvas = elm 'canvas', width:8, height:8
+    @build()
+
+  build: ->
+    @internal_canvas = elm 'canvas', width:mode.entity_width, height:mode.entity_height
     @context = @internal_canvas.getContext '2d'
-    @image_data = @context.createImageData  8, 8
+    @image_data = @context.createImageData  mode.entity_width, mode.entity_height

   pixel_at: ( row, column ) ->
     [ address, shift, mask ] = @directions_to  row, column
@@ -612,8 +617,8 @@
       for x in [0..@width-1]
         @paint x, y, 0

-    @view_width = 5
-    @view_height = 3
+    @view_width = 8
+    @view_height = 5
     @view_x = 0
     @view_y = 0
     for row in [0..@view_height-1]
@@ -728,6 +733,8 @@
     mode = MODE[ asset_mode][ color_mode]

     character_set.fill_out_data()
+    for c in character_set.characters
+      c.build()
     character_set.render()
     editor.build()


:lipstick:

:lipstick:

--- src/packages/autocomplete/spec/autocomplete-spec.coffee
+++ src/packages/autocomplete/spec/autocomplete-spec.coffee
@@ -306,7 +306,7 @@

     describe "when text is removed from the mini-editor", ->
       it "reloads the match list based on the mini-editor's text", ->
-        editor.getBuffer().insert([10,0] ,"t")
+        editor.getBuffer().insert([10,0], "t")
         editor.setCursorBufferPosition([10,0])
         autocomplete.attach()


Auto update members every 5 seconds.

Auto update members every 5 seconds.

--- app/assets/javascripts/angular/controllers/members_ctrl.js.coffee
+++ app/assets/javascripts/angular/controllers/members_ctrl.js.coffee
@@ -1,4 +1,12 @@
 angular.module("hubud")
-  .controller "MembersCtrl", ($scope, Restangular) ->
-    Restangular.all("members").getList().then (response) ->
-      $scope.members = response
+  .controller "MembersCtrl", ($scope, Restangular, $timeout) ->
+
+    fetchMembers = ->
+      console.log "Update members"
+      Restangular.all("members").getList().then (response) ->
+        $scope.members = response
+
+        # Fetch new members every 5 seconds
+        $timeout fetchMembers, 5000
+
+    fetchMembers()


Whoops wrong default

Whoops wrong default

--- components/commercial_filter/models/params.coffee
+++ components/commercial_filter/models/params.coffee
@@ -23,7 +23,7 @@
     medium: null
     major_periods: []
     partner_cities: []
-    include_artworks_by_followed_artists: false
+    include_artworks_by_followed_artists: null
     aggregations: ['TOTAL', 'COLOR', 'MEDIUM', 'MAJOR_PERIOD', 'PARTNER_CITY', 'FOLLOWED_ARTISTS']
     ranges:
       price_range:


fixed notifications ordering and unread state

--- core/app/backbone/views/notifications_view.coffee
+++ core/app/backbone/views/notifications_view.coffee
@@ -61,6 +61,7 @@
     self = this
     unless @_visible
       @collection.fetch
+        reset: true # Don't append models, this will break sorting and highlighting
         success: refreshAgain
         error: (collection, response)->
           if response.status is 403


Removed duplicate regex replace entry

Removed duplicate regex replace entry


respond to object changes

respond to object changes

--- lib/util/progress.coffee
+++ lib/util/progress.coffee
@@ -10,8 +10,24 @@
     span = document.createElement 'span'
     span.classList.add 'inline-block'
     prog = document.createElement 'progress'
+    prog.setAttribute 'max', 1
+    if p.progress? then prog.setAttribute 'value', p.progress
     span.appendChild prog
+
+    Object.observe p, (changes) ->
+      for {name, object} in changes
+        continue unless name == "progress"
+        if object.progress?
+          prog.setAttribute 'value', object.progress
+        else
+          prog.removeAttribute 'value'
+
     span

   test: ->
-    @statusBar.addLeftTile item: @progressView(), priority: -10
+    @p = {progress: 0}
+    @statusBar.addLeftTile item: @progressView(@p), priority: -10
+
+  # for i in [0..100]
+  #   do (i) =>
+  #     setTimeout (=> @p.progress = i/100), i*10


Define enter as ‘core:confirm’ in select-list mini editors

We were leaving this to packages to define, and they were getting it
wrong by selecting into the ‘input’ which is now in shadow.

--- keymaps/base.cson
+++ keymaps/base.cson
@@ -14,6 +14,9 @@
   'enter': 'editor:newline'
   'shift-tab': 'editor:outdent-selected-rows'
   'ctrl-K': 'editor:delete-line'
+
+'.select-list atom-text-editor.mini':
+  'enter': 'core:confirm'

 '.tool-panel.panel-left, .tool-panel.panel-right':
   'escape': 'tool-panel:unfocus'


fixed bug where search results would populate if search value length was zero


Fixed tests

Fixed tests

--- test/memegen-test.coffee
+++ test/memegen-test.coffee
@@ -13,5 +13,8 @@
     require('../src/memegen')(@robot)

   it 'registers a respond listener', ->
-    expect(@robot.respond).to.have.been.calledWith(/meme list/i)
-    expect(@robot.respond).to.have.been.calledWith(/meme (.+) --top (.+) --bottom (.+)/i)
+    expect(@robot.respond).to.have.been.calledWith(/meme create/i)
+    expect(@robot.respond).to.have.been.calledWith(/meme cancel/i)
+
+  it 'registers a hear listener', ->
+    expect(@robot.hear).to.have.been.calledWith(/(.+)/i)


[grunt] preprocess path of Gray Matter to form proper object with pages data

--- tasks/data.coffee
+++ tasks/data.coffee
@@ -1,3 +1,5 @@
+crumble = require('../modules/crumble')
+
 module.exports = () ->

   ###
@@ -10,6 +12,9 @@
     build:
       options:
         baseDir: '<%= path.source.templates %>'
+
+        preprocessPath: (path) ->
+          return [crumble(path)..., 'props']

       files: [
         expand: true


Respond instead of hear

Respond instead of hear

--- src/devops-reactions.coffee
+++ src/devops-reactions.coffee
@@ -8,7 +8,7 @@
 #   None
 #
 # Commands:
-#   devops reactions
+#   hubot devops reactions
 #
 # Author:
 #
@@ -19,7 +19,7 @@
 devopsRegex = /(devops reactions|!devops)/i

 module.exports = (robot) ->
-  robot.hear devopsRegex, (msg) ->
+  robot.respond devopsRegex, (msg) ->
     msg.http(url).get() (err, res, body) ->
       location = res.headers.location
       jsdom.env location, [jquery], (errors, window) ->


Moved condition into the while loop (instead of while true)

--- dl/src/common/key_utils.coffee
+++ dl/src/common/key_utils.coffee
@@ -16,11 +16,10 @@
         iterations = 0
         secret = salt + password
         # hash for 1 second
-        while true
+        while Date.now() - start_t < 1000
             secret = hash.sha256 secret
             iterations += 1
-            if Date.now() - start_t >= 1000
-                break
+
         checksum = hash.sha256 secret
         [
             iterations


(fix) Few more syntax errors to fix.

(fix) Few more syntax errors to fix.

--- src/flavors/it_style_tests.coffee
+++ src/flavors/it_style_tests.coffee
@@ -15,7 +15,7 @@
       it: """(function(){
         var __it_index = 0;
         return function(name, fn) {
-          registerTest(name, 'it("'+name+'" + fn.toString() + ')';
+          registerTest(name, 'it("'+name+'"' + fn.toString() + ')');
           try {
             fn();
             pass(name);


Batch updates on input events

Batch updates on input events

--- src/editor-component.coffee
+++ src/editor-component.coffee
@@ -277,7 +277,7 @@
     @props.editor.setHeight(@refs.scrollView.getDOMNode().clientHeight)

   onInput: (char, replaceLastChar) ->
-    @props.editor.insertText(char)
+    ReactUpdates.batchedUpdates => @props.editor.insertText(char)

   onScreenLinesChanged: ({start, end}) ->
     {editor} = @props


Deprecate EditorComponent::setInvisibles with grim

--- src/editor-component.coffee
+++ src/editor-component.coffee
@@ -3,6 +3,7 @@
 {debounce, defaults, isEqualForProperties} = require 'underscore-plus'
 scrollbarStyle = require 'scrollbar-style'
 {Range, Point} = require 'text-buffer'
+grim = require 'grim'

 GutterComponent = require './gutter-component'
 InputComponent = require './input-component'
@@ -943,6 +944,7 @@

   # Deprecated
   setInvisibles: (invisibles={}) ->
+    grim.deprecate "Use config.set('editor.invisibles', invisibles) instead"
     atom.config.set('editor.invisibles', invisibles)

   # Deprecated


Remove width setting of overflowExpander

--- src/editor-component.coffee
+++ src/editor-component.coffee
@@ -600,9 +600,7 @@

     if position is 'absolute' or width
       clientWidth = scrollViewNode.clientWidth
-      if clientWidth > 0
-        overflowExpander.style.height = clientHeight + "px"
-        editor.setWidth(clientWidth)
+      editor.setWidth(clientWidth) if clientWidth > 0

   measureLineHeightAndCharWidthsIfNeeded: (prevState) ->
     if not isEqualForProperties(prevState, @state, 'lineHeight', 'fontSize', 'fontFamily')


removed console logs

removed console logs

--- src/javascripts/contact_us.coffee
+++ src/javascripts/contact_us.coffee
@@ -19,15 +19,10 @@
           type: "POST"
           url: $trigger.attr('data-action')
           data: $form.serialize() + "&contactid=#{$trigger.attr('data-contact-id')}"
-          success: (data) ->
-            console.log("success")
-            console.log($form)
+          success: (data) ->
             $form.replaceWith(data)
             $that.find('form').show()
-          error: (x, y, z) ->
-            # Server error or timeout, nothing to do
-            # FIXME: Uncomment the real error message
-            console.log("error")
+          error: (x, y, z) ->
             $form.after('
Ett fel inträffade, vänligen försök senare eller skicka ditt meddelande till nedanstående e-postadress.
')

     # Scroll to top of form


[Gutter] Remove unused param 'presenter' from GutterComponent constructor

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

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

     @domNode = document.createElement('div')


Fixing require chai

Fixing require chai

--- test/_spec.coffee
+++ test/_spec.coffee
@@ -1,6 +1,10 @@
 exports = {}

-exports.assert = (chai or require('chai')).assert
+if typeof chai is 'undefined'
+    assert = require('chai').assert
+else
+    assert = chai.assert
+exports.assert = assert

 exports.$ = Rye


Don't cache this subscription because it caused weirdness

--- router.coffee
+++ router.coffee
@@ -107,7 +107,7 @@
       @render() if @ready()

     waitOn: -> [
-      subs.subscribe 'singlePostBySlug', @params.slug
+      Meteor.subscribe 'singlePostBySlug', @params.slug
       subs.subscribe 'commentsBySlug', @params.slug
       subs.subscribe 'authors'
     ]


Fixed easyXDM compilation, it now uses the easyXDM located in libs/ folder

--- js-library/Gruntfile.coffee
+++ js-library/Gruntfile.coffee
@@ -111,8 +111,8 @@
           'dist/server/factlink.start_highlighting.min.js': ['<banner>', 'dist/factlink.start_highlighting.js']
           'dist/server/factlink.stop_highlighting.min.js':  ['<banner>', 'dist/factlink.stop_highlighting.js']
           'dist/server/factlink.min.js':                    ['<banner>', 'dist/factlink.js']
-          'dist/server/easyXDM/easyXDM.min.js':             ['<banner>', 'dist/easyXDM/easyXDM.js']
-          'dist/easyXDM/easyXDM.min.js':                    ['<banner>', 'dist/easyXDM/easyXDM.js']
+          'dist/server/easyXDM/easyXDM.min.js':             ['<banner>', 'libs/easyXDM.js']
+          'dist/easyXDM/easyXDM.min.js':                    ['<banner>', 'libs/easyXDM.js']

   grunt.registerTask 'default', ['jshint', 'qunit', 'less', 'concat', 'uglify', 'copy']
   grunt.registerTask 'server',  ['concat', 'uglify', 'less', 'copy']


Reverting. Using ShareIt

Reverting. Using ShareIt

Reverting changes. Using ShareIt settings instead.

--- router.coffee
+++ router.coffee
@@ -40,23 +40,6 @@
   data: ->
     posts: Post.where {},
       sort: publishedAt: -1
-  onAfterAction: ->
-    if not Meteor.isClient
-      return
-    if SEO and @ready()
-      title = 'Blog'
-      if Blog.settings.title
-        title += " | #{Blog.settings.title}"
-      description = if Blog.settings.description then Blog.settings.description else ''
-      SEO.set
-        title: title
-        meta:
-          description: description
-        og:
-          title: title
-          description: description
-        , false
-      return

 # BLOG TAG

@@ -69,29 +52,10 @@
   ]
   fastRender: true
   data: ->
-    tag: @params.tag
     posts: Post.where
       tags: @params.tag
     ,
       sort: publishedAt: -1
-  onAfterAction: ->
-    if not Meteor.isClient
-      return
-    if SEO and @ready()
-      tag = @data().tag
-      title = "Blog: tagged \"#{tag}\""
-      if Blog.settings.title
-        title += " | #{Blog.settings.title}"
-      description = if Blog.settings.description then Blog.settings.description else ''
-      SEO.set
-        title: title
-        meta:
-          description: description
-        og:
-          title: title
-          description: description
-        , false
-      return

 # SHOW BLOG

@@ -127,24 +91,6 @@
   ]
   data: ->
     Post.first slug: @params.slug
-  onAfterAction: ->
-    if not Meteor.isClient
-      return
-    if SEO and @ready()
-      title = @data().title
-      if Blog.settings.title
-        title += " | #{Blog.settings.title}"
-      description = if @data().metaDesc then @data().metaDesc else @data().excerpt
-      slug = @data().slug
-      SEO.set
-        title: title
-        meta:
-          description: description
-        og:
-          title: title
-          description: description
-        , false
-      return

 # BLOG ADMIN INDEX


For consistency, also precompress css files

--- js-library/Gruntfile.coffee
+++ js-library/Gruntfile.coffee
@@ -133,8 +133,10 @@
           'build/easyXDM/easyXDM.min.js':                    ['build/js/jail_iframe/libs/easyXDM.js']
           'build/server/nu.min.js':                          ['build/nu.js']
     shell:
-      gzip_files:
+      gzip_js_files:
         command: 'find build/ -iname \'*.js\'  -print0 | xargs -0 -n 1 gzip -kf -9'
+      gzip_css_files:
+        command: 'find build/ -iname \'*.css\'  -print0 | xargs -0 -n 1 gzip -kf -9'

     copy:
       build:
@@ -149,7 +151,9 @@
         files: [
           { src: ['*.js', 'server/**/*.js', 'easyXDM/*.js',
                   '*.js.gz', 'server/**/*.js.gz', 'easyXDM/*.js.gz',
-                  '**/*.css', '**/*.png', '**/*.gif', 'robots.txt'], cwd: 'build', dest: 'dist', expand: true }
+                  '**/*.css',
+                  '**/*.css.gz',
+                  '**/*.png', '**/*.gif', 'robots.txt'], cwd: 'build', dest: 'dist', expand: true }
           { src: ['**/*.png', '**/*.gif', 'robots.txt'], cwd: 'build', dest: 'dist/server', expand: true }
         ]
     watch:
@@ -211,7 +215,7 @@

   grunt.registerTask 'jail_iframe', ['concat:jail_iframe', 'code_inliner:basic_css', 'uglify:jail_iframe']
   grunt.registerTask 'compile', ['clean', 'copy:build', 'copy:start_stop_files', 'coffee', 'sass', 'cssUrlEmbed', 'cssmin',
-                                 'jail_iframe', 'concat', 'uglify:all_except_jail_iframe', 'code_inliner', 'shell:gzip_files', 'copy:dist']
+                                 'jail_iframe', 'concat', 'uglify:all_except_jail_iframe', 'code_inliner', 'shell:gzip_css_files', 'shell:gzip_js_files', 'copy:dist']
   grunt.registerTask 'test',    ['jshint', 'qunit']

   grunt.registerTask 'default', ['compile', 'test']


Allow more than 3 back ticks for fenced code blocks

--- src/packages/gfm/grammars/gfm.cson
+++ src/packages/gfm/grammars/gfm.cson
@@ -54,80 +54,80 @@
     'name': 'comment.hr.gfm'
   }
   {
-    'begin': '^```coffee(script)?$'
+    'begin': '^`{3,}coffee(script)?$'
     'beginCaptures':
       '0': 'name': 'support.gfm'
-    'end': '^```$'
+    'end': '^`{3,}$'
     'endCaptures':
       '0': 'name': 'support.gfm'
     'name': 'markup.code.coffee.gfm'
     'patterns': ['include': 'source.coffee']
   }
   {
-    'begin': '^```(javascript|js)$'
+    'begin': '^`{3,}(javascript|js)$'
     'beginCaptures':
       '0': 'name': 'support.gfm'
-    'end': '^```$'
+    'end': '^`{3,}$'
     'endCaptures':
       '0': 'name': 'support.gfm'
     'name': 'markup.code.js.gfm'
     'patterns': ['include': 'source.js']
   }
   {
-    'begin': '^```css$'
+    'begin': '^`{3,}css$'
     'beginCaptures':
       '0': 'name': 'support.gfm'
-    'end': '^```$'
+    'end': '^`{3,}$'
     'endCaptures':
       '0': 'name': 'support.gfm'
     'name': 'markup.code.css.gfm'
     'patterns': ['include': 'source.css']
   }
   {
-    'begin': '^```xml$'
+    'begin': '^`{3,}xml$'
     'beginCaptures':
       '0': 'name': 'support.gfm'
-    'end': '^```$'
+    'end': '^`{3,}$'
     'endCaptures':
       '0': 'name': 'support.gfm'
     'name': 'markup.code.xml.gfm'
     'patterns': ['include': 'text.xml']
   }
   {
-    'begin': '^```(ruby|rb)$'
+    'begin': '^`{3,}(ruby|rb)$'
     'beginCaptures':
       '0': 'name': 'support.gfm'
-    'end': '^```$'
+    'end': '^`{3,}$'
     'endCaptures':
       '0': 'name': 'support.gfm'
     'name': 'markup.code.ruby.gfm'
     'patterns': ['include': 'source.ruby']
   }
   {
-    'begin': '^```java$'
+    'begin': '^`{3,}java$'
     'beginCaptures':
       '0': 'name': 'support.gfm'
-    'end': '^```$'
+    'end': '^`{3,}$'
     'endCaptures':
       '0': 'name': 'support.gfm'
     'name': 'markup.code.java.gfm'
     'patterns': ['include': 'source.java']
   }
   {
-    'begin': '^```(sh|bash)$'
+    'begin': '^`{3,}(sh|bash)$'
     'beginCaptures':
       '0': 'name': 'support.gfm'
-    'end': '^```$'
+    'end': '^`{3,}$'
     'endCaptures':
       '0': 'name': 'support.gfm'
     'name': 'markup.code.shell.gfm'
     'patterns': ['include': 'source.shell']
   }
   {
-    'begin': '^```.*$'
+    'begin': '^`{3,}.*$'
     'beginCaptures':
       '0': 'name': 'support.gfm'
-    'end': '^```$'
+    'end': '^`{3,}$'
     'endCaptures':
       '0': 'name': 'support.gfm'
     'name': 'markup.raw.gfm'


updating pipeline

updating pipeline

--- js/pipeline/pipeline.coffee
+++ js/pipeline/pipeline.coffee
@@ -56,37 +56,37 @@
   $complete: ->

-# Data Structure
-# --------------
+# Topology Configurations
+# -----------------------
+# `PipeConfig` provides two things:
+# * Information on how to run a given pipe.
+# * Information on how to wire up the pipes.
+class PipeConfig
+  constructor: (options) ->
+    {
+      # Name for the given pipe in the topology.
+      # It must be unique within the cluster.
+      @name
+      # Names of the dependent pipes, and the clustering strategy.
+      # It is a map {pipe name : grouper}. See `Grouper` for more info.
+      @dependencies
+      # List of required fields emitted by the given pipe.
+      @fields
+      # Number of concurrent instances of the given pipe.
+      @concurrency
+      # how often `onInterval()` event is triggered for a given pipe.
+      # `0` disables `onInterval()` altogether.
+      @interval_ms
+    } = options
+    unless @dependencies? then @dependencies = {}
+    unless @interval_ms?  then @interval_ms = 0
+    unless @concurrency?  then @concurrency = 1
+
 class Grouper
   # required fields.
   fields: () -> []
   # grouping function. maps message -> int.
   group:  (message) ->
-
-# Topology Configurations
-# -----------------------
-class PipeConfig
-  constructor: (options) ->
-    {
-      # Name for the given pipe in the topology.
-      @name
-      # Names of the dependent pipes, and the
-      # clustering strategy.
-      # name -> grouper[fields]
-      @dependencies
-      # List of fields the given pipe must have.
-      @fields
-      # Concurrency factor of the pipe.
-      @concurrency
-      # how often "onInterval" is called.
-      @interval_ms
-    } = options
-    # no periodic invocation.
-    unless @dependencies?  then @dependencies = {}
-    unless @interval_ms? then @interval_ms = 0
-    unless @concurrency? then @concurrency = 1
-

 # Implementation detail of the pipe context.
 # Internally it has callbacks on emit and completion to make things
@@ -120,6 +120,7 @@
     @name_to_dependents = {}
     @handles = [] # various handles.

+  # Registers a given pipe & config into the topology.
   register: (pipe, config) ->
     name = config.name
     assert not @name_to_pipe[name], "Pipe already registered"


forgot to use exception handling for improper options; now using

--- lib/bebopt.coffee
+++ lib/bebopt.coffee
@@ -85,12 +85,13 @@
       return self))

   _optError: (parent, child) =>
-    if @["_#{parent}"][child] is undefined
+    console.log parent
+    if parent[child.arg] is undefined
       if child.length < 2
-        console.error("#{@app}: invalid option -- '#{child}'")
-        process.exit(1)
-      else if child.length > 1
-        console.error("#{@app}: unrecognized option '--#{child}'")
+        console.error("#{@app}: invalid option -- '#{child.arg}'")
+        process.exit(1)
+      else
+        console.error("#{@app}: unrecognized option '--#{child.arg}'")
         process.exit(1)

   _parentError: () =>
@@ -127,7 +128,15 @@
       if /^--$/.test(arg)
         optend = true
       else if /^-$/.test(arg)
-        noopt = true
+        noopt = true # XXX prevents `-' from creating an empty option object
+        # when found on the command-line: e.g. `cat -'
+        # NOTE that `-' traditionally means READ FROM STDIN
+        # however, in the case of an option parser...
+        # well, I think that it's meaningless.
+        # we should never read from STDIN, as
+        # an option parser has no business doing so.
+        # although being able to echo options like so would be interesting:
+        # `echo -- '-vhx' | <PROGRAM USING BEBOPT>` lulz
       else
         if noopt
           null # do nothing
@@ -189,6 +198,7 @@
   # within the respective
   # option list, such as `@_long'
   _catchSpaceDelimArgs: (opt, list) =>
+    @_optError(list, opt)
     if list[opt.arg].type isnt 'flag'
       if opt.optarg is undefined
         @_args.forEach((nonOpt, ind) =>
@@ -203,6 +213,7 @@
         ++elem.index)
     return list

+# XXX transforms `-hxv' into `-h -x -v'
   _splitCombinedShorts: () =>
     @_opts.forEach((elem, ind) =>
       dashes = elem.arg.replace(/^(--?).*/, '$1').length # number of dashes
@@ -225,10 +236,11 @@
     @_log(@)
     @_opts.forEach((elem, ind) =>
       dashes = elem.arg.replace(/^(--?).*/, '$1').length # number of dashes
+      elem.arg = elem.arg.replace(/^--?(.*)/, '$1')
+      console.log elem
       if dashes is 2
         elem = @_catchSpaceDelimArgs(elem, @_long)
-      # check option types
-      if dashes is 1 and elem.arg.length < 2 # short
+      else
         elem = @_optTypeError(elem, @_short)

     )


Multiple versions result

Multiple versions result

--- lib/client.coffee
+++ lib/client.coffee
@@ -234,9 +234,16 @@
 			v = cell.value.toBuffer()
 			t = cell.timestamp

-			cols["#{f}:#{q}"] =
+			o =
 				value: v
 				timestamp: t
+
+			# multiple versions
+			if cols["#{f}:#{q}"]
+				cols["#{f}:#{q}"] = [cols["#{f}:#{q}"]] unless Array.isArray cols["#{f}:#{q}"]
+				cols["#{f}:#{q}"].push o
+			else
+				cols["#{f}:#{q}"] = o

 			family: f
 			qualifier: q


fix #169

fix #169

--- lib/client.coffee
+++ lib/client.coffee
@@ -92,7 +92,7 @@

     @post(req, (msg) =>
       switch msg.typehint
-        when "FalseResponse" then log("no doc")
+        when "FalseResponse" then atom.notifications.addError("No documentation found")
         else Documentation.openDoc(Documentation.formUrl("localhost", @httpPort, msg.text))
     )


fixes #170

fixes #170

--- lib/client.coffee
+++ lib/client.coffee
@@ -114,7 +114,7 @@
       if(pos)
         @goToPosition(pos)
       else
-        log("No declPos in response from Ensime, cannot go anywhere")
+        atom.notifications.addError("No declPos in response from Ensime server, cannot go anywhere :(")
     )


removing redundant compile from command_line.coffee

--- command_line.coffee
+++ command_line.coffee
@@ -87,7 +87,7 @@
       if      opts.run    then eval js
       else if opts.print  then puts js
       else if opts.lint   then lint js
-      else                     write_js source, coffee.compile code
+      else                     write_js source, js
   catch err
     if opts.watch then puts err.message else throw err


translate something

translate something

--- def/it/about.cson
+++ def/it/about.cson
@@ -1,15 +1,15 @@
 About:
-  tabTitle: "About"
-  releaseNotes: "Release Notes"
-  newUpdate: "New Update"
-  upToDate: "Atom is up to date!"
-  checkingForUpdates: "Checking for updates..."
-  checkUpdatesNow: "Check now"
-  automaticDownloadUpdates: "Automatic download updates"
-  restartInstall: "Restart and install"
-  license: "License"
-  termsOfUse: "Terms of Use"
-  with: " with "
-  by: " by "
-  andTheAwesome: "And the awesome "
-  atomCommunity: "Atom Community"
+  tabTitle: "Info"
+  releaseNotes: "Note di Rilascio della versione"
+  newUpdate: "Nuovo Aggiornamento"
+  upToDate: "Atom è aggiornato!"
+  checkingForUpdates: "Verifica aggiornamenti..."
+  checkUpdatesNow: "Verifica ora"
+  automaticDownloadUpdates: "Scarica gli aggiornamenti in automatico"
+  restartInstall: "Riavvia e installa"
+  license: "Licenza"
+  termsOfUse: "Condizioni di uso"
+  with: " con "
+  by: " da "
+  andTheAwesome: "e il fantastico "
+  atomCommunity: "La comunità di Atom"


use write without response

use write without response

--- examples/ble_flash/ble_flash.coffee
+++ examples/ble_flash/ble_flash.coffee
@@ -229,7 +229,7 @@
             data_on_the_fly = data_on_the_fly - 1
             data = queued_data.shift()

-            data_char.write data, false, (error)->
+            data_char.write data, true, (error)->
                 data_on_the_fly = data_on_the_fly + 1

                 if error


$.proxyR helper

$.proxyR helper

--- lib/assets/javascripts/jquery_patch.js.coffee
+++ lib/assets/javascripts/jquery_patch.js.coffee
@@ -21,3 +21,7 @@
 # @see .each
 $.fn.eachR = (context, func) ->
   return $.eachR(context, this, func, context, true)
+
+# Reverse order of args to $.proxy
+$.proxyR = (context, func) ->
+  return $.proxy(func, context)


welcome bug fix

welcome bug fix

--- src/plugins/faction/welcome.coffee
+++ src/plugins/faction/welcome.coffee
@@ -43,6 +43,6 @@
         storage.welcomedAgents[player.toLowerCase()] = true
         storage.save()

-        FactionUtil.send Bot.getTemplate(Config.Faction.Plugin.Welcome.JokeTemplates).fillPlayer(player).fillSmily().toString()
+        FactionUtil.send Bot.getTemplate(Config.Faction.Plugin.Welcome.Templates).fillPlayer(player).fillSmily().toString()

 module.exports = plugin


update contexts to describe the right...context

--- test/integration/send-message-2-spec.coffee
+++ test/integration/send-message-2-spec.coffee
@@ -42,6 +42,7 @@
         whitelists:
           message:
             sent: 'spy-uuid': {}
+            received: 'nsa-uuid': {}

     @devices.insert @senderDevice, done

@@ -146,7 +147,7 @@
       it 'should deliver the sent message to the receiver', ->
         expect(@message).to.exist

-    context 'subscribed to someone elses received messages', ->
+    context 'subscribed to someone elses sent messages', ->
       @timeout 5000
       beforeEach 'create message sent subscription', (done) ->
         subscription =
@@ -187,7 +188,7 @@
       it 'should deliver the sent message to the receiver', ->
         expect(@message).to.exist

-    context 'subscribed to someone elses received messages, but is not authorized', ->
+    context 'subscribed to someone elses sent messages, but is not authorized', ->
       @timeout 5000
       beforeEach 'create message sent subscription', (done) ->
         subscription =


'sharp' module now must be a local module

--- lib/tasks/build/parse/resources/parser.coffee
+++ lib/tasks/build/parse/resources/parser.coffee
@@ -5,7 +5,8 @@
 yaml = require 'js-yaml'
 mkdirp = require 'mkdirp'
 try
-	sharp = require 'sharp'
+	sharpModulePath = pathUtils.join fs.realpathSync(''), '/node_modules/sharp'
+	sharp = require sharpModulePath

 {utils, log, assert, Resources} = Neft

@@ -44,7 +45,7 @@
 supportImageResource = (path, rsc) ->
 	unless sharp
 		unless logShowed
-			log.error "Image resources are not supported, because 'sharp' module is not installed"
+			log.warn "Install 'sharp' module for full image resources support; 'npm install sharp'"
 			logShowed = true
 		return


Minor refators

Minor refators

--- app/components/project-preferences.coffee
+++ app/components/project-preferences.coffee
@@ -3,19 +3,17 @@
 `import ENV from 'irene/config/environment';`
 `import deviceSelection from 'irene/utils/device-selection'`

-sliceDevices = ENUMS.DEVICE_TYPE.CHOICES.reverse()[1..]
-devices = sliceDevices.reverse()
+ProjectPreferencesComponent = Ember.Component.extend

-ProjectPreferencesComponent = Ember.Component.extend
-  devices: devices
   project: null
   versions: ["Loading..."]
+  currentDevice: ["Loading..."]
   availableDevices: ["Loading..."]
   availableVersions: ["Loading..."]
-  currentDevice: ["Loading..."]
+  devices: ENUMS.DEVICE_TYPE.CHOICES[0...-1]

   didInsertElement: ->
-    this.send('deviceChanged');
+    @send('deviceChanged')

   actions:
     deviceChanged: (value) ->


Add a test that we really are using the CS2 compiler, by checking that function parameter default values now behave like ES2015 ones do

--- packages/non-core/coffeescript/tests/coffeescript_tests.coffee
+++ packages/non-core/coffeescript/tests/coffeescript_tests.coffee
@@ -30,6 +30,11 @@
   test.isTrue testingForNativeImportedModule123456789?

+# CoffeeScript 2 is active, with its conforming-to-ES2015 breaking changes
+Tinytest.add "coffeescript - ES2015 conformity", (test) ->
+	f = (a = 1) -> a
+	test.isTrue f(null) is null # `f(null)` would be 1 in CoffeeScript 1.x
+
 # JSX
 Tinytest.add "coffeescript - JSX", (test) ->
 	# Mock React


updated acho interface

updated acho interface

--- lib/Report.coffee
+++ lib/Report.coffee
@@ -1,15 +1,15 @@
 'use strict'

-Logger   = require 'acho'
+Acho     = require 'acho'
 chalk    = require 'chalk'
 jsonlint = require 'jsonlint'
+DEFAULT  = require './Default'
 keys     = require './Keywords'
-DEFAULT  = require './Default'

 module.exports = class Report

   constructor: (@name = 'Your file', @isColorizable = false, @isValidationEnable = false) ->
-    @logger = new Logger @isColorizable
+    @logger = Acho color: @isColorizable

   lint: (objt) ->
     objt = JSON.stringify(objt) if typeof objt isnt 'string'


ComputeUtils: flattenPayload {“foo”: {“bar”: 2}} -> {“foo_bar”: 2}

--- workers/social/lib/social/models/computeproviders/computeutils.coffee
+++ workers/social/lib/social/models/computeproviders/computeutils.coffee
@@ -424,9 +424,21 @@
   return signatures

+# Flatten's given object with keys prefixed ~ GG
+flattenPayload = (payload, prefix = 'payload', res = {}) ->
+
+  for own key, value of payload
+    key = "#{prefix}_#{key}"
+    if value and 'object' is typeof value
+    then res = flattenPayload value, key, res
+    else res[key] = value
+
+  return res
+
+
 module.exports = {
   fetchUserPlan, fetchGroupStackTemplate, fetchUsage
   PLANS, PROVIDERS, guessNextLabel, checkUsage
   revive, reviveClient, reviveCredential, reviveGroupLimits
-  checkTemplateUsage, generateSignatures
+  checkTemplateUsage, generateSignatures, flattenPayload
 }


fix graph reset bug

fix graph reset bug

--- app/controllers/interactive/graphs.coffee
+++ app/controllers/interactive/graphs.coffee
@@ -60,6 +60,15 @@
     @headingText.html @action_title
     $('[data-link="graphs"]').removeClass 'pressed'

+  setGraph: =>
+    if @options.graphType is 'histogram'
+      @xAxisItem.find('label').html 'I\'d like to see...'
+      @graph = new Histogram { el: '#graph', channel: 'graph', height: 310, width: 512 }
+    else
+      @xAxisItem.find('label').html 'I\'d like to see how...'
+      @graph = new Scatterplot { el: '#graph', channel: 'graph', height: 310, width: 512 }
+
+
   # Graph interface functions
   setGraphType: (e) =>
     button = $(e.currentTarget)
@@ -69,12 +78,9 @@
     @el.find('svg').empty()
     @reset()

-    if $(e.currentTarget).data('variables') is 'histogram'
-      @xAxisItem.find('label').html 'I\'d like to see...'
-      @graph = new Histogram { el: '#graph', channel: 'graph', height: 310, width: 512 }
-    else
-      @xAxisItem.find('label').html 'I\'d like to see how...'
-      @graph = new Scatterplot { el: '#graph', channel: 'graph', height: 310, width: 512 }
+    @setGraph()
+
+    console.log @graph

   setXAxis: (e) =>
     @options.xAxis = $(e.currentTarget).val()
@@ -143,12 +149,7 @@
     @setPressed $('[data-source="group"]')
     @setPressed $("[data-variables=\"#{@options.graphType}\"]")

-    if $(e.currentTarget).data('variables') is 'histogram'
-      @xAxisItem.find('label').html 'I\'d like to see...'
-
-    @graph.data = new Array
-    @graph.filteredData = new Array
-    @graph.filters = new Array
+    @setGraph()

   # Image Generation
   generateImageFromGraph: (e) =>


init: nam install

init: nam install

--- lib/commands/init.coffee
+++ lib/commands/init.coffee
@@ -235,6 +235,11 @@
     git commit -m 'Project initialization'
     """
     cwd: "#{params.path}"
+  .execute
+    cmd: """
+    npm install
+    """
+    cwd: "#{params.path}"
   .then (err, status) ->
     if err
       rl.write "#{err.stack?.trim() or err.message}\n"


commandstream: Continue setting nodeMap on graph

--- lib/commandstream.coffee
+++ lib/commandstream.coffee
@@ -293,6 +293,7 @@
   for message in messages
     index = toCommandStreamBuffer message, componentLib, mapping.nodes, mapping.components, buffer, index

+  graph.nodeMap = mapping.nodes # HACK
   buffer = buffer.slice(0, index)
   return buffer


Fix assertions in macro/request look-aheads

In roff, \*n may be used to expand a defined string into an argument. An
invocation like the following statement is a legal invocation:

    .ds t " BOLD TEXT"
    .B\*t

The previous lookaheads only tested for the existence of whitespace (and
some weren't even taking tabs into consideration, either). Since escapes
may be introduced anywhere, the lookaheads have been extended to include
backslashes.

The error highlighting for underline macros has been removed. Expansions
like the above would be incorrectly marked as invalid; furthermore, it's
not the role of a language package to validate a user's input.

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -1,6 +1,6 @@
 name: "Roff"
 scopeName: "text.roff"
-firstLineMatch: "^\\.TH +(?:\\S+)"
+firstLineMatch: "^\\.TH[ \t]+(?:\\S+)"
 fileTypes: [
 	"1", "1b", "1c", "1has", "1in", "1m", "1s", "1x",
 	"2",
@@ -324,7 +324,7 @@
 				|lf|ll|ls|lt|mc|mk|na|ne|nf|nh|nm|nn|nr|ns|nx|os|pc|pi|pl
 				|pm|pn|po|ps|rd|rm|rn|rr|rs|rt|so|sp|ss|sv|sy|ta|tc|ti|tl
 				|tm|tr|uf|vs|wh)
-				(?=[ \t]|$)"
+				(?=[ \t\\\\]|$)"
 			end: "(?<!\\\\)(?=\n)"
 			beginCaptures:
 				1: {name: "punctuation.definition.request.roff"}
@@ -335,7 +335,7 @@

 			# Flow control/conditional requests
 			name: "meta.function.request.$1.roff"
-			begin: "^([.'])[ \t]*(ie|if|el)(?=[ \t]|$)"
+			begin: "^([.'])[ \t]*(ie|if|el)(?=[ \t\\\\]|$)"
 			end: "(?<!\\\\)(?=\n)"
 			beginCaptures:
 				1: {name: "punctuation.definition.request.roff"}
@@ -346,14 +346,13 @@

 			# Underlined passage
 			name: "meta.request.ul.roff"
-			begin: "^([.'][ \t]*ul)(?: +(\\d+))?\\s*(.*?)\\s*?(\\\\\".*?)?\n"
+			begin: "^([.'][ \t]*ul)(?=[ \t\\\\]|$)(.*?)$"
 			end:   "([^\n ]+\n)"
 			patterns: [{ include: "$self" }]
 			beginCaptures:
 				1: {name: "entity.function.name.roff"}
-				2: {name: "variable.parameter.argument.roff"}
-				3: {name: "invalid.illegal.roff"}
-				4: {name: "comment.line.roff"}
+				2: {patterns: [{include: "#escapes"}]}
+				3: {name: "comment.line.roff"}
 			endCaptures:
 				0: {name: "markup.underline.roff"}
 		}]
@@ -368,7 +367,7 @@
 	# Macro: .B
 	"macro-bold":
 		name: "markup.bold.roff"
-		begin: "^[.'][ \t]*B(?:$| +)"
+		begin: "^[.'][ \t]*B(?:$|[ \t]+|(?=\\\\))"
 		end:   "([^\n ]+ ?\n)"
 		patterns: [{ include: "$self" }]

@@ -376,7 +375,7 @@
 	# Macro: .I
 	"macro-italic":
 		name: "markup.italic.roff"
-		begin: "^[.'][ \t]*I(?:$| +)"
+		begin: "^[.'][ \t]*I(?:$|[ \t]+|(?=\\\\))"
 		end:   "([^\n ]+ ?\n)"
 		patterns: [{ include: "$self" }]

@@ -384,7 +383,7 @@
 	# Macro: .BI
 	"macro-bold-italic":
 		name: "markup.bold-italic.roff"
-		begin: "^([.'][ \t]*BI)(?=[ \t]|$)"
+		begin: "^([.'][ \t]*BI)(?=[ \t\\\\]|$)"
 		end:   "(?<!\\\\)(?=\n)"
 		beginCaptures:
 			1: {name: "entity.function.name.roff"}
@@ -415,7 +414,7 @@
 	# Macro: .BR
 	"macro-bold-roman":
 		name: "markup.bold-roman.roff"
-		begin: "^([.'][ \t]*BR)(?=[ \t]|$)"
+		begin: "^([.'][ \t]*BR)(?=[ \t\\\\]|$)"
 		end:   "(?<!\\\\)(?=\n)"
 		beginCaptures:
 			1: {name: "entity.function.name.roff"}
@@ -445,7 +444,7 @@
 	# Macro: .IB
 	"macro-italic-bold":
 		name: "markup.italic-bold.roff"
-		begin: "^([.'][ \t]*IB)(?=[ \t]|$)"
+		begin: "^([.'][ \t]*IB)(?=[ \t\\\\]|$)"
 		end:   "(?<!\\\\)(?=\n)"
 		beginCaptures:
 			1: {name: "entity.function.name.roff"}
@@ -476,7 +475,7 @@
 	# Macro: .IR
 	"macro-italic-roman":
 		name: "markup.italic-roman.roff"
-		begin: "^([.'][ \t]*IR)(?=[ \t]|$)"
+		begin: "^([.'][ \t]*IR)(?=[ \t\\\\]|$)"
 		end:   "(?<!\\\\)(?=\n)"
 		beginCaptures:
 			1: {name: "entity.function.name.roff"}
@@ -506,7 +505,7 @@
 	# Macro: .RB
 	"macro-roman-bold":
 		name: "markup.roman-bold.roff"
-		begin: "^([.'][ \t]*RB)(?=[ \t]|$)"
+		begin: "^([.'][ \t]*RB)(?=[ \t\\\\]|$)"
 		end:   "(?<!\\\\)(?=\n)"
 		beginCaptures:
 			1: {name: "entity.function.name.roff"}
@@ -535,7 +534,7 @@
 	# Macro: .RI
 	"macro-roman-italic":
 		name: "markup.roman-italic.roff"
-		begin: "^([.'][ \t]*RI)(?=[ \t]|$)"
+		begin: "^([.'][ \t]*RI)(?=[ \t\\\\]|$)"
 		end:   "(?<!\\\\)(?=\n)"
 		beginCaptures:
 			1: {name: "entity.function.name.roff"}


refactored verifyEnrollmentEligibility :metal:

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -1256,7 +1256,7 @@
     daisy queue

-  verifyEnrollmentEligibility = (options, queue, callback, fetchData) ->
+  verifyEnrollmentEligibility = (options, callback) ->

     { email, client, invitationToken } = options

@@ -1274,8 +1274,7 @@
       if not isEligible
         return callback new Error "you can not register to #{client.context.group}"

-      fetchData invitation
-      queue.next()
+      callback null, invitation

   createUser = (options, callback) ->
@@ -1475,16 +1474,17 @@
           queue.next()

       ->
-        verifyUser { slug, email, recaptcha, userFormData, foreignAuthType }, (err) ->
+        params = { slug, email, recaptcha, userFormData, foreignAuthType }
+        verifyUser params, (err) ->
           return callback err  if err
           queue.next()

       ->
-        verifyEnrollmentEligibility {
-          email
-          client
-          invitationToken
-        }, queue, callback, (invitation_) -> invitation = invitation_
+        params = { email, client, invitationToken }
+        verifyEnrollmentEligibility params, (err, invitation_) ->
+          return callback err  if err
+          invitation = invitation_
+          queue.next()

       ->
         userInfo =


Server: removed outdated comment

Server: removed outdated comment

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -341,9 +341,6 @@
       constructor = this
       JSession.fetchSession clientId, (err, { session })->
         return callback err  if err
-        # temp fix:
-        # this broke login, reverted. - SY
-        # if not session? or session.username isnt username
         unless session
           console.error "login: session not found", username
           return callback { message: "Couldn't restore your session!" }


followable : added group data to follow relation

--- workers/social/lib/social/traits/followable.coffee
+++ workers/social/lib/social/traits/followable.coffee
@@ -139,7 +139,11 @@
       else if count > 0
         callback null, count
       else
-        @addFollower follower, respondWithCount : yes, (err, docs, count)=>
+        options            =
+          respondWithCount : yes
+          data             : {group}
+
+        @addFollower follower, options, (err, docs, count)=>
           if err
             callback err
           else


Remove focused spec

Remove focused spec

--- spec/atom-spec.coffee
+++ spec/atom-spec.coffee
@@ -4,7 +4,7 @@
 Package = require '../src/package'
 ThemeManager = require '../src/theme-manager'

-fdescribe "the `atom` global", ->
+describe "the `atom` global", ->
   beforeEach ->
     atom.workspaceView = new WorkspaceView


Fixed specs

Fixed specs

--- spec/main-spec.coffee
+++ spec/main-spec.coffee
@@ -19,19 +19,38 @@
           else
             test.schema

+          refs = test.refs?.map (ref) ->
+            if typeof ref is 'string'
+              pick(suite, ref)
+            else
+              ref
+
           if test.normalize
             backup = JSON.stringify(schema)
-            data = deref.util.normalizeSchema(schema)
+            data = deref.util.normalizeSchema(test.root, schema)

             expect(backup).toBe JSON.stringify(schema)
           else
-            $(schema, test.expand)
+            data = $(test.root, schema, refs, test.expand)

-          if test.example
-            expect(test.example).toHaveSchema data, test.refs
+          if test.data
+            expect(test.data).toHaveSchema data, $.refs

           if test.hasRefs >= 0
             expect(data).toHaveRefs test.hasRefs

-          if test.hasKeys
-            expect(pick(data, key)).toEqual test for key, test of test.hasKeys
+          if test.foundRefs
+            for key, value of test.foundRefs
+              found = try
+                $.util.findByRef(key, $.refs)
+              catch e
+                null
+
+              if typeof value is 'object'
+                unless found
+                  throw "Reference not found: #{key}"
+
+                expect(pick(found, k)).toEqual(v) for k, v of value
+
+          if test.hasProps
+            expect(pick(data, k)).toEqual(v) for k, v of test.hasProps


Fixed undefined object is not a function

--- lib/format.coffee
+++ lib/format.coffee
@@ -57,7 +57,7 @@
       # The cursor is right before a word in this case, so let's use the current cursor position as a reference
       #
       mainCursor.setBufferPosition(currentCursorPosition)
-      currentPosition = currentCursorPosition()
+      currentPosition = currentCursorPosition

     whitespaceText = textBuffer.getTextInRange([[0, 0], currentPosition])


fix spec

fix spec

--- test/address_resolver.coffee
+++ test/address_resolver.coffee
@@ -15,7 +15,7 @@
         @resolver.find('00').then (result) ->
           assert.ok result == null

-    context 'when the postal code is short', ->
+    context 'when the postal code not found', ->
       beforeEach ->
         @error = error.AddressNotFoundError
         @resolver = new AddressResolver
@@ -27,13 +27,19 @@

     context 'when run twice the same code', ->
       beforeEach ->
+        dict =
+          '0010933': [1,"札幌市北区","新川西三条"]
+
         @adapter =
+          called: 0
           codePrefixes: []
           addressDicts: {}

           find: (prefix) ->
             @codePrefixes.push prefix
-            Promise.resolve null
+            result = if @called > 0 then dict else null
+            @called++
+            Promise.resolve result
           store: (prefix, dict) ->
             @addressDicts[prefix] = dict


Ignore fractional-pixel in tab width test to pass on Windows

--- spec/tabs-spec.coffee
+++ spec/tabs-spec.coffee
@@ -108,11 +108,12 @@

       event = triggerMouseEvent('mouseenter', tabBar)

-      initialWidth1 = tabBar.tabAtIndex(0).getBoundingClientRect().width.toFixed(2)
-      initialWidth2 = tabBar.tabAtIndex(2).getBoundingClientRect().width.toFixed(2)
-
-      expect(tabBar.tabAtIndex(0).style.maxWidth).toBe initialWidth1 + 'px'
-      expect(tabBar.tabAtIndex(2).style.maxWidth).toBe initialWidth2 + 'px'
+      initialWidth1 = tabBar.tabAtIndex(0).getBoundingClientRect().width.toFixed(0)
+      initialWidth2 = tabBar.tabAtIndex(2).getBoundingClientRect().width.toFixed(0)
+
+      # Minor OS differences cause fractional-pixel differences so ignore fractional part
+      expect(parseFloat(tabBar.tabAtIndex(0).style.maxWidth.replace('px', '')).toFixed(0)).toBe initialWidth1
+      expect(parseFloat(tabBar.tabAtIndex(2).style.maxWidth.replace('px', '')).toFixed(0)).toBe initialWidth2

   describe "when the mouse is moved away from the tab bar", ->
     it "resets the width on every tab", ->


add function to create default options

add function to create default options

--- test/browserify-tests.coffee
+++ test/browserify-tests.coffee
@@ -1,4 +1,13 @@
 clearPreviousResults = -> delete Result[key] for key of Result
+
+defaultOptions = ->
+  return options =
+    basedir:'/full/path/to/app/packages/.npm/package'
+    debug:true
+    transforms:
+      envify:
+        NODE_ENV: 'development'
+        _:'purge'

 # test the usual successful run
 Tinytest.add 'test processFile', (test) ->
@@ -22,8 +31,7 @@
   test.equal Result.browserify.array?, true, 'must have an array to browserify'
   test.equal Result.browserify.array?.length, 1, 'browserify gets [readable], length should be 1'
   test.equal Result.browserify.array[0]?.isTestPassThrough, true, 'should receive our test PassThrough'
-  options = debug:true, basedir:'/full/path/to/app/packages/.npm/package'
-  test.equal Result.browserify?.options, options
+  test.equal Result.browserify?.options, defaultOptions()

   # test transform (only envify)
   test.equal Result?.envify?.options?, true, 'envify should be called with options'
@@ -50,7 +58,7 @@
 Tinytest.add 'test with browserify error', (test) ->

   clearPreviousResults()
-
+
   # cause it to use the on error callback
   Result.errorWanted = true

@@ -73,8 +81,7 @@
   test.equal Result.browserify.array?, true, 'must have an array to browserify'
   test.equal Result.browserify.array?.length, 1, 'browserify gets [readable], length should be 1'
   test.equal Result.browserify.array[0]?.isTestPassThrough, true, 'should receive our test PassThrough'
-  options = debug:true, basedir:'/full/path/to/app/packages/.npm/package'
-  test.equal Result.browserify?.options, options
+  test.equal Result.browserify?.options, defaultOptions()

   # test bundling with browserify and reading its result
   test.equal Result.browserify?.bundle, true, 'must run browserify.bundle()'


Cleanup weather.coffee

Cleanup weather.coffee

--- homedisplay/info_weather/static/js/weather.coffee
+++ homedisplay/info_weather/static/js/weather.coffee
@@ -81,7 +81,7 @@

     sea = data.warnings.sea.replace new RegExp("
", "g"), ""
     warnings_html = ""
-    weather_early_html = ""
+    warnings_early_html = ""
     warnings_data =
       "female": data.warnings.pedestrian
       "ship": sea
@@ -144,13 +144,6 @@
     current_row = null
     highlight_set = false
     last_header = null
-    new_item = """

-

-

-
-
-      m/s
-
"""
     current_day = null
     days = 0

@@ -178,18 +171,20 @@
         current_day = date
         days += 1

-      current_row.append new_item
-      current_item = current_row.find(".col-md-1").last()
-
-      current_item.find(".timestamp").html "#{timestamp.hour()}:00"
-      current_item.find(".temperature").html "#{@FeelsLike}°C"
-      current_item.find(".symbol").html """"""
-      current_item.find(".temperature-unit").html "°C"
-      current_item.find(".wind-speed").html Math.round(@WindSpeedMS)
       if @WindCompass8?
         # Do not show wind direction if no data is available - fallback is arrow pointing to north, which is
         # worse than showing nothing.
-        current_item.find(".wind-direction").html ""
+        wind_direction = ""
+      else
+        wind_direction = ""
+
+      current_row.append """

+        #{timestamp.format("HH")}:00

+

+        #{@FeelsLike}°C
+        #{wind_direction}
+        #{Math.round(@WindSpeedMS)}m/s
+
"""

       current_index += 1

@@ -200,20 +195,24 @@
       console.warn "Received invalid data ", data
       return

-    if data.main_forecasts.suninfo?
-      processSunInfo data.main_forecasts.suninfo
+    if data.main_forecasts?
+      mf = data.main_forecasts
+      if mf.suninfo?
+        processSunInfo mf.suninfo
+
+      if mf.observations?
+        setObservations weather, mf.observations
+
+      if mf.forecasts?
+        processForecasts mf.forecasts[0].forecast

     if data.main_warnings?
       processWarnings data.main_warnings

-    if data.main_forecasts? and data.main_forecasts.observations?
-      setObservations weather, data.main_forecasts.observations

     if data.marine_forecasts?
       processMarine data.marine_forecasts

-    if data.main_forecasts.forecasts?
-      processForecasts data.main_forecasts.forecasts[0].forecast


fix comments

fix comments

--- lib/browse.coffee
+++ lib/browse.coffee
@@ -57,7 +57,7 @@
         when "linux"
           args = filePath

-      # Open packages folder
+      # Reveal file
       exec "#{@fileManager} #{args}"
       return

@@ -92,7 +92,7 @@
         atom.notifications.addError(@self, detail: error, dismissable: true)
         return

-      # Open packages folder
+      # Open config folder
       exec "#{@fileManager} #{configPath}"

   getFileManager: ->


No code is good code

No code is good code

--- src/scripts/view/item-view.coffee
+++ src/scripts/view/item-view.coffee
@@ -22,11 +22,6 @@
   modelEvents:
     'change': 'render'

-  todoText: ''
-
-  initialize: ->
-    @todoText = @model.get 'title'
-
   onRender: ->
     @$el.removeClass 'active completed'
     @$el.addClass if @model.get 'completed' then 'completed' else 'active'
@@ -42,24 +37,24 @@

   onEditStart: ->
     @toggleEditingMode()
-    this.ui.edit.focus().val @todoText
+    this.ui.edit.focus().val this.ui.label.text()

   onEditKeypress: (event) ->
     ENTER_KEY  = 13
     ESCAPE_KEY = 27

-    if event.which == ENTER_KEY
+    if event.which is ENTER_KEY
       this.ui.edit.trigger 'blur'

-    if event.which == ESCAPE_KEY
+    if event.which is ESCAPE_KEY
       this.ui.edit.val @model.get 'title'
       this.ui.edit.trigger 'blur'

   onEditStop: (event) ->
     @toggleEditingMode()
-    @todoText = event.target.value.trim()
+    todoText = event.target.value.trim()

-    if @todoText
-      @model.set('title', @todoText).save()
+    if todoText
+      @model.set('title', todoText).save()
     else
       @model.destroy()


improve syncstring maintenance error message

--- src/smc-hub/postgres-blobs.coffee
+++ src/smc-hub/postgres-blobs.coffee
@@ -644,7 +644,7 @@
                         else if not x?
                             cb("no such syncstring with id '#{opts.string_id}'")
                         else if x.archived
-                            cb("already archived")
+                            cb("string_id='#{opts.string_id}' already archived as blob id '#{x.archived}'")
                         else
                             project_id = x.project_id
                             last_active = x.last_active


Tweak warning

Tweak warning

--- src/smc-webapp/project_files.cjsx
+++ src/smc-webapp/project_files.cjsx
@@ -349,14 +349,14 @@

 TerminalModeDisplay = rclass
     render : ->
-        <Row style={textAlign:'left', color:'#888', marginTop:'20px', wordWrap:'break-word'} >
+        <Row style={textAlign:'left', color:'#888', marginTop:'5px', wordWrap:'break-word'} >
             <Col sm=2>
             </Col>
             <Col sm=8>
-                <Alert style={marginTop: '10px', fontWeight : 'bold'} bsStyle='danger'>
-                Warning: You are in terminal mode.

-                This was caused by the leading / in your file search. If you would instead like to see just your folders, enter a space in front of the /.

-                Terminal mode in file listing is experimental and comes with no guarantees about its usability or future existence.
+                <Alert style={marginTop: '5px', fontWeight : 'bold'} bsStyle='danger'>
+                Warning: You are in terminal mode.

+                This was caused by the leading / in your file search. If you want to just see your folders, enter a space in front of the /.

+                Terminal mode inside the search bar is experimental and comes with no guarantees about its usability or future existence.
             </Alert>
             </Col>
             <Col sm=2>