Add campaign link

Add campaign link

--- framer/Components/ShareComponent.coffee
+++ framer/Components/ShareComponent.coffee
@@ -172,7 +172,7 @@
 		@_enableUserSelect(ctaSlogan)

 		ctaLink = new Button
-			url: "http://www.framerjs.com"
+			url: "http://framerjs.com/?utm_source=share.framerjs.com&utm_medium=banner&utm_campaign=product"
 			parent: @cta
 			y: ctaSlogan.y + 24
 			height: 16
@@ -349,6 +349,7 @@
 				@description.style =
 					cursor: "default"

+			# Remove events when description is shown to make links clickable
 			@description.off Events.TapStart, descriptionClickRegister
 			@description.off Events.TapEnd, descriptionClickCompare


Generate the room name once position is known and subscribe to a room with the the location name.

--- src/app/main/main.controller.coffee
+++ src/app/main/main.controller.coffee
@@ -13,10 +13,11 @@

     pusher = $pusher(client)

-    $scope.channel = pusher.subscribe('private-channel')
+    $scope.subscribeToLocalRoom = ->
+      $scope.channel = pusher.subscribe("private-location-#{$scope.roomName()}")

-    $scope.channel.bind "pusher:subscription_succeeded", ->
-      $scope.state.pusherSubscription.active = true
+      $scope.channel.bind "pusher:subscription_succeeded", ->
+        $scope.state.pusherSubscription.active = true

     alert = $mdDialog.alert({
       title: 'Getting your location',
@@ -30,6 +31,16 @@
         lng: 150.644

     map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions)
+
+    $scope.getPositionToDegreeOfAccuracy = (accuracy = 2) ->
+      {
+        latitude: $scope.position.coords.latitude.toFixed(accuracy)
+        longitude: $scope.position.coords.longitude.toFixed(accuracy)
+      }
+
+    $scope.roomName = ->
+      position = $scope.getPositionToDegreeOfAccuracy(1)
+      "#{position.latitude}#{position.longitude}"

     $scope.shouldShowNewMessageForm = ->
       return false unless $scope.position?
@@ -52,6 +63,8 @@
       navigator.geolocation.getCurrentPosition (position) ->
         $scope.position = position
         $mdDialog.hide()
+        $scope.subscribeToLocalRoom()
+
         pos = new google.maps.LatLng(position.coords.latitude, position.coords.longitude)
         map.setCenter pos
         map.setZoom 14


fixed billing info modal for existent credit account

--- client/landing/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
+++ client/landing/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
@@ -26,7 +26,7 @@
         "Domain Address"              :
           callback                    : @bound "registerDomain"
           buttons                     :
-            billingButton             :
+            registerButton             :
               title                   : "Register Domain"
               style                   : "cupid-green hidden"
               type                    : "submit"
@@ -129,28 +129,18 @@

   needBilling:(paymentRequired)->
     form = @forms["Domain Address"]
-    {createButton, billingButton} = form.buttons
+    {createButton, registerButton} = form.buttons

     unless paymentRequired
       createButton.show()
-      billingButton.hide()
+      registerButton.hide()
       return
-
-    paymentController = KD.getSingleton('paymentController')
-    group             = KD.getSingleton("groupsController").getCurrentGroup()
-
-    paymentController.getBillingInfo 'user', group, (err, account)->
-      need = err or not account or not account.cardNumber
-      if need
-        billingButton.show()
-        createButton.hide()
-      else
-        createButton.show()
-        billingButton.hide()
+    createButton.hide()
+    registerButton.show()

   registerDomain:->
     form = @forms["Domain Address"]
-    {createButton, billingButton} = form.buttons
+    {createButton, registerButton} = form.buttons
     @clearSuggestions()

     {DomainOption, domainName, regYears, domains} = form.inputs
@@ -175,33 +165,41 @@
         switch status
           when "regthroughus", "regthroughothers"
             @showSuggestions suggestions
-            billingButton.hideLoader()
+            registerButton.hideLoader()
             return createButton.hideLoader()
           when "unknown"
             notifyUser "An error occured. Please try again later."
             return createButton.hideLoader()
         form = @forms["Domain Address"]
-        {createButton, billingButton} = form.buttons
+        {createButton, registerButton} = form.buttons
         paymentController = KD.getSingleton('paymentController')
         group             = KD.getSingleton("groupsController").getCurrentGroup()
-        billingButton.hideLoader()
-        paymentController.setBillingInfo 'user', group, (success)->
-          if success
-            billingButton.hideLoader()
-            billingButton.hide()
-            createButton.show()
-
+
+        registerTheDomain () = ->
             KD.remote.api.JDomain.registerDomain
               domainName : domainInput.getValue()
               years      : regYears.getValue()
             , (err, domain)=>
-              createButton.hideLoader()
               if err
                 warn err
                 notifyUser "An error occured. Please try again later."
               else
                 @showSuccess domain
                 domain.setDomainCNameToProxyDomain()
+              registerButton.hideLoader()
+
+        paymentController.getBillingInfo 'user', group, (err, account)->
+          need = err or not account or not account.cardNumber
+          if need
+            paymentController.setBillingInfo 'user', group, (success)->
+              if success
+                registerTheDomain()
+              else
+                registerButton.hideLoader()
+          else
+            registerTheDomain()
+
+

     else if domainOptionValue is 'existing'
       @createDomain {domainName, regYears:0, domainType:'existing'}, (err, domain)=>


cleaned up Contrib.FastCollectionView#modelSerialize

--- src/backbone.giraffe.contrib.coffee
+++ src/backbone.giraffe.contrib.coffee
@@ -189,8 +189,8 @@
   @getDefaults: (ctx) ->
     collection: if ctx.collection then null else new Giraffe.Collection # lazy lood for efficiency
     modelTemplate: null # either this or a `modelTemplateStrategy` function is required
+    modelSerialize: -> @model # function returning the data passed to `modelTemplate`; called in the context of `modelTemplateCtx`
     modelTemplateStrategy: ctx.templateStrategy # inherited by default, can be overridden to directly provide a string without using `template` and `serialize`
-    modelSerialize: ctx.modelSerialize # default defined on prototype returns `this.model`; is function returning the data passed to `modelTemplate`; called in the context of `modelTemplateCtx`
     modelEl: null # optional selector or Giraffe.View#ui name to contain the model html

@@ -303,18 +303,12 @@

   ###
-  * Default serialize function for the model template.
-  ###
-  modelSerialize: ->
-    @model
-
-
-  ###
   * Generates a model's html string using `modelTemplateCtx` and its options.
   ###
   _renderModel: (model) ->
     @modelTemplateCtx.model = model
     @modelTemplateCtx.templateStrategy()
+    @

   ###


DataField

DataField

- Allow empty tooltip.text/content: When not set, not Tooltip
  is created.

--- src/base/DataField/DataField.coffee
+++ src/base/DataField/DataField.coffee
@@ -39,7 +39,7 @@
 		if @_hidden
 			@hide()

-		if @_tooltip
+		if @_tooltip and (@_tooltip.text or @_tooltip.content)
 			tt_opts = copyObject(@_tooltip)
 			tt_opts.element = @DOM
 			@__tooltip = new Tooltip(tt_opts)


removed comments

removed comments

--- src/api/apps/articles/test/model/save.test.coffee
+++ src/api/apps/articles/test/model/save.test.coffee
@@ -19,8 +19,6 @@
       done()

     date = new Date("Tue Jan 01 2019 00:00:00")
-    # offset = date.getTimezoneOffset()
-    # dateWithOffset = moment(date).add(offset, 'm').toDate();
     sandbox.useFakeTimers(date)

@@ -199,13 +197,9 @@
       })

     it 'appends unix timestamp for current date to the slug if the slug exists already and it is a draft and there is no publish date', (done) ->
-      x = new Date()
-      console.log("date now",x)
-      os = x.getTimezoneOffset()
-
+      now = new Date()
+      os = now.getTimezoneOffset()
       sandbox.clock.tick(os * -60000)
-      y = new Date()
-      console.log("date now",y)

       Save.sanitizeAndSave( =>
         Save.generateSlugs {


Rely on events to hide the "Check for update" menu item

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -165,7 +165,6 @@

     autoUpdater.on 'update-not-available', @onUpdateNotAvailable
     autoUpdater.on 'error', @onUpdateError
-    @applicationMenu.showCheckForUpdateItem(false)
     autoUpdater.checkForUpdates()

   # Registers basic application commands, non-idempotent.


Separate the offset determination from the dimensions.

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -360,15 +360,18 @@
   focusedWindow: ->
     _.find @windows, (atomWindow) -> atomWindow.isFocused()

+  # Get the platform-specific window offset for new windows.
+  getWindowOffsetForCurrentPlatform: ->
+    offsetByPlatform =
+      darwin: 22
+      win32: 26
+    offsetByPlatform[process.platform] ? 0
+
   # Get the dimensions for opening a new window by cascading as appropriate to
   # the platform.
   getDimensionsForNewWindow: ->
-    offsetByPlatform =
-      darwin: 22
-      win32: 26
-
     dimensions = (@focusedWindow() ? @lastFocusedWindow)?.getDimensions()
-    offset = offsetByPlatform[process.platform]
+    offset = @getWindowOffsetForCurrentPlatform()
     if dimensions? and offset?
       dimensions.x += offset
       dimensions.y += offset


Add correct action messages for JOpinion and JDiscussion

--- client/app/CommonViews/messagesList.coffee
+++ client/app/CommonViews/messagesList.coffee
@@ -69,6 +69,8 @@
     JAccount        : "started following you."
     JPrivateMessage : "your private message."
     JComment        : "your comment."
+    JDiscussion     : "your discussion."
+    JOpinion        : "your opinion."

   bucketNameMap = ->
     CReplieeBucketActivity  : "comment"


lexer: removed unused UNARY tokens

lexer: removed unused UNARY tokens


Added THIS to CALLABLE.

Added THIS to CALLABLE.

--- lexer.coffee
+++ lexer.coffee
@@ -511,7 +511,7 @@
 # Tokens which could legitimately be invoked or indexed. A opening
 # parentheses or bracket following these tokens will be recorded as the start
 # of a function invocation or indexing operation.
-CALLABLE: ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@']
+CALLABLE: ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS']

 # Tokens that indicate an access -- keywords immediately following will be
 # treated as identifiers.


added chroma.luminance() shortcut

added chroma.luminance() shortcut

--- src/color.coffee
+++ src/color.coffee
@@ -555,3 +555,5 @@
     l2 = b.luminance()
     if l1 > l2 then (l1 + 0.05) / (l2 + 0.05) else (l2 + 0.05) / (l1 + 0.05)

+chroma.luminance = (color) ->
+    chroma.color(color).luminance()


cleaning up array constructions

cleaning up array constructions

--- src/color.coffee
+++ src/color.coffee
@@ -75,9 +75,9 @@

     parsedColor = null

-    if arguments.length == 0
+    if args.length == 0
       parsedColor = [0, 0, 0, 1]
-    else if arguments.length == 1 && Object.isArray(arguments[0])
+    else if args.length == 1 && Object.isArray(args.first())
       alpha = if arguments[0][3]? then arguments[0][3] else 1
       parsedColor = [
         parseInt(arguments[0][0]),


Use jQuery events

Use jQuery events

--- client/publication.coffee
+++ client/publication.coffee
@@ -625,8 +625,7 @@
 Template.publicationAnnotationsItem.rendered = ->
   # Run for the first time only
   unless @rendered
-    $annotation = @data
-    $saved = $(@find '.saved')
+    $saved = $(@findAll '.saved')

     fadeOutSavedNotification = _.debounce ->
       $saved.fadeOut 500
@@ -634,16 +633,18 @@

     # TODO: Improve cross-browser compatibility
     # https://developer.mozilla.org/en-US/docs/Web/Reference/Events/input
-    @find('.body[contenteditable=true]').addEventListener 'input', ->
-      LocalAnnotations.update $annotation._id,
+    $(@findAll '.body[contenteditable=true]').on 'input', (e) =>
+      LocalAnnotations.update @data._id,
         $set:
-          body: $(@).text()
+          body: $(e.target).text()
           local: false
       , (error) ->
         throw error if error

         $saved.show()
         fadeOutSavedNotification()
+
+      return

     @rendered = true


Don't remove stale stances, just set them latest false

--- angular/core/models/poll_model.coffee
+++ angular/core/models/poll_model.coffee
@@ -110,7 +110,7 @@
       existing = []
       _.each @latestStances('-createdAt'), (stance) ->
         if _.contains(existing, stance.participant())
-          stance.remove()
+          stance.latest = false
         else
           existing.push(stance.participant())


Eliminate double-JSON.stringify on save/patch. Clear @previouslySavedContent when not in use.

--- src/code/providers/document-store-provider.coffee
+++ src/code/providers/document-store-provider.coffee
@@ -190,7 +190,7 @@
         {withCredentials}
       success: (data) ->
         content = cloudContentFactory.createEnvelopedCloudContent data
-        if @options.patch then @previouslySavedContent = content.clone()
+        @previouslySavedContent = if @options.patch then content.clone() else null
         metadata.name ?= data.docName
         callback null, content
       error: ->
@@ -266,7 +266,7 @@
       dataType: 'json'
       type: 'POST'
       url: url
-      data: pako.deflate(JSON.stringify sendContent)
+      data: pako.deflate sendContent
       contentType: mimeType
       processData: false
       beforeSend: (xhr) ->
@@ -275,7 +275,7 @@
       xhrFields:
         withCredentials: true
       success: (data) ->
-        if @options.patch then @previouslySavedContent = cloudContent.clone()
+        @previouslySavedContent = if @options.patch then cloudContent.clone() else null
         if data.id then metadata.providerData.id = data.id

         callback null, data


Fix the import progress not having the correct translations

--- packages/rocketchat-importer/client/admin/adminImportProgress.coffee
+++ packages/rocketchat-importer/client/admin/adminImportProgress.coffee
@@ -12,28 +12,29 @@
 	@completed = new ReactiveVar 0
 	@total = new ReactiveVar 0
 	@updateProgress = ->
-		Meteor.call 'getImportProgress', FlowRouter.getParam('importer'), (error, progress) ->
-			if error
-				console.warn 'Error on getting the import progress:', error
-				handleError error
-				return
+		if FlowRouter.getParam('importer') isnt ''
+			Meteor.call 'getImportProgress', FlowRouter.getParam('importer'), (error, progress) ->
+				if error
+					console.warn 'Error on getting the import progress:', error
+					handleError error
+					return

-			if progress
-				if progress.step is 'importer_done'
-					toastr.success t(progress.step)
-					FlowRouter.go '/admin/import'
-				else if progress.step is 'importer_import_failed'
-					toastr.error t(progress.step)
+				if progress
+					if progress.step is 'importer_done'
+						toastr.success t(progress.step[0].toUpperCase() + progress.step.slice(1))
+						FlowRouter.go '/admin/import'
+					else if progress.step is 'importer_import_failed'
+						toastr.error t(progress.step[0].toUpperCase() + progress.step.slice(1))
+						FlowRouter.go '/admin/import/prepare/' + FlowRouter.getParam('importer')
+					else
+						instance.step.set t(progress.step[0].toUpperCase() + progress.step.slice(1))
+						instance.completed.set progress.count.completed
+						instance.total.set progress.count.total
+						setTimeout(() ->
+							instance.updateProgress()
+						, 100)
+				else
+					toastr.warning t('Importer_not_in_progress')
 					FlowRouter.go '/admin/import/prepare/' + FlowRouter.getParam('importer')
-				else
-					instance.step.set t(progress.step)
-					instance.completed.set progress.count.completed
-					instance.total.set progress.count.total
-					setTimeout(() ->
-						instance.updateProgress()
-					, 100)
-			else
-				toastr.warning t('importer_not_in_progress')
-				FlowRouter.go '/admin/import/prepare/' + FlowRouter.getParam('importer')

 	instance.updateProgress()


Reverted autorun call

Reverted autorun call

--- packages/rocketchat-message-star/client/views/starredMessages.coffee
+++ packages/rocketchat-message-star/client/views/starredMessages.coffee
@@ -9,7 +9,8 @@
 		return 'notready' unless Template.instance().subscriptionsReady()

 Template.starredMessages.onCreated ->
-	this.subscribe 'starredMessages', Template.currentData().rid
+	this.autorun =>
+		this.subscribe 'starredMessages', Template.currentData().rid

 Template.starredMessages.events
 	'click .message-cog': (e) ->


on login, avatararea and finderheader render issues fixed.

--- core/KDObject.coffee
+++ core/KDObject.coffee
@@ -170,7 +170,13 @@
   setData:(data)->
     return warn "setData called with null or undefined!" unless data?
     @data = data
-    data.emit? "update"
+    # fix
+    # this unfortunately doesn't work
+    # because we change the data here.
+    # in a view constructor we do data.on "update"
+    # but here that data is reset/changed and listener becomes obsolete
+    # bc new data isn't being listened
+    data.emit? 'update'

   getData:-> @data


Make constraints string DOM less lame (no longer one text node per char) using new fragmentReplacingStrainChars method

--- gae/scripts/view.coffee
+++ gae/scripts/view.coffee
@@ -725,12 +725,7 @@
         for [needle, substitution] in replacements
             constraintsString = constraintsString.replace(needle, substitution)

-        for char in constraintsString
-            if char in "CDHSN"
-                strain = model.Strain.fromChar char
-                @appendChild StrainView.fromStrain strain
-            else
-                @appendChild document.createTextNode char
+        @appendChild StrainView.fragmentReplacingStrainChars(constraintsString)

     @fromConstraintsString: (constraintsString) ->
         return alloc @, constraintsString


webserver: reimplement "services-presence" in terms of kontrol

--- server/lib/server/services-presence.coffee
+++ server/lib/server/services-presence.coffee
@@ -1,93 +1,45 @@
-{ argv } = require 'optimist'
+request = require 'request'

-koding = require './bongo'
+supportedServices = ['broker']

-opsview = require 'koding-opsview-wrapper'
+getFailoverUrl = ->
+  { webProtocol: protocol, webHostname: hostname, webPort: port } =
+    KONFIG.broker

-parseServiceKey = require 'koding-service-key-parser'
+  # piece the url together from the config:
+  url = "#{ protocol }//#{ hostname }#{ if port then ":#{port}" else "" }"

-allServices = {}
+failover = (req, res, multi) ->
+  if req.params.service is 'broker'
+    url = getFailoverUrl()
+    res.set 'Content-Type', 'application/json'
+    res.send if multi then [url] else JSON.stringify url
+    yes
+  else
+    no

-incService = (serviceKey, amount) ->
-  serviceInfo = parseServiceKey serviceKey
+module.exports = (req, res, next) ->
+  { params, query } = req

-  { serviceGenericName, serviceUniqueName } = serviceInfo
+  { service } = params

-  fetchHostname serviceGenericName, serviceUniqueName, (err, hostname) ->
+  if service not in supportedServices
+    next()
+    return

-    allServices[serviceGenericName] ?= { seq: 0, services: {} }
+  multi = query.all?

-    genericServices = allServices[serviceGenericName].services
+  if KONFIG.runKontrol # let kontrol provide the url

-    if genericServices[serviceUniqueName]?
-    then genericServices[serviceUniqueName].amount += amount
-    else genericServices[serviceUniqueName] = { amount, hostname }
+    url = "#{ KONFIG.kontrold.api.url }/workers/url/#{ service }#{
+      if multi
+      then '?all'
+      else ''
+    }"

-    if genericServices[serviceUniqueName].amount is 0
-      delete genericServices[serviceUniqueName]
-    else if genericServices[serviceUniqueName].amount < 0
-      console.error 'Negative service count!'
+    request(url)
+      .on('error', (err) -> next()  unless failover req, res, multi)
+      .pipe(res)

-koding.connect ->
-  opsview.send 'Services presence', 0, 'Service started'
-  koding.monitorPresence
-    join: (serviceKey) ->
-      incService serviceKey, 1
-    leave: (serviceKey) ->
-      incService serviceKey, -1
-
-fetchHostname = (serviceGenericName, serviceUniqueName, callback) ->
-  if 'broker' is serviceGenericName and not KONFIG.runKontrol
-    process.nextTick -> callback null,
-      if KONFIG.broker.webPort?
-      then "#{ KONFIG.broker.webHostname }:#{ KONFIG.broker.webPort }"
-      else KONFIG.broker.webHostname
-  else
-    (koding.getClient().collection 'jKontrolWorkers')
-      .findOne {
-        serviceUniqueName
-        hostname  : ///^#{ serviceGenericName }///
-        status    : 0
-      }, { hostname: 1 }, (err, worker) ->
-          return callback err  if err?
-          callback null, worker?.hostname ? null
-
-module.exports = do (failing = no) -> (req, res) ->
-  { params:{ service }, query } = req
-
-  protocol = KONFIG.broker.webProtocol ? 'https:'
-
-  genericServices = allServices[service]
-
-  { services: s } = genericServices
-
-  services =
-    Object.keys(s).map( (k) ->
-      { hostname } = s[k]
-      unless hostname?
-        console.warn """
-          Could not find that hostname:
-          #{k}
-          #{require('util').inspect genericServices.services[k]}
-          """
-      return hostname
-    ).filter Boolean
-
-  res.set "Content-Type", "text/json"
-
-  if query.all?
-    res.send services.map (hostname) -> "#{ protocol }//#{ hostname }"
-
-  else if services.length is 0
-    # FAILURE! send an alert to opsview.
-    unless failing
-      failing = yes
-      opsview.send 'Services presence', 2, 'Service loadbalancing failure detected!'
-    # Fail-over to the value hard-coded into the config.
-    { webHostname, webPort } = KONFIG.broker
-    res.send "\"#{ protocol }//#{ webHostname }#{ if webHostname.port then ":#{webHostname.port}" else "" }\""
-
-  else
-    failing = no
-    i = genericServices.seq++ % services.length
-    res.send "\"#{ protocol }//#{ services[i] }\""
+  else # handle fail over
+    next()  unless failover req, res, multi


Fixed some whitespace.

Fixed some whitespace.

--- src/lib/pattern.coffee
+++ src/lib/pattern.coffee
@@ -365,7 +365,7 @@
         # JavaScript scalar, taken in part from
         # [json2.js](http://www.JSON.org/json2.js).
         while hasArgument
-          arg         = ///
+          arg = ///
             ( '(?:[^\\']|\\.)+'|"(?:[^\\"]|\\.)+"   # string
             | true | false                          # boolean
             | null                                  # null


Make AdaptiveScale a little simpler.

Make AdaptiveScale a little simpler.

--- bokehjs/src/coffee/common/ticking.coffee
+++ bokehjs/src/coffee/common/ticking.coffee
@@ -172,19 +172,16 @@
                                               desired_n_ticks)

   class AdaptiveScale extends AbstractScale
-    constructor: (@mantissas, @base=10.0, @min_magnitude=0.0,
-                  @max_magnitude=Infinity)->
+    constructor: (@mantissas, @base=10.0, @min_interval=0.0,
+                  @max_interval=Infinity)->
       super(['mantissas', 'base', 'min_magnitude', 'max_magnitude'])
-
-      @min_interval = _.first(@mantissas) * @min_magnitude
-      @max_interval =  _.last(@mantissas) * @max_magnitude

       prefix_mantissa =  _.last(@mantissas) / @base
       suffix_mantissa = _.first(@mantissas) * @base
-      @allowed_mantissas = _.flatten([prefix_mantissa, @mantissas,
-                                      suffix_mantissa])
-
-      @base_factor = if @min_magnitude == 0.0 then 1.0 else @min_magnitude
+      @extended_mantissas = _.flatten([prefix_mantissa, @mantissas,
+                                       suffix_mantissa])
+
+      @base_factor = if @min_interval == 0.0 then 1.0 else @min_interval

     get_interval: (data_low, data_high, desired_n_ticks) ->
       data_range = data_high - data_low
@@ -196,9 +193,9 @@
       ideal_mantissa = ideal_interval / ideal_magnitude

       # An untested optimization.
-#       index = _.sortedIndex(@allowed_mantissas, ideal_mantissa)
-#       candidate_mantissas = @allowed_mantissas[index..index + 1]
-      candidate_mantissas = @allowed_mantissas
+#       index = _.sortedIndex(@extended_mantissas, ideal_mantissa)
+#       candidate_mantissas = @extended_mantissas[index..index + 1]
+      candidate_mantissas = @extended_mantissas

       errors = candidate_mantissas.map((mantissa) ->
         Math.abs(desired_n_ticks -
@@ -331,7 +328,7 @@

       return ticks_in_range

-  HUNDRED_MILLIS = 100.0
+  ONE_MILLI = 1.0
   ONE_SECOND = 1000.0
   ONE_MINUTE = 60.0 * ONE_SECOND
   ONE_HOUR = 60 * ONE_MINUTE
@@ -347,36 +344,29 @@
     constructor: () ->
       super([
         # Sub-second.
-        # (0 - 500 ms)
-        new AdaptiveScale([1.0, 2.0, 5.0], 10.0, 0.0, HUNDRED_MILLIS),
+        new AdaptiveScale([1, 2, 5], 10, 0, 500 * ONE_MILLI),

         # Seconds, minutes.
-        # (1 s - 30 min)
-        new AdaptiveScale([1.0, 2.0, 5.0, 10.0, 15.0, 20.0, 30.0], 60.0,
-                          ONE_SECOND, ONE_MINUTE),
+        new AdaptiveScale([1, 2, 5, 10, 15, 20, 30], 60,
+                          ONE_SECOND, 30 * ONE_MINUTE),

         # Hours.
-        # (1 hr - 12 hr)
-        new AdaptiveScale([1.0, 2.0, 4.0, 6.0, 8.0, 12.0], 24.0,
-                          ONE_HOUR, ONE_HOUR),
+        new AdaptiveScale([1, 2, 4, 6, 8, 12], 24.0, ONE_HOUR, 12 * ONE_HOUR),

         # Days.
-        # (1 day - 14 days)
         new DaysScale(arange(1, 32)),
         new DaysScale(arange(1, 31, 3)),
         new DaysScale([1, 8, 15, 22]),
         new DaysScale([1, 15]),

         # Months.
-        # (1 month - 6 months)
         new MonthsScale(arange(0, 12)),
         new MonthsScale(arange(0, 12, 2)),
         new MonthsScale(arange(0, 12, 4)),
         new MonthsScale(arange(0, 12, 6)),

         # Catchall for large timescales.
-        # (1 year - infinity)
-        new AdaptiveScale([1.0, 2.0, 5.0], 10.0, ONE_YEAR, Infinity),
+        new AdaptiveScale([1, 2, 5], 10, ONE_YEAR, Infinity),
       ])

   class BasicTickFormatter


file list scrolls to visible area when invisible;

--- app/assets/javascripts/file.js.coffee
+++ app/assets/javascripts/file.js.coffee
@@ -7,7 +7,9 @@
     path = column_element.data('ls-path')
     $.getJSON path, (d) ->
       # find next column
-      next_column = column_element.parent().next('li').find('ul.column[data-ls-path]')
+      column_parent = column_element.parent()
+      column_height = column_parent.height()
+      next_column = column_parent.next('li').find('ul.column[data-ls-path]')
       next_column_ls_path = null
       if next_column.length > 0
         load_file_list next_column
@@ -26,8 +28,12 @@
           html: ''+b.name
         })
         anchor.data('path', path+b.name)
-        if b.name is next_column_ls_path then anchor.addClass('active')
         column_element.append($('
').append(anchor))
+        # select
+        if b.name is next_column_ls_path
+          anchor.addClass('active')
+          if anchor.position().top > column_height - 20 # scroll to visible area
+            column_parent.animate({ scrollTop: anchor.position().top - column_height / 2 }, 1);

   create_new_list = (path, after_element) ->
     parent = after_element.parent()


improve game logic

improve game logic

--- app/assets/javascripts/game.js.coffee
+++ app/assets/javascripts/game.js.coffee
@@ -3,8 +3,8 @@
   states = {
     inited: 'inited'
     choose_face: 'choose_face'
-    player1: 'player1_turn'
-    player2: 'player2_turn'
+    player1: 'player1'
+    player2: 'player2'
     finished: 'finished'
   }

@@ -21,6 +21,9 @@

   p1_face_index = undefined
   p2_face_index = undefined
+
+  p1_click_try = 0
+  p2_click_try = 0

   question_panel = undefined

@@ -68,6 +71,10 @@
       else
         state = states['player2']

+
+      $('body').on 'click', p1_card_selector, choose_face_p2
+      $('body').on 'click', p2_card_selector, choose_face_p1
+
       change_turn()

     else alert('Fuck off')
@@ -83,7 +90,7 @@
     else
       """

Your turn

-
Please ask a question

+
Please ask a question or choose face on apponent board (you have #{3 - p2_click_try} attempts)

           A person
           <select class="relation"><option value="+">have</option><option value="-">dont have</option></select>
@@ -114,7 +121,26 @@

   choose_target = ->
     p2_face_index = $(@).attr('card_id')
+    $('body').off 'click', p2_card_selector
     choose_turn()
+
+
+  choose_face_p1 = ->
+    p1_click_try++
+    face_index = $(@).attr('card_id')
+
+    if (face_index == p2_face_index)
+      finished('player1')
+
+
+  choose_face_p2 = ->
+    p2_click_try++
+
+    face_index = $(@).attr('card_id')
+
+    if (face_index == p1_face_index)
+      finished('player2')
+

   choose_question = ->
     rel_val = question_panel.find('.relation').val()
@@ -123,7 +149,7 @@
     questions_list = state = states['player1'] ? p1_questions : p2_questions

     if (questions_list[rel_val + property_val])
-      finished()
+      finished(if state == states['player1'] then 'player2' else 'player1')
     else
       questions_list[rel_val + property_val] = true
       alert(rel_val + property_val)
@@ -141,14 +167,11 @@

     proc_question_panel()

-  finished = ->
+  finished = (player_name) ->
     state = states['finished']
-
+    alert(player_name + ' win!!')

   return {
     initiate: initiate
-#    properties: properties
-#    property_variants: property_variants
-#    choose_property_variant: choose_property_variant
     get_state: -> return state;
   }


new line added

new line added

--- app/assets/javascripts/main.js.coffee
+++ app/assets/javascripts/main.js.coffee
@@ -219,6 +219,7 @@
       $(".discussion-additional-info").toggle()
       event.preventDefault()
     )
+
 Application.seeMoreDescription = () ->
   #expand/shrink description text
   if $("body.discussions.show").length > 0


improve alignment

improve alignment

--- app/assets/javascripts/zvg/zvg.coffee
+++ app/assets/javascripts/zvg/zvg.coffee
@@ -411,7 +411,7 @@
     color = (d) -> c(d.key)

     selection.attr('transform', (_, i) -> "translate(0, #{i * h})") # FIXME
-      .append('rect').attr('width', @legend_width).attr('height', h).style('fill', 'white').style('stroke', 'blue') # TEMP, but should include a background rect for hover
+      .append('rect').attr('width', @legend_width).attr('height', h).style('fill', 'white').style('stroke', 'none')
     selection.append('g')
       .attr('class', 'legend-icon')
       .attr("transform", "translate(10, #{h/2})")
@@ -465,13 +465,14 @@
         @_checked[d] = false
       else
         @_checked[d] = true
-      #@filter_data(@container.selectAll('input:checked').data())
       @filter_data((key for key, condition of @_checked when condition))
       @render()
     )

     items.append('text').attr('class', 'legend_text')
       .text((d) -> d)
+      .style('alignment-baseline', 'middle')
+      .attr('transform', "translate(12, 5)")

   #initialize_legend: ->
@@ -486,7 +487,7 @@
       .append('rect')
       .attr('width', @legend_width)
       .attr('height', @height)
-      .style('stroke', 'green')
+      .style('stroke', 'none')
       .style('fill', 'none')
     #


pass the correct arg to onChange

pass the correct arg to onChange

--- app/classifier/tasks/text/editor.cjsx
+++ app/classifier/tasks/text/editor.cjsx
@@ -26,6 +26,7 @@
     else if !checked
       removalIndex = text_tags.indexOf(value)
       text_tags.splice(removalIndex,1);
+    @props.task.text_tags = text_tags
     @props.onChange @props.task


Use order attr to order US statuses in Kanban page

--- app/coffee/modules/kanban/main.coffee
+++ app/coffee/modules/kanban/main.coffee
@@ -136,7 +136,7 @@
             @scope.points = _.sortBy(project.points, "order")
             @scope.pointsById = groupBy(project.points, (x) -> x.id)
             @scope.usStatusById = groupBy(project.us_statuses, (x) -> x.id)
-            @scope.usStatusList = _.sortBy(project.us_statuses, "id")
+            @scope.usStatusList = _.sortBy(project.us_statuses, "order")
             return project

     loadInitialData: ->


Package name is changed.

Package name is changed.

--- tests.coffee
+++ tests.coffee
@@ -5,4 +5,4 @@
     isDefined = true

   test.isTrue isDefined, "PackageBuilder is not defined"
-  test.isTrue Package['meteor-package-builder'].PackageBuilder, "Package.meteor-package-builder.PackageBuilder is not defined"
+  test.isTrue Package['peerlibrary:package-builder'].PackageBuilder, "Package.peerlibrary:package-builder.PackageBuilder is not defined"


fix(thread-list): Unread gem hover state should override "star"

Fixes T3216

--- internal_packages/thread-list/lib/thread-list-icon.cjsx
+++ internal_packages/thread-list/lib/thread-list-icon.cjsx
@@ -19,7 +19,7 @@
       return 'thread-icon-star'

     if @props.thread.unread
-      return 'thread-icon-unread thread-icon-star-on-hover'
+      return 'thread-icon-unread'

     msgs = @_nonDraftMessages()
     last = msgs[msgs.length - 1]
@@ -27,9 +27,9 @@
     myEmail = NamespaceStore.current()?.emailAddress
     if msgs.length > 1 and last.from[0]?.email is myEmail
       if Utils.isForwardedMessage(last)
-        return 'thread-icon-forwarded thread-icon-star-on-hover'
+        return 'thread-icon-forwarded'
       else
-        return 'thread-icon-replied thread-icon-star-on-hover'
+        return 'thread-icon-replied'

     return 'thread-icon-star-on-hover'


increase page_size in workflows request to API

--- app/lib/get-workflows-in-order.coffee
+++ app/lib/get-workflows-in-order.coffee
@@ -1,5 +1,7 @@
-getWorkflowsInOrder = (project, query) ->
+getWorkflowsInOrder = (project, query = {}) ->
   order = project.configuration?.workflow_order ? []
+  unless query?.page_size?
+    query['page_size'] = 50

   project.get('workflows', query).then (workflows) ->
     workflowsByID = {}


Increase parallax

Increase parallax

--- app/scripts/giraffe-background.coffee
+++ app/scripts/giraffe-background.coffee
@@ -21,8 +21,8 @@

 moveGiraffe = (percentX, percentY) ->
   dialog = $('.giraffe-background_dialog')
-  dialog.css('right', "#{150 + percentX * 120}px")
-  dialog.css('top', "#{250 + -percentY * 40}px")
+  dialog.css('right', "#{150 + percentX * 140}px")
+  dialog.css('top', "#{250 + -percentY * 70}px")

   giraffe = $('.giraffe-background_giraffe')
   giraffe.css('right', "#{percentX * 50}px")


extracting point

extracting point


Added gutil to rsync

Added gutil to rsync

--- app/templates/gulp/tasks/rsync.coffee
+++ app/templates/gulp/tasks/rsync.coffee
@@ -1,6 +1,7 @@
 gulp         = require "gulp"
 extend       = require "extend"
-rsync        = require("rsyncwrapper").rsync
+rsync        = require "rsyncwrapper"
+gutil        = require "gutil"

 conf = require '../gulpconfig'
 secret = require '../secrets'


Breadcrumb decript file path.

Breadcrumb decript file path.

--- src/FileManager/BreadcrumbDirective.coffee
+++ src/FileManager/BreadcrumbDirective.coffee
@@ -1,34 +1,33 @@
 angular.module('fileManager')
-.directive('breadcrumb', ($stateParams)->
+.directive('breadcrumb', ($stateParams, File, session)->
   return {
     restrict: 'E'
     template: """

-
{{ piece.value }}

+
{{ piece.name }}

               """

     link: (scope, element, attrs) ->
+      scope.breadcrumb = []
       scope.$watch($stateParams, ->
-        path = "/#{$stateParams.path}"
-        path = path.split('/')
-
-        result = []
-        link   = ''
-        for piece in path
-          if piece isnt ''
-            link += '/' + piece
-            add=
-              path: link.replace('#', '%23')
-              value: piece
-            result.push(add)
-
-        scope.breadcrumb = result
+        scope.breadcrumb = []
+        getPath($stateParams.path)
       )

+      getPath = (id)=>
+        File.getFile(id).then (file)=>
+          scope.breadcrumb.unshift({
+            _id:  file._id
+            name: file.metadata.name
+          })
+
+          parent_id = file.metadata.parentId
+          if parent_id? and parent_id != session.getRootFolder()
+            getPath(parent_id)
   }
 )


* minor comments changes

* minor comments changes

--- src/index.coffee
+++ src/index.coffee
@@ -28,7 +28,7 @@
     if aFile.path? and aFile.dest?
       if isFunction(aFile.getContentSync)
         if !aFile.hasOwnProperty('_contents') && aFile.parent && !aFile.parent.isDirectory()
-          #  the inherited file resource object use the parent's contents if parent is a file(not folder) and  no contents on itself.
+          # the inherited file resource object use the parent's contents if parent is a file(not folder) and  no contents on itself.
           vFile = aFile.parent
         else
           vFile = aFile


Fixed require() call

Fixed require() call

--- src/index.coffee
+++ src/index.coffee
@@ -1,4 +1,4 @@
-TimeManager = require 'time-manager'
+TimeManager = require './time-manager'

 timeManager = new TimeManager


Remove duplicate `FilterNotFound` from error array

--- src/index.coffee
+++ src/index.coffee
@@ -17,8 +17,8 @@

 # Errors
 [ "ArgumentError", "ContextError", "FilterNotFound",
-  "FilterNotFound", "FileSystemError", "StandardError",
-  "StackLevelError", "SyntaxError"
+  "FileSystemError", "StandardError", "StackLevelError",
+  "SyntaxError"
 ].forEach (className) ->

   Liquid[className] = customError("Liquid.#{className}", Liquid.Error)


Handle missing options

Handle missing options

--- src/index.coffee
+++ src/index.coffee
@@ -28,7 +28,7 @@

 # Apply select keys from a configuration object to a browserify bundle.
 applyConfig = (b, cfg) ->
-  (b[c] v for v in [].concat cfg[c] if cfg[c]? and b[c]?) for c in configs
+  (b[c] v for v in [].concat cfg[c] if cfg?[c]? and b?[c]?) for c in configs

 # Write the dependency bundle out to the temporary file.
 writeDeps = (callback) ->


stringifying in better ways

stringifying in better ways

--- src/index.coffee
+++ src/index.coffee
@@ -1,5 +1,11 @@
 _deep_equal = require "deep-equal"
 _empty = require "lodash.isempty"
+
+str = (v) ->
+  try
+    return JSON.stringify v
+  catch e
+    return String v

 exist = {}
 empty = {}


Fix to work with ubret dashboard loading

--- src/index.coffee
+++ src/index.coffee
@@ -33,7 +33,7 @@
     deps: ["BaseTool"]
   "Mapper" :
     source: "ubret/map.js"
-    deps: ["BaseTool", "L"]
+    deps: ["BaseTool", "Leaflet"]
   "Statistics" :
     source: "ubret/statistics.js"
     deps: ["BaseTool"]
@@ -44,7 +44,7 @@
     source: "ubret/spectra.js"
     deps: ["BaseTool"]

-Ubret.Loader = (tools, cb=null) ->
+Ubret.Loader = (tools, cb) ->
   isScriptLoaded = (script) ->
     not (typeof window[script] is 'undefined' or typeof Ubret[script] is 'undefined')

@@ -65,17 +65,17 @@

   findDeps = (deps, accum) ->
     dependencies = []
-    dependencies.push Ubret.Dependencies[dep].deps for dep in deps
+    dependencies.push Ubret.Dependencies[dep].deps for dep in deps when Ubret.Dependencies
     dependencies = unique dependencies
     if dependencies.length is 0
-      return accum
+      return unique accum
     else
       return findDeps(dependencies, accum.concat(dependencies))

   loadScripts = ->
-    if tools is 0
+    if tools.length is 0
       return
-    callback = if tools.length is 1 and cb isnt null then cb else loadScripts
+    callback = if tools.length is 1 then cb else loadScripts
     tool = tools.pop()
     unless (isScriptLoaded tool) or (isScriptLoaded Ubret.Dependencies[tool].symbol)
       source = Ubret.Dependencies[tool].source


Grid row does not longer disappear, resolves #149

--- wzk/ui/grid/RowBuilder.coffee
+++ wzk/ui/grid/RowBuilder.coffee
@@ -68,6 +68,7 @@
   buildCell: (model, col, row) ->
     cell = row.addCell @formatter.format(model, col)
     cell.addClass col
+    cell

   ###*
     @protected


fix test-install command with new access

--- commands.coffee
+++ commands.coffee
@@ -29,7 +29,7 @@

         client = new Client "http://localhost:9101/"
         client.setBasicAuth 'home', 'token'
-        client.post "data/", data, (err, res, body) ->
+        client.post "access/", data, (err, res, body) ->
             if err
                 console.log "Cannot create app"
                 console.log err.stack or err


Added 'cancelled' trigger to FormView.

Added 'cancelled' trigger to FormView.

--- lib/ST/FormView.coffee
+++ lib/ST/FormView.coffee
@@ -90,4 +90,5 @@
       self.save()
       dialog.close()
     buttonbar.button 'Cancel', ->
+      self.trigger 'cancelled'
       dialog.close()


add test for join arg order

add test for join arg order

--- test/main.coffee
+++ test/main.coffee
@@ -91,8 +91,8 @@
         a.should.eql [1,2,3]
         b.should.eql [4,5,6]
         done()
+      p.resolve 4,5,6
       p2.resolve 1,2,3
-      p.resolve 4,5,6

     it 'should complete with error', (done) ->
       p = swear()


Removed _callbackHandler() in favour of builtin closure wrapper (for ... do () =>). Coding style related changes.

--- unipi-evok.coffee
+++ unipi-evok.coffee
@@ -43,15 +43,17 @@
       # register devices
       deviceConfigDef = require("./device-config-schema")
       for key, device of deviceConfigTemplates
-        className = device.class
-        # convert camel-case classname to kebap-case filename
-        filename = className.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
-        classType = require('./devices/' + filename)(env)
-        @_base.debug "Registering device class #{className}"
-        @framework.deviceManager.registerDeviceClass(className, {
-          configDef: deviceConfigDef[className],
-          createCallback: @_callbackHandler(className, classType)
-        })
+        do (key, device) =>
+          className = device.class
+          # convert camel-case classname to kebap-case filename
+          filename = className.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
+          classType = require('./devices/' + filename)(env)
+          @_base.debug "Registering device class #{className}"
+          @framework.deviceManager.registerDeviceClass(className, {
+            configDef: deviceConfigDef[className],
+            createCallback: (config, lastState) =>
+              return new classType(config, @, lastState)
+          })

       # auto-discovery
       @framework.deviceManager.on('discover', (eventData) =>
@@ -64,11 +66,13 @@
               device.id += obj.circuit
               device.name += obj.circuit
               device.circuit = obj.circuit
-              matched = @framework.deviceManager.devicesConfig.some (element, iterator) =>
+              matched = @framework.deviceManager.devicesConfig.some (element, iterator) ->
                 element.class is device.class and element.circuit is device.circuit

               if not matched
-                process.nextTick @_discoveryCallbackHandler('pimatic-unipi-evok', device.name, device)
+                process.nextTick(
+                  @_discoveryCallbackHandler('pimatic-unipi-evok', device.name, device)
+                )
         )
       )

@@ -76,10 +80,6 @@
       return () =>
         @framework.deviceManager.discoveredDevice pluginName, deviceName, deviceConfig

-    _callbackHandler: (className, classType) ->
-      # this closure is required to keep the className and classType context as part of the iteration
-      return (config, lastState) =>
-        return new classType(config, @, lastState)

   # ###Finally
   # Create a instance of plugin


Reindent if new jsx text applied. Fixes #150

--- lib/auto-indent.coffee
+++ lib/auto-indent.coffee
@@ -57,6 +57,7 @@
     document.addEventListener 'mouseup', => @mouseUp = true

     @disposables.add @editor.onDidChangeCursorPosition (event) => @changedCursorPosition(event)
+    @disposables.add @editor.onDidStopChanging () => @didStopChanging()

     @atomTabLength = @editor.getTabLength()

@@ -106,6 +107,19 @@
     startPointOfJsx =  autoCompleteJSX.getStartOfJSX @editor, cursorPosition
     @editor.transact 300, =>
       @indentJSX new Range(startPointOfJsx, endPointOfJsx)
+
+  # Buffer has stopped changing. Indent as required
+  didStopChanging: () ->
+    selectedRange = @editor.getSelectedBufferRange()
+    highestRow = Math.max selectedRange.start.row, selectedRange.end.row
+    if highestRow isnt @highestSelectedRow
+      @highestSelectedRow = highestRow
+      scope = @editor.scopeDescriptorForBufferPosition(highestRow,0).getScopesArray()
+      if 'meta.tag.jsx' in scope
+        endPointOfJsx = new Point highestRow,0
+        startPointOfJsx =  autoCompleteJSX.getStartOfJSX @editor, endPointOfJsx
+        @editor.transact 300, =>
+          @indentJSX new Range(startPointOfJsx, endPointOfJsx)

   # is the jsx on this line in scope
   jsxInScope: (bufferRow) ->


Fix JSX closing bracket rule for 'props-aligned'

--- lib/auto-indent.coffee
+++ lib/auto-indent.coffee
@@ -726,9 +726,9 @@
           @indentRow({row: row,  blockIndent: parentTag.tagIndentation})
       else if closingBracketRule is PROPSALIGNED
         if @eslintIndentOptions.jsxIndentProps[0]
-          @indentRow({row: row,  blockIndent: parentTag.firstTagInLineIndentation,jsxIndentProps: 1})
+          @indentRow({row: row,  blockIndent: parentTag.firstCharIndentation,jsxIndentProps: 1})
         else
-          @indentRow({row: row,  blockIndent: parentTag.firstTagInLineIndentation})
+          @indentRow({row: row,  blockIndent: parentTag.firstCharIndentation})

   # indent a row by the addition of one or more indents.
   # returns false if no indent required as it is already correct


61st Commit

61st Commit

--- protractor.conf.coffee
+++ protractor.conf.coffee
@@ -8,7 +8,7 @@
   @onPrepare: ->
     SpecReporter = require 'jasmine-spec-reporter'
     jasmine.getEnv().addReporter new SpecReporter displayStacktrace: true
-    browser.ignoreSynchronization = true
+    browser.ignoreSynchronization = false
   @framework: 'jasmine2'
   @jasmineNodeOpts:
     isVerbose: false


kanjo update

kanjo update

--- scripts/kanjo.coffee
+++ scripts/kanjo.coffee
@@ -11,7 +11,7 @@
 #   hubot 聞いてよ - get 美女 ネタ

 module.exports = (robot) ->
-  robot.respond /聞いてよ (.*)/i, (msg) ->
+  robot.respond /聞いてよ\s(.*)/i, (msg) ->
     query = msg.match[1]
     robot.http("http://ap.mextractr.net/ma9/emotion_analyzer")
       .query({
@@ -33,7 +33,7 @@
             })
             .get() (err, res, bjinbody) ->
               jbjinbody = JSON.parse(bjinbody)
-              msg.send jbjinbody[0].thumb
+              msg.send "http://bjin.me/images/pic" + jbjinbody[0].ID + ".jpg"
               msg.send "ぅωぅω"
         else
          robot.http("http://bjin.me/api/")
@@ -44,5 +44,5 @@
           })
           .get() (err, res, bbjinbody) ->
                   jbbjinbody = JSON.parse(bbjinbody)
-                  msg.send jbbjinbody[0].thumb
-                  msg.send "け〃ωきた〃Ｕτ"+                  msg.send "http://bjin.me/images/pic" + jbjinbody[0].ID + ".jpg"
+                  msg.send "け〃ωきた〃Ｕτ"


Describe collections

Describe collections

--- lib/collections.coffee
+++ lib/collections.coffee
@@ -1 +1,18 @@
-@Campaigns = new Meteor.Collection 'campaigns'+### Campaings
+  @param name
+  @param createdAt
+  @param description
+  @param mainMedia
+    - type
+    - url
+###
+@Campaigns = new Meteor.Collection 'campaigns'
+
+
+### Users
+  @param email
+  @param password
+  @param name
+  @param type [organization, person]
+###
+@Users = Meteor.users


Re-add initModalManager call

Re-add initModalManager call

--- src/desktop/lib/global_client_setup.coffee
+++ src/desktop/lib/global_client_setup.coffee
@@ -31,6 +31,7 @@
   listenForInvert()
   listenForBounce()
   confirmation.check()
+  initModalManager()
   mountStitchBlocks()


replace roles with default script

replace roles with default script

--- scripts/roles.coffee
+++ scripts/roles.coffee
@@ -4,8 +4,7 @@
 # Commands:
 #   hubot <user> is a badass guitarist - assign a role to a user
 #   hubot <user> is not a badass guitarist - remove a role from a user
-#   hubot all about <user> - see what roles a user has
-#   hubot who is <user> - see a random selection of two roles a user has
+#   hubot who is <user> - see what roles a user has
 #
 # Examples:
 #   hubot holman is an ego surfer
@@ -13,10 +12,14 @@

 module.exports = (robot) ->

+  if process.env.HUBOT_AUTH_ADMIN?
+    robot.logger.warning 'The HUBOT_AUTH_ADMIN environment variable is set not going to load roles.coffee, you should delete it'
+    return
+
   getAmbiguousUserText = (users) ->
     "Be more specific, I know #{users.length} people named like that: #{(user.name for user in users).join(", ")}"

-  robot.respond /all about @?([\w .\-]+)\?*$/i, (msg) ->
+  robot.respond /who is @?([\w .\-]+)\?*$/i, (msg) ->
     joiner = ', '
     name = msg.match[1].trim()

@@ -34,52 +37,11 @@
             joiner = '; '
           msg.send "#{name} is #{user.roles.join(joiner)}."
         else
-          msg.send "#{name} is dead to me."
+          msg.send "#{name} is nothing to me."
       else if users.length > 1
         msg.send getAmbiguousUserText users
       else
         msg.send "#{name}? Never heard of 'em"
-
-  robot.respond /who is @?([\w .\-]+)\?*$/i, (msg) ->
-    joiner = ' '
-    name = msg.match[1].trim()
-
-    users = robot.brain.usersForFuzzyName(name)
-    if users.length is 1
-      user = users[0]
-      user.roles = user.roles or [ ]
-      if user.roles.length > 0
-
-        if user.roles.length > 6
-          length = user.roles.length
-          first_index = Math.floor(Math.random() * length) % length
-          second_index = Math.floor(Math.random() * length) % length
-          third_index = Math.floor(Math.random() * length) % length
-          while ( first_index == second_index )
-            second_index = Math.floor(Math.random() * length) % length
-          while ( first_index == third_index || third_index == second_index && true )
-            third_index = Math.floor(Math.random() * length) % length
-          roles = [user.roles[first_index], user.roles[second_index], user.roles[third_index]]
-
-        else if user.roles.length > 2
-          length = user.roles.length
-          first_index = Math.floor(Math.random() * length) % length
-          second_index = Math.floor(Math.random() * length) % length
-          while ( first_index == second_index )
-            second_index = Math.floor(Math.random() * length) % length
-
-          roles = [user.roles[first_index], user.roles[second_index]]
-
-        else
-          roles = user.roles
-
-        msg.send "#{name} is #{roles.join(joiner)}."
-      else
-        msg.send "#{name} is dead to me."
-    else if users.length > 1
-      msg.send getAmbiguousUserText users
-    else
-      msg.send "#{name}? Never heard of 'em"

   robot.respond /@?([\w .\-_]+) is (["'\w: \-_]+)[.!]*$/i, (msg) ->
     name    = msg.match[1].trim()


tweak slug script

tweak slug script

--- scripts/slugs.coffee
+++ scripts/slugs.coffee
@@ -17,10 +17,10 @@

     if team.peopleIds.length is 0
       console.log "EMPTY TEAM - nodeknockout.com/teams/#{team.slug} (#{team.name})"
-      return next(null, team)
+      return next()

     # don't change any team that's already being setup
-    return next(null, team) if team.setup?.status
+    return next() if team.setup?.status

     changes =
       "-1": "theteam"
@@ -96,6 +96,8 @@
       "its-always-been-brok": "always-broken"
       "figure-it-out-guys": "makeshift"
       "hooky": "toptal"
+      "frost-tw": "isdmd"
+      "sporky-dorky": "sporkydorky"

     if team.name == "ヽ( ´¬`)ノ" #special that there was no slug previously
       old = team.slug


rename

rename

--- test/test.coffee
+++ test/test.coffee
@@ -1,4 +1,4 @@
-describe 'muskout', ->
+describe 'it', ->

 	init = ->
 		ko.cleanNode(document.body)


Added JSON Lib tests and used equal over eql

--- test/test.coffee
+++ test/test.coffee
@@ -21,7 +21,7 @@
 describe 'Whip', ->
   describe 'parser', ->
     it 'should lex a single atom', ->
-      'a'.should.eql w.parse('a').value
+      'a'.should.equal w.parse('a').value

     it 'should lex an atom in a list', ->
       [].should.eql unan w.parse '()'
@@ -58,29 +58,29 @@

     describe 'atoms', ->
       it 'should return string atom', ->
-         ip('"a"').should.eql 'a'
+         ip('"a"').should.equal 'a'

       it 'should return string with space atom', ->
-          ip('"a b"').should.eql 'a b'
+          ip('"a b"').should.equal 'a b'

       it 'should return string with opening paren', ->
-         ip('"(a"').should.eql '(a'
+         ip('"(a"').should.equal '(a'

       it 'should return string with closing paren', ->
-         ip('")a"').should.eql ')a'
+         ip('")a"').should.equal ')a'

       it 'should return string with parens', ->
-         ip('"(a)"').should.eql '(a)'
+         ip('"(a)"').should.equal '(a)'

       it 'should return number atom', ->
-         ip('123').should.eql 123
+         ip('123').should.equal 123

     describe 'invocation', ->
       it 'should run print on an int', ->
-         ip('(print 1)').should.eql 1
+         ip('(print 1)').should.equal 1

       it 'should return first element of list', ->
-         ip('(head (1 2 3))').should.eql 1
+         ip('(head (1 2 3))').should.equal 1

       it 'should return rest of list', ->
          ip('(tail (1 2 3))').should.eql [2, 3]
@@ -92,7 +92,7 @@
          ip('((lambda (tail (1 2))))').should.eql [2]

       it 'should return correct result for lambda that takes and returns arg', ->
-         ip('((lambda (x) x) 1)').should.eql 1
+         ip('((lambda (x) x) 1)').should.equal 1

       it 'should return correct result for lambda that returns list of vars', ->
          ip('((lambda (x y) (x y)) 1 2)').should.eql [1, 2]
@@ -101,7 +101,7 @@
          ip('((lambda (x y) (0 x y)) 1 2)').should.eql [0, 1, 2]

       it 'should return correct result when invoke lambda w params', ->
-         ip('((lambda (x) (head (x))) 1)').should.eql 1
+         ip('((lambda (x) (head (x))) 1)').should.equal 1

     describe 'let', ->
       it 'should eval inner expression w names bound', ->
@@ -113,12 +113,12 @@
          ip('(let ((x 1) (y x)) (x y))').should.eql [1, undefined]

       it 'should accept empty binding list', ->
-         ip('(let () 42)').should.eql 42
+         ip('(let () 42)').should.equal 42

     describe 'if', ->
       it 'should choose the right branch', ->
-         ip('(if true 42 4711)').should.eql 42
-         ip('(if false 42 4711)').should.eql 4711
+         ip('(if true 42 4711)').should.equal 42
+         ip('(if false 42 4711)').should.equal 4711

     describe 'dictionaries', ->
       it 'should generate dictionary with keys and values', ->
@@ -127,19 +127,19 @@
 describe 'stdlib', ->
   describe 'arithmetic', ->
     it 'should add two float lits', ->
-       ip('(+ 2.5 2.5)').should.eql 5
+       ip('(+ 2.5 2.5)').should.equal 5

     it 'should subtract two number lits', ->
-       ip('(- 10 5)').should.eql 5
+       ip('(- 10 5)').should.equal 5

     it 'should multiply two number lits', ->
-       ip('(* 5 5)').should.eql 25
+       ip('(* 5 5)').should.equal 25

     it 'should divide two number lits', ->
-       ip('(/ 25 5)').should.eql 5
+       ip('(/ 25 5)').should.equal 5

     it 'should modulo two number lits', ->
-       ip('(% 19 7)').should.eql 5
+       ip('(% 19 7)').should.equal 5

     it 'should compare greatness of two number lits', ->
       ip('(> 5 1)').should.be.true
@@ -147,10 +147,10 @@

     describe 'type coercion', ->
       it 'should concatenate two strings', ->
-         ip('(+ "foo " "bar")').should.eql 'foo bar'
+         ip('(+ "foo " "bar")').should.equal 'foo bar'

       it 'should generate javascript-ruled string', ->
-         ip('(+ 5 "5")').should.eql '55'
+         ip('(+ 5 "5")').should.equal '55'

   describe 'boolean expressions', ->
     it 'should evaluate not false to true', ->
@@ -177,20 +177,20 @@

   describe 'indexing', ->
     it 'should find value of list from index', ->
-      ip('(@ 1 (1 2 3))').should.eql 2
+      ip('(@ 1 (1 2 3))').should.equal 2

     it 'should find attribute of dict from index', ->
-      ip('(@ "key1" {"key0":"foo" "key1":42 key2:false})').should.eql 42
+      ip('(@ "key1" {"key0":"foo" "key1":42 key2:false})').should.equal 42

     it 'should find letter of string from index', ->
-      ip('(@ 3 "foobar")').should.eql 'b'
+      ip('(@ 3 "foobar")').should.equal 'b'

     describe 'lengths', ->
       it 'should get length of string', ->
-        ip('(_ "foobar")').should.eql 6
+        ip('(_ "foobar")').should.equal 6

       it 'should get length of list', ->
-        ip('(_ (1 2 3))').should.eql 3
+        ip('(_ (1 2 3))').should.equal 3

       it 'should not any get length of dict', ->
         should.not.exist ip('(_ {0:0 1:1})')
@@ -200,3 +200,11 @@
         ip('(\\ (.. 1 100) -5)').should.eql [96, 97, 98, 99, 100]

   # TODO: Haskell functions, map, and reduce functions
+
+describe 'JSON lib', ->
+  describe 'harvest function', ->
+    it 'should parse JSON strings and numbers', ->
+      ip('(harvest "{\\"key\\":\\"value\\", \\"key2\\":42}")')
+        .should.eql key: 'value', key2: 42
+    it 'should parse JSON containing null', ->
+      should.not.exist ip('(harvest "{\\"foo\\": null}")').foo


Parentheses removed from style.coffee

Parentheses removed from style.coffee

--- gulp/tasks/style.coffee
+++ gulp/tasks/style.coffee
@@ -7,7 +7,7 @@

 gulp.task 'style', false, ->
   gulp.src config.style
-  .pipe $.plumber(errorHandler: util.onError)
+  .pipe $.plumber errorHandler: util.onError
   .pipe do $.less
   .pipe $.autoprefixer {cascade: false}
   .pipe do $.minifyCss
@@ -17,7 +17,7 @@

 gulp.task 'style:dev', false, ->
   gulp.src config.style
-  .pipe $.plumber(errorHandler: util.onError)
+  .pipe $.plumber errorHandler: util.onError
   .pipe do $.sourcemaps.init
   .pipe do $.less
   .pipe $.autoprefixer {map: true}


fix(attachments): Add warning for ENOSPC as well

--- src/flux/stores/file-download-store.coffee
+++ src/flux/stores/file-download-store.coffee
@@ -298,11 +298,17 @@
       buttons: ["OK"]

   _catchFSErrors: (error) ->
+    message = null
     if error.code in ['EPERM', 'EMFILE', 'EACCES']
+      message = "N1 could not save an attachment. Check that permissions are set correctly and try restarting N1 if the issue persists."
+    if error.code in ['ENOSPC']
+      message = "N1 could not save an attachment because you have run out of disk space."
+
+    if message
       remote.dialog.showMessageBox
         type: 'warning'
         message: "Download Failed"
-        detail: "N1 could not save an attachment. Check that permissions are set correctly and that you have enough disk space.\n\n#{error.message}"
+        detail: "#{message}\n\n#{error.message}"
         buttons: ["OK"]
       return Promise.resolve()
     else


Extract method: #openUrlInBrowser

Extract method: #openUrlInBrowser

--- lib/github-file.coffee
+++ lib/github-file.coffee
@@ -13,7 +13,10 @@
       console.warn error for error in @validationErrors()
       return

-    child_process.exec "open #{@blobUrl()}", (error, stdout, stderr) ->
+    @openUrlInBrowser(@blobUrl())
+
+  openUrlInBrowser: (url) ->
+    child_process.exec "open #{url}", (error, stdout, stderr) ->
       throw error if error?

   blobUrl: ->


:bug: Fix a critical typo in LinterPlus::constructor

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -19,7 +19,7 @@

     @_subscriptions.add atom.config.observe 'linter.showErrorInline', (showBubble) =>
       @views.showBubble = showBubble
-    @_subscriptions.add atom.config.observe 'linter-plus.lintOnFly', (value) =>
+    @_subscriptions.add atom.config.observe 'linter.lintOnFly', (value) =>
       @lintOnFly = value
     @_subscriptions.add atom.workspace.onDidChangeActivePaneItem (editor) =>
       @activeEditor = editor


:art: Use grim to indicate deprecated APIs

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -5,6 +5,7 @@
 Helpers = require './helpers'
 Commands = require './commands'
 Messages = require './messages'
+{deprecate} = require 'grim'

 class Linter
   constructor:(@state)  ->
@@ -79,19 +80,19 @@
     return @messages.onDidClassify(callback)

   onDidChangeProjectMessages: (callback) ->
-    console.warn("Linter::onDidChangeProjectMessages is deprecated, use Linter::onDidChangeMessages instead")
+    deprecate("Linter::onDidChangeProjectMessages is deprecated, use Linter::onDidChangeMessages instead")
     return @onDidChangeMessages(callback)

   getProjectMessages: ->
-    console.warn("Linter::getProjectMessages is deprecated, use Linter::getMessages instead")
+    deprecate("Linter::getProjectMessages is deprecated, use Linter::getMessages instead")
     return @getMessages()

   setProjectMessages: (linter, messages) ->
-    console.warn("Linter::setProjectMessages is deprecated, use Linter::setMessages instead")
+    deprecate("Linter::setProjectMessages is deprecated, use Linter::setMessages instead")
     return @setMessages(linter, messages)

   deleteProjectMessages: (linter) ->
-    console.warn("Linter::deleteProjectMessages is deprecated, use Linter::deleteMessages instead")
+    deprecate("Linter::deleteProjectMessages is deprecated, use Linter::deleteMessages instead")
     return @setMessages(linter, messages)

   getActiveEditorLinter: ->


added support for async `Then` with a done callback

--- lib/mocha-given.coffee
+++ lib/mocha-given.coffee
@@ -58,13 +58,22 @@
 	label = o(specArgs).firstThat (arg) -> o(arg).isString()
 	fn    = o(specArgs).firstThat (arg) -> o(arg).isFunction()

-	itFunc "then #{label ? stringifyExpectation(fn)}", ->
-		try
-			expect(fn.call(@)).to.be.ok()
-		catch exception
-			msg = exception.message
-			msg += getErrorDetails fn, @
-			throw new Error msg
+	if !fn.toString().replace(/\n/g,'').match(/^function\s?\(\)/i)
+		itFunc "then #{label ? stringifyExpectation(fn)}", (done) ->
+			try
+				expect(fn.apply(@, Array.prototype.slice.call(arguments))).to.be.ok()
+			catch exception
+				msg = exception.message
+				msg += getErrorDetails fn, @
+				throw new Error msg
+	else
+		itFunc "then #{label ? stringifyExpectation(fn)}", ->
+			try
+				expect(fn.call(@)).to.be.ok()
+			catch exception
+				msg = exception.message
+				msg += getErrorDetails fn, @
+				throw new Error msg

 MochaGivenSuite = (suite) ->
 	suites = [suite]


:+1: works on other OS with #17

:+1: works on other OS with #17

--- lib/preferences.coffee
+++ lib/preferences.coffee
@@ -72,6 +72,7 @@
     return unless menu
     for d in @defS.Settings.menu
       el = menu.querySelector("[name='#{d.label}']>a")
+      continue unless el
       PU.applyTextWithOrg el, d.value

     # Left-side button


command line flag to run kucalc version of hub

--- src/smc-hub/hub.coffee
+++ src/smc-hub/hub.coffee
@@ -419,26 +419,33 @@
 compute_server = undefined
 init_compute_server = (cb) ->
     winston.debug("init_compute_server: creating compute_server client")
-    require('./compute-client.coffee').compute_server
-        database : database
-        dev      : program.dev
-        single   : program.single
-        base_url : BASE_URL
-        cb       : (err, x) ->
-            if not err
-                winston.debug("compute server created")
-            else
-                winston.debug("FATAL ERROR creating compute server -- #{err}")
-            compute_server = x
-            database.compute_server = compute_server
-            # This is used by the database when handling certain writes to make sure
-            # that the there is a connection to the corresponding project, so that
-            # the project can respond.
-            database.ensure_connection_to_project = (project_id) ->
-                local_hub_connection.connect_to_project(project_id, database, compute_server)
-
+    f = (err, x) ->
+        if not err
+            winston.debug("compute server created")
+        else
+            winston.debug("FATAL ERROR creating compute server -- #{err}")
             cb?(err)
-
+            return
+        compute_server = x
+        database.compute_server = compute_server
+        # This is used by the database when handling certain writes to make sure
+        # that the there is a connection to the corresponding project, so that
+        # the project can respond.
+        database.ensure_connection_to_project = (project_id) ->
+            local_hub_connection.connect_to_project(project_id, database, compute_server)
+        cb?()
+
+    if program.kucalc
+        require('./kucalc/compute-client').compute_server
+            database : database
+            cb       : f
+    else
+        require('./compute-client').compute_server
+            database : database
+            dev      : program.dev
+            single   : program.single
+            base_url : BASE_URL
+            cb       : f

 update_primus = (cb) ->
     misc_node.execute_code
@@ -688,6 +695,7 @@
         .option('--base_url [string]', 'Base url, so https://sitenamebase_url/', String, '')  # '' or string that starts with /
         .option('--local', 'If option is specified, then *all* projects run locally as the same user as the server and store state in .sagemathcloud-local instead of .sagemathcloud; also do not kill all processes on project restart -- for development use (default: false, since not given)', Boolean, false)
         .option('--foreground', 'If specified, do not run as a deamon')
+        .option('--kucalc', 'if given, assume running in the KuCalc kubernetes environment')
         .option('--dev', 'if given, then run in VERY UNSAFE single-user local dev mode')
         .option('--single', 'if given, then run in LESS SAFE single-machine mode')
         .option('--db_pool <n>', 'number of db connections in pool (default: 1)', ((n)->parseInt(n)), 1)


Fixes #117

Fixes #117

--- src/store/calls.coffee
+++ src/store/calls.coffee
@@ -2,18 +2,17 @@
 store = require('./index')
 argsMatch = require('./../args-match')

-lastCall = null #<-- remember this to pop our DSL of when(<call>)/verify(<call>)
-store.onReset -> lastCall = null
+callHistory = [] #<-- remember this to pop our DSL of when(<call>)/verify(<call>)
+store.onReset -> callHistory = []

 module.exports =

   log: (testDouble, args, context) ->
     store.for(testDouble).calls.push({args, context})
-    lastCall = {testDouble, args, context}
+    callHistory.push({testDouble, args, context})

   pop: ->
-    _.tap (call = lastCall), (call) ->
-      lastCall = null #<-- no double-dipping since it's global & destructive
+    _.tap (callHistory.pop()), (call) ->
       store.for(call.testDouble).calls.pop() if call?

   wasInvoked: (testDouble, args, config) ->


Document TextBuffer events

Document TextBuffer events

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -15,6 +15,55 @@

 # Public: A mutable text container with undo/redo support and the ability to
 # annotate logical regions in the text.
+#
+# ## Events
+#
+# * `changed` -
+#      Emitted synchronously whenever the buffer changes. Binding a slow handler
+#      to this event has the potential to destroy typing performance. Consider
+#      using `contents-modified` instead and aim for extremely fast performance
+#      (< 2 ms) if you must bind to it. Your handler will be called with an
+#      object containing the following keys.
+#      * `oldRange` - The {Range} of the old text
+#      * `newRange` - The {Range} of the new text
+#      * `oldText` - A {String} containing the text that was replaced
+#      * `newText` - A {String} containing the text that was inserted
+#
+# * `markers-updated` -
+#      Emitted synchronously when the `changed` events of all markers have been
+#      fired for a change. The order of events is as follows:
+#      * The text of the buffer is changed
+#      * All markers are updated accordingly, but their `changed` events are not
+#        emited
+#      * The `changed` event is emitted
+#      * The `changed` events of all updated markers are emitted
+#      * The `markers-updated` event is emitted.
+#
+# * `contents-modified` -
+#      Emitted asynchronously 300ms (or `TextBuffer::stoppedChangingDelay`)
+#      after the last buffer change. This is a good place to handle changes to
+#      the buffer without compromising typing performance.
+#
+# * `modified-status-changed` -
+#      Emitted with a {Boolean} when the result of {::isModified} changes.
+#
+# * `contents-conflicted` -
+#      Emitted when the buffer's underlying file changes on disk at a moment
+#      when the result of {::isModified} is true.
+#
+# * `will-reload` -
+#      Emitted before the in-memory contents of the buffer are refreshed from
+#      the contents of the file on disk.
+#
+# * `reloaded` -
+#      Emitted after the in-memory contents of the buffer are refreshed from
+#      the contents of the file on disk.
+#
+# * `will-be-saved` - Emitted before the buffer is saved to disk.
+#
+# * `saved` - Emitted after the buffer is saved to disk.
+#
+# * `destroyed` - Emitted when the buffer is destroyed.
 module.exports =
 class TextBuffer
   @Point: Point


:art: Add section for Gutter methods

:art: Add section for Gutter methods

Need for generated docs

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -514,25 +514,6 @@
   onDidChangeLineNumberGutterVisible: (callback) ->
     @emitter.on 'did-change-line-number-gutter-visible', callback

-  # Public: Creates and returns a {Gutter}.
-  # * `options` An {Object} with the following fields:
-  #   * `name` (required) A unique {String} to identify this gutter.
-  #   * `priority` (optional) A {Number} that determines stacking order between
-  #       gutters. Lower priority items are forced closer to the edges of the
-  #       window. (default: -100)
-  #   * `visible` (optional) {Boolean} specifying whether the gutter is visible
-  #       initially after being created. (default: true)
-  addGutter: (options) ->
-    @gutterContainer.addGutter(options)
-
-  # Public: Returns the {Array} of all gutters on this editor.
-  getGutters: ->
-    @gutterContainer.getGutters()
-
-  # Public: Returns the {Gutter} with the given name, or null if it doesn't exist.
-  gutterWithName: (name) ->
-    @gutterContainer.gutterWithName(name)
-
   # Essential: Calls your `callback` when a {Gutter} is added to the editor.
   # Immediately calls your callback for each existing gutter.
   #
@@ -2841,6 +2822,29 @@
     @displayBuffer.outermostFoldsInBufferRowRange(startRow, endRow)

   ###
+  Section: Gutters
+  ###
+
+  # Public: Creates and returns a {Gutter}.
+  # * `options` An {Object} with the following fields:
+  #   * `name` (required) A unique {String} to identify this gutter.
+  #   * `priority` (optional) A {Number} that determines stacking order between
+  #       gutters. Lower priority items are forced closer to the edges of the
+  #       window. (default: -100)
+  #   * `visible` (optional) {Boolean} specifying whether the gutter is visible
+  #       initially after being created. (default: true)
+  addGutter: (options) ->
+    @gutterContainer.addGutter(options)
+
+  # Public: Returns the {Array} of all gutters on this editor.
+  getGutters: ->
+    @gutterContainer.getGutters()
+
+  # Public: Returns the gutter with the given name, or null if it doesn't exist.
+  gutterWithName: (name) ->
+    @gutterContainer.gutterWithName(name)
+
+  ###
   Section: Scrolling the TextEditor
   ###


Make `TextEditor.prototype.scrollPastEnd` false by default

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -150,7 +150,7 @@
     @hasTerminatedPendingState = false

     @mini ?= false
-    @scrollPastEnd ?= true
+    @scrollPastEnd ?= false
     @showInvisibles ?= true
     @softTabs ?= true
     tabLength ?= 2


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -13,7 +13,7 @@
         text: titleText
         color: I.fontColor

-      canvas.font '14px Helvetica'
+      canvas.font '14px Helvetica'

       canvas.centerText
         y: App.height / 2


fix retreiving wrong content from a goal

--- lib/text-buffer.coffee
+++ lib/text-buffer.coffee
@@ -77,7 +77,7 @@
         @core.panelModel.set 'Out of goal', ['For this command, please place the cursor in a goal'], 'warning'

     warnCurrentGoalIfEmpty: (goal, warning) =>
-        content = goal.content
+        content = goal.getContent()
         isEmpty = content.replace(/\s/g, '').length is 0
         if isEmpty
             warn 'Text Buffer', 'empty content'
@@ -130,7 +130,7 @@
             @core.editor.setCursorBufferPosition previousGoal

     give: -> @getCurrentGoal().done (goal) =>
-            @warnCurrentGoalIfEmpty goal, 'Please type in the expression to give'
+            @warnCurrentGoalIfEmpty goal, 'Nothing to give'
             @core.executable.give goal
         , @warnOutOfGoal

@@ -147,7 +147,7 @@
         , @warnOutOfGoal

     goalTypeAndInferredType: -> @getCurrentGoal().done (goal) =>
-            @warnCurrentGoalIfEmpty goal, 'Please type in the expression to infer'
+            @warnCurrentGoalIfEmpty goal, 'Nothing to infer'
             @core.executable.goalTypeAndInferredType goal
         , @warnOutOfGoal

@@ -156,7 +156,7 @@
         , @warnOutOfGoal

     case: -> @getCurrentGoal().done (goal) =>
-            @warnCurrentGoalIfEmpty goal, 'Please type in the expression to make case'
+            @warnCurrentGoalIfEmpty goal, 'Nothing to make case'
             @core.executable.case goal
         , @warnOutOfGoal

@@ -190,7 +190,6 @@

             # if changed then modify text buffer
             if obj.content isnt obj.modifiedContent
-                # console.log "#{range} diff: #{diff} '#{obj.content}' '#{obj.modifiedContent}'"
                 @core.editor.setTextInBufferRange range , obj.modifiedContent
             return obj