more scaffold

more scaffold

--- dev/msg.iced
+++ dev/msg.iced
@@ -7,53 +7,6 @@
 {Message} = require '../lib/openpgp/processor'
 {PgpKeyRing} = require '../lib/keyring'
 {KeyManager} = require '../lib/keymanager'
-
-#=================================================================
-
-class Runner
-  constructor : ({@msgfile, @keyfile, @passphrase}) ->
-    @ring = new PgpKeyRing
-
-  _read_file : (fn, cb) ->
-    esc = make_esc cb, "read_file #{fn}"
-    await fs.readFile fn, esc defer data
-    [err, msgs] = armor.mdecode data
-    await util.athrow err, esc defer()
-    cb null, msgs
-
-  read_keys : (cb) ->
-    esc = make_esc cb, "read_keys"
-    await @_read_file @keyfile, esc defer msgs
-    asp = new util.ASP {}
-    userid = "anon@keybase.io"
-    for msg in msgs
-      await KeyManager.import_from_pgp_message { msg, asp, userid }, esc defer km
-      if km.is_pgp_locked()
-        await km.unlock_pgp { @passphrase }, esc defer()
-      @ring.add_key_manager km
-    cb null
-
-  read_msg : (cb) ->
-    esc = make_esc cb, "read_msg"
-    await @_read_file @msgfile, esc defer msgs
-    @msg = msgs[0]
-    cb null
-
-  process : (cb) ->
-    esc = make_esc cb, "process"
-    proc = new Message @ring
-    await proc.parse_and_process @msg.body, esc defer literals
-    console.log literals
-    for l in literals
-      console.log l.toString()
-    cb null
-
-  run : (cb) ->
-    esc = make_esc cb, "run"
-    await @read_keys esc defer()
-    await @read_msg esc defer()
-    await @process esc defer()
-    cb null

 #=================================================================

@@ -69,21 +22,76 @@

 #=================================================================

-ok = false
-if not argv.msg?
-  console.error "need a msg file to operate on"
-else if not argv.keyfile?
-  console.error "need a keyfile to read keys from "
-else if (argv.s or not argv.e) and not argv.passphrase
-  console.error "need a passphrase to unlock a signing or decrypting key"
-else
-  ok = true
+class Runner
+  constructor : (@argv) ->
+    @ring = new PgpKeyRing

-process.exit -1 unless ok
+  _read_file : (fn, cb) ->
+    esc = make_esc cb, "read_file #{fn}"
+    await fs.readFile fn, esc defer data
+    [err, msgs] = armor.mdecode data
+    await util.athrow err, esc defer()
+    cb null, msgs
+
+  read_keys : (cb) ->
+    esc = make_esc cb, "read_keys"
+    await @_read_file @argv.keyfile, esc defer msgs
+    asp = new util.ASP {}
+    userid = "anon@keybase.io"
+    for msg in msgs
+      await KeyManager.import_from_pgp_message { msg, asp, userid }, esc defer km
+      if km.is_pgp_locked() and @need_private_keys()
+        await km.unlock_pgp { passphrase : @argv.passphrase }, esc defer()
+      @ring.add_key_manager km
+    cb null
+
+  read_msg : (cb) ->
+    esc = make_esc cb, "read_msg"
+    await @_read_file @argv.msg, esc defer msgs
+    @msg = msgs[0]
+    cb null
+
+  from_pgp : (cb) ->
+    esc = make_esc cb, "process"
+    proc = new Message @ring
+    await proc.parse_and_process @msg.body, esc defer literals
+    console.log literals
+    for l in literals
+      console.log l.toString()
+    cb null
+
+  to_pgp : (cb) ->
+    cb null
+
+  need_private_keys : () -> (not @do_to_pgp()) or @argv.s
+  do_to_pgp : () -> @argv.s or @argv.e
+
+  parse_args : (cb) ->
+    ok = false
+    err = if not @argv.msg?
+      new Error "need a msg file to operate on"
+    else if not @argv.keyfile?
+      new Error "need a keyfile to read keys from"
+    else if @need_private_keys() and not @argv.passphrase
+      new Error "need a passphrase to unlock a signing or decrypting key"
+    else
+      null
+    cb err
+
+  run : (cb) ->
+    esc = make_esc cb, "run"
+    await @parse_args esc defer()
+    await @read_keys esc defer()
+    await @read_msg esc defer()
+    if @do_to_pgp()
+      await @to_pgp esc defer()
+    else
+      await @from_pgp esc defer()
+    cb null

 #=================================================================

-runner = new Runner { msgfile : argv.m, keyfile : argv.k, passphrase : argv.p }
+runner = new Runner argv
 await runner.run defer err
 throw err if err?
 process.exit 0


Fix select all checkboxes problem

Fix select all checkboxes problem


Remove link in todo-list title via js

Remove link in todo-list title via js


Pulse channel refactoring.

Pulse channel refactoring.

--- emulator/core/channels/pulse-channel.coffee
+++ emulator/core/channels/pulse-channel.coffee
@@ -15,12 +15,10 @@

     constructor: (@channelId) ->
         @setEnabled false
-        @setDutyEnvelope 0
-        @setSweep 0
-        @setTimer 0
-        @setLengthCounter 0
-        @updateState()
-        @updateVolume()
+        @writeDutyEnvelope 0
+        @writeSweep 0
+        @writeTimer 0
+        @writeLengthCounter 0

     setEnabled: (enabled) ->
         @enabled = enabled
@@ -31,12 +29,6 @@
     ###########################################################

     writeDutyEnvelope: (value) ->
-        @setDutyEnvelope value
-        @updateState()
-        @updateVolume()
-        value
-
-    setDutyEnvelope: (value) ->
         @dutySelection = (value & 0xC0) >>> 6      # Selects output waveform
         @lengthCounterHalt = (value & 0x20) isnt 0 # Disables length counter decrementation
         @useConstantVolume = (value & 0x10) isnt 0 # 0 - envelope volume is used / 1 - constant volume is used
@@ -45,73 +37,56 @@
         @envelopePeriod = @constantVolume          # Envelope duration period (constant volume alias)
         @envelopeCycle or= 0                       # Cycle of envelope divider
         @envelopeVolume or= 0                      # Envelope volume value
+        value

     ###########################################################
     # Sweep register
     ###########################################################

     writeSweep: (value) ->
-        @setSweep value
-        @updateState()
-        value
-
-    setSweep: (value) ->
         @sweepEnabled = (value & 0x80) isnt 0 # Sweeping enabled
         @sweepPeriod = (value & 0x70) >>> 4   # Period after which sweep is applied
         @sweepNegate = (value & 0x08) isnt 0  # 0 - sweep is added to timer period / 1 - sweep is subtracted from timer period
         @sweepShift = value & 0x07            # Shift of timer period when computing sweep
         @sweepReset = true                    # Sweep counter will be reseted
         @sweepCycle or= 0                     # Sweep counter
+        value

     ###########################################################
     # Timer register
     ###########################################################

     writeTimer: (value) ->
-        @setTimer value
-        @updateState()
+        @timerPeriod = (@timerPeriod or 0) & 0x700 | (value & 0xFF) # Lower 8 bits of timer
+        @timerCycle or= 0
         value
-
-    setTimer: (value) ->
-         @timerPeriod = (@timerPeriod or 0) & 0x700 | (value & 0xFF) # Lower 8 bits of timer
-         @timerCycle or= 0

     ###########################################################
     # Length counter / Timer register
     ###########################################################

     writeLengthCounter: (value) ->
-        @setLengthCounter value
-        @updateState()
-        value
-
-    setLengthCounter: (value) ->
         @timerPeriod = (@timerPeriod or 0) & 0x0FF | (value & 0x7) << 8           # Higher 3 bits of timer
         @lengthCounter = LENGTH_COUNTER_VALUES[(value & 0xF8) >>> 3] if @enabled  # Length counter update
         @dutyPosition = 0     # Output waveform position is reseted
         @envelopeReset = true # Envelope and its divider will be reseted
+        value

     ###########################################################
     # Tick
     ###########################################################

     tick: ->
-        # The timer actually counts T, T-1, ... 0, 1, ... T
-        # and clocks waveform generator (duty position) when it goes from T-1 to T.
-        # We are emulating it as 2*T, 2*T-1, ..., 0.
         if --@timerCycle <= 0
-            @timerCycle = (@timerPeriod + 1) << 1 # Real period is T + 1 APU cycles
+            @timerCycle = (@timerPeriod + 1) << 1 # Ticks twice slower than CPU
             @dutyPosition = (@dutyPosition + 1) & 0x7

     tickQuarterFrame: ->
         @updateEnvelope()
-        @updateVolume()
-        @updateState()

     tickHalfFrame: ->
         @updateLengthCounter()
         @updateSweep()
-        @updateState()

     ###########################################################
     # Envelope
@@ -147,7 +122,7 @@
         if @sweepCycle > 0
             @sweepCycle--
         else
-            @timerPeriod += @getSweep() if @sweepEnabled and @sweepShift and @timerPeriodValid
+            @timerPeriod += @getSweep() if @sweepEnabled and @sweepShift and @$isTimerPeriodValid()
             @sweepCycle = @sweepPeriod
         if @sweepReset
             @sweepReset = false
@@ -160,23 +135,17 @@
         else
             sweep

-    ###########################################################
-    # State
-    ###########################################################
-
-    updateState: ->
-        @timerPeriodValid = @timerPeriod >= 0x8 and @timerPeriod + @getSweep() < 0x800
-
-    updateVolume: ->
-        @volume = if @useConstantVolume then @constantVolume else @envelopeVolume
+    isTimerPeriodValid: ->
+        @timerPeriod >= 0x8 and @timerPeriod + @getSweep() < 0x800

     ###########################################################
     # Output value
     ###########################################################

     getOutputValue: ->
-        if @enabled and @timerPeriodValid and @lengthCounter
-            @volume * DUTY_WAVEFORMS[@dutySelection][@dutyPosition]
+        if @enabled and @lengthCounter and @$isTimerPeriodValid()
+            volume = if @useConstantVolume then @constantVolume else @envelopeVolume
+            volume * DUTY_WAVEFORMS[@dutySelection][@dutyPosition]
         else
             0


no need to loop the arr and incr

no need to loop the arr and incr

--- workers/social/lib/social/models/bundle/groupbundle.coffee
+++ workers/social/lib/social/models/bundle/groupbundle.coffee
@@ -76,10 +76,9 @@
       , {}, (err, cursor)=>
         cursor.toArray (err, arr)=>
           return callback err  if err
-          arr.forEach (vm)->
-            createdVMs += 1

-          firstVM = group.slug is 'koding' and createdVMs == 0 and planCode is 'free'
+          createdVMs = arr.length
+          firstVM    = group.slug is 'koding' and createdVMs == 0 and planCode is 'free'

           callback null, paidVMs > createdVMs or firstVM


Notification: follow/unfollow user request handlers are added to socialworker

--- workers/social/lib/social/models/socialapi/requests.coffee
+++ workers/social/lib/social/models/socialapi/requests.coffee
@@ -181,12 +181,23 @@
   url = "#{SOCIAL_API_URL}/notification/glance"
   post url, {accountId}, callback

-createFollowNotification = (data, callback)->
-  unless data.accountId and data.targetId
-    return callback {message: "Request is not valid"}
-
-  url = "#{SOCIAL_API_URL}/notification/follow"
-  post url, data, callback
+followUser = (data, callback)->
+  followHelper data, followTopic, callback
+
+unfollowUser = (data, callback)->
+  followHelper data, unfollowTopic, callback
+
+followHelper = (data, followFn, callback) ->
+  unless data.accountId and data.creatorId
+    return callback {message: "Request is not valid"}
+
+  data.typeConstant = "followers"
+  data.name = "FollowersChannelAccount-#{data.creatorId}"
+
+  createChannel data, (err, socialApiChannel) =>
+    return callback err  if err
+    data.channelId = socialApiChannel.id
+    followFn data, callback

 createGroupNotification = (data, callback)->
   unless data.admins?.length and data.actorId and data.name
@@ -250,6 +261,7 @@
   fetchGroupChannels
   listNotifications
   glanceNotifications
-  createFollowNotification
+  followUser
+  unfollowUser
   createGroupNotification
 }


Removed old library from karma conf

Removed old library from karma conf

--- karma.conf.coffee
+++ karma.conf.coffee
@@ -15,7 +15,6 @@
     }
     reporters: ['progress', 'osx']
     files: [
-      "vendor/bower/jasmine-given/dist/jasmine-given.js"
       "vendor/bower/jquery/jquery.js"
       "vendor/bower/lodash/dist/lodash.js"
       "vendor/bower/angular/angular.js"


Remove data:// check from data URIs (in coffee script)

--- src/data.coffee
+++ src/data.coffee
@@ -5,9 +5,6 @@
 class Data
   constructor: (client, path) ->
     @client = client
-
-    if path.indexOf('data://') != 0
-      throw 'Supplied path is invalid.'

     @data_path = path.replace /data\:\/\//, ''


only send ajax request first read

only send ajax request first read

--- app/assets/javascripts/app/models/conversation.js.coffee
+++ app/assets/javascripts/app/models/conversation.js.coffee
@@ -17,6 +17,7 @@
     }

   open: ->
+    return if @read
     @read = true
     @save()


Listen to Turbolinks 5 events.

Listen to Turbolinks 5 events.

* `turbolinks:request-start` replaces `page:fetch`.
* `turbolinks:load` replaces `page:load`.

The `ajaxStart?` check is required for initial page loads as Turbolinks now
triggers the `turbolinks:load` event on the first page load.

--- app/assets/javascripts/peek/views/performance_bar.coffee
+++ app/assets/javascripts/peek/views/performance_bar.coffee
@@ -119,15 +119,15 @@
   span.tipsy gravity: $.fn.tipsy.autoNS
   updateStatus span

-
 updateStatus = (html) ->
   $('#serverstats').html html

 ajaxStart = null
-$(document).on 'pjax:start page:fetch', (event) ->
+$(document).on 'pjax:start page:fetch turbolinks:request-start', (event) ->
   ajaxStart = event.timeStamp

-$(document).on 'pjax:end page:load', (event, xhr) ->
+$(document).on 'pjax:end page:load turbolinks:load', (event, xhr) ->
+  return unless ajaxStart?
   ajaxEnd    = event.timeStamp
   total      = ajaxEnd - ajaxStart
   serverTime = if xhr then parseInt(xhr.getResponseHeader('X-Runtime')) else 0


get rid of underscore, once again

get rid of underscore, once again

--- jquery.transparency.coffee
+++ jquery.transparency.coffee
@@ -1,4 +1,3 @@
-_ = require 'underscore'
 $ = jQuery

 assign = (node, attribute, value) ->


Machine: getOldOwner: little getter to get oldOwner

--- client/app/lib/providers/machine.coffee
+++ client/app/lib/providers/machine.coffee
@@ -148,3 +148,4 @@
   isRunning   : -> @status?.state is Machine.State.Running
   isStopped   : -> @status?.state is Machine.State.Stopped
   isUsable    : -> @isRunning() or @isStopped()
+  getOldOwner : -> @jMachine.meta.oldOwner


fix wiki hide button not working in firefox (STUD-1581)

--- cms/static/coffee/src/views/tabs.coffee
+++ cms/static/coffee/src/views/tabs.coffee
@@ -31,7 +31,7 @@
       )

     toggleVisibilityOfTab: (event, ui) =>
-      checkbox_element = event.srcElement
+      checkbox_element = event.target
       tab_element = $(checkbox_element).parents(".course-tab")[0]

       saving = new NotificationView.Mini({title: gettext("Saving…")})


link-tooltip: when selection changes, only call hide() if not hidden

--- src/modules/link-tooltip.coffee
+++ src/modules/link-tooltip.coffee
@@ -34,7 +34,7 @@
       if anchor
         this.setMode(anchor.href, false)
         this.show(anchor)
-      else
+      else if @container.style.left != Tooltip.HIDE_MARGIN
         @range = null   # Prevent restoring selection to last saved
         this.hide()
     )


do not show link dialog unless there is a selection

--- src/modules/link-tooltip.coffee
+++ src/modules/link-tooltip.coffee
@@ -47,6 +47,7 @@
   )
   ScribeDOM.addEventListener(@button, 'click', =>
     @savedRange = @editor.getSelection()
+    return unless @savedRange? and !@savedRange.isCollapsed()
     if ScribeDOM.hasClass(@button, 'active')
       formatLink.call(this, false)
       hideTooltip.call(this)


removed wet biomass losses from sankey

removed wet biomass losses from sankey

--- app/assets/javascripts/d3/sankey.coffee
+++ app/assets/javascripts/d3/sankey.coffee
@@ -41,7 +41,6 @@
           {left: 'wet',                             right: 'biofuels',                    gquery: 'wet_biomass_to_biofuels_in_biomass_sankey', color: '#38ada9'},
           {left: 'wet',                             right: 'greengas',                    gquery: 'wet_biomass_to_greengas_in_biomass_sankey', color: '#38ada9'},
           {left: 'wet',                             right: 'export',                      gquery: 'wet_biomass_to_export_in_biomass_sankey', color: '#38ada9'},
-          {left: 'wet',                             right: 'losses',                      gquery: 'wet_biomass_to_losses_in_biomass_sankey', color: '#DCDCDC'},
           {left: 'dry',                             right: 'greengas',                    gquery: 'dry_biomass_to_greengas_in_biomass_sankey', color: '#009432'},
           {left: 'dry',                             right: 'electricity_prod',            gquery: 'dry_biomass_to_electricity_prod_in_biomass_sankey', color: '#009432'},
           {left: 'dry',                             right: 'central_heat_prod',           gquery: 'dry_biomass_to_central_heat_prod_in_biomass_sankey', color: '#009432'},


better code

better code

--- src/game.coffee
+++ src/game.coffee
@@ -171,21 +171,27 @@
            y >= 0 and y < @board.nbVerticalTiles

+  isMine: (i) -> @data[i] is @entities.Mine
+
+
+  isFlag: (i) -> @board.cases[i].constructor is Flag
+
+
   showMines: (deadPosition) ->
     for i in [0...@board.cases.length]
       [x, y] = @coordFromPosition(i)
       params = [@board.board, x, y]
-      if @data[i] is @entities.Mine
+      if @isMine(i) or @isFlag(i)
         @board.cases[i].g.remove()
         if i is deadPosition
-          @board.cases[i] = new ExplodedMine params...
-        else if @board.cases[i].constructor is Flag
-          @board.cases[i] = new FlaggedMine params...
+          newTileClass = ExplodedMine
+        else if @isMine(i) and @isFlag(i)
+          newTileClass = FlaggedMine
+        else if @isFlag(i)
+          newTileClass = BadFlag
         else
-          @board.cases[i] = new Mine params...
-      else if @board.cases[i].constructor is Flag
-        @board.cases[i].g.remove()
-        @board.cases[i] = new BadFlag params...
+          newTileClass = Mine
+        @board.cases[i] = new newTileClass params...

   gameOver: (deadPosition) =>


Fix typo

Fix typo

--- app/assets/javascripts/graphs.js.coffee
+++ app/assets/javascripts/graphs.js.coffee
@@ -139,7 +139,7 @@
         return if !$(this).hasClass('disabled') && graph.series.active().length == 1

         mId = $(this).data('metric-id')
-        $(this).parents('.legend-container').find('.legend-' + m_id).toggleClass('disabled')
+        $(this).parents('.legend-container').find('.legend-' + mId).toggleClass('disabled')
         for s in graph.series
           if s.id == mId
             s.disabled = ! s.disabled


set the currentYear automatically

set the currentYear automatically

--- app/assets/javascripts/species/controllers/downloads_for_cites_restrictions_controller.js.coffee
+++ app/assets/javascripts/species/controllers/downloads_for_cites_restrictions_controller.js.coffee
@@ -19,7 +19,7 @@
   timeScopeIsCurrent: ( ->
     @get('timeScope') == 'current'
   ).property('timeScope')
-  years: [1975..2013]
+  years: [1975..new Date().getFullYear()]
   selectedYears: []
   documentType: 'CitesSuspensions'
   documentTypeIsCitesSuspensions: ( ->


Fixed schema for users.

Fixed schema for users.

--- app/lib/collections/schemas/user.coffee
+++ app/lib/collections/schemas/user.coffee
@@ -1,4 +1,9 @@
 schema = new SimpleSchema
+  username:
+    type: String
+    label: 'Username'
+    min: 3
+    max: 32
   emails:
     type: [Object]
     label: 'Emails'


updated close action

updated close action

--- app/scripts/components/BsPopover.coffee
+++ app/scripts/components/BsPopover.coffee
@@ -185,6 +185,7 @@
     actions:
         close: ->
             Bootstrap.TooltipBoxManager.removeTip @get("tip_id")
+            @sendAction "close"
 )

 Ember.Handlebars.helper 'bs-popover', Bootstrap.BsPopoverComponent


now augmenting user-supplied onEnter and onLeave functions instead of passing them straight through

--- src/html.coffee
+++ src/html.coffee
@@ -136,12 +136,15 @@
     window.location.href = linkVal if linkVal
     return

-  if not onEnter
-    onEnter = (val) -> highlight.value(val)
-
-  if not onLeave?
-    onLeave = (val) ->
-      if (highlight.value() is val) then highlight.value("")
+  myOnEnter = (val) ->
+    return if onEnter?(val) is false
+    highlight.value(val)
+    return
+
+  myOnLeave = (val) ->
+    return if myOnLeave?(val) is false
+    if (highlight.value() is val) then highlight.value("")
+    return

   dvl.register {
     name: 'update_html_list'
@@ -190,8 +193,8 @@
       cont = sel
         .attr('class', _class)
         .on('click', onClick)
-        .on('mouseover', onEnter)
-        .on('mouseout', onLeave)
+        .on('mouseover', myOnEnter)
+        .on('mouseout', myOnLeave)
         .select('a')
           .attr('href', _link)


Add mobile operator codes for Iraq

Add mobile operator codes for Iraq

--- src/script/countries/IRQ.coffee
+++ src/script/countries/IRQ.coffee
@@ -11,7 +11,15 @@
 		@optionalTrunkPrefix = '0'
 		@nationalNumberSeparator = ' '
 		@nationalDestinationCode =
-			['1', '21', '23', '24', '25', '30', '32', '33', '36', '37', '40', '42', '43', '50', '53', '60', '62', '66']
+			['7(\\d{2})', '1', '21', '23', '24', '25', '30', '32', '33', '36', '37', '40', '42', '43', '50', '53', '60', '62', '66']
+
+	specialRules: (withoutCountryCode, withoutNDC, ndc) =>
+		phone = new PhoneNumber(@countryNameAbbr, @countryCode, ndc, withoutNDC)
+
+		if withoutNDC.length is 7
+			phone.isMobile = ndc[0] is '7'
+			return phone
+		else if withoutNDC.length is 7 or withoutNDC.length is 6 then return phone

 # register
 iraq = new Iraq()


productionize.

productionize.

--- src/server/config/config.coffee
+++ src/server/config/config.coffee
@@ -1,5 +1,5 @@
 # Sets application config parameters depending on `env` name
-env = process.env.NODE_ENV or 'dev'
+env = process.env.NODE_ENV or 'prod'
 console.log "Set app environment: #{env}"

 switch(env)


Refactor expression manipulation.

Refactor expression manipulation.

--- math/expression_manipulation_spec.coffee
+++ math/expression_manipulation_spec.coffee
@@ -115,6 +115,23 @@
       expect(exp).toBeInstanceOf @math.components.expression
       expect(exp.isOpen()).toEqual true

+    describe "when adding to a sub-expression", ->
+      it "should add everything correctly", ->
+        exp = @exp_builder()
+        exp = @manip.append_number.build(value: 6).invoke(exp)
+        exp = @manip.append_addition.build().invoke(exp)
+
+        exp = @manip.open_sub_expression.build().invoke(exp)
+        exp = @manip.append_number.build(value: 6).invoke(exp)
+        exp = @manip.append_addition.build().invoke(exp)
+
+        exp = @manip.open_sub_expression.build().invoke(exp)
+        exp = @manip.close_sub_expression.build().invoke(exp)
+        exp = @manip.close_sub_expression.build().invoke(exp)
+        expected = @exp_builder(6, '+', [6, '+', []])
+
+        expect(exp).toBeAnEqualExpressionTo expected
+
   describe "closing a sub expression ", ->
     it "takes an open subexpression and closes it", ->
       exp = @exp_builder({open_expression: []})


more fix for repeating errors

more fix for repeating errors

git-svn-id: 00f01ee4f729ccc52c3e336777c3e81b5adc88db@249982 ef392f78-ee14-0410-84e5-9db46baae5bb

--- app/scripts/directives.coffee
+++ app/scripts/directives.coffee
@@ -405,14 +405,22 @@
             token = {and_block : [[]]}
             s.data.push token
             s.addOr token.and_block[0]
+            s.repeatError = false

         s.removeToken = (i) ->
             unless s.data.length > 1 then return
             s.data.splice(i, 1)
+            repeatError = true
+            for token in s.data
+                if not token.repeat or token.repeat[0] > 0
+                    repeatError = false
+                    break
+            s.repeatError = repeatError

         s.toggleRepeat = (token) ->
             unless token.repeat
                 token.repeat = [1,1]
+                s.repeatError = false
             else
                 s.repeatError = false
                 delete token.repeat


Removes debug messages.

Removes debug messages.

--- app/assets/javascripts/voluntary_ranking/views/ranking_items/colllection_view.js.coffee
+++ app/assets/javascripts/voluntary_ranking/views/ranking_items/colllection_view.js.coffee
@@ -10,11 +10,9 @@
       start: (event, ui) =>
         first_position = $('#ranking li:first').data('position')
         last_position = $('#ranking li:last').data('position')
-        console.log 'start:' + first_position + ' - ' + last_position

       update: (event, ui) =>
         source_item = $(ui.item).closest('li')
-        console.log 'update:' + $(source_item).data('position')
         Volontariat.current_position = first_position

         $.each $('#ranking li'), (index, element) ->


Add support for unwatching and unassigning from slack reactions

--- src/jira.coffee
+++ src/jira.coffee
@@ -412,6 +412,22 @@
     .catch (error) ->
       send msg, "An error has occured: #{error}"

+  handleUnassignRequest = (msg, includeAttachment=yes) ->
+    msg.finish?()
+    [ __, ticket ] = msg.match
+    fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}",
+      method: "PUT"
+      body: JSON.stringify
+        fields:
+          assignee:
+            name: null
+    .then () ->
+      fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}"
+    .then (json) ->
+      send msg,
+        text: "Unassigned <@#{msg.message.user.id}> from <#{jiraUrl}/browse/#{ticket}|#{ticket}>"
+        attachments: [ buildJiraAttachment json, no ] if includeAttachment
+
   handleAssignRequest = (msg, includeAttachment=yes) ->
     msg.finish?()
     [ __, ticket, person ] = msg.match
@@ -442,7 +458,7 @@
     else
       send msg, "Cannot find slack user `#{person}`"

-  handleWatchRequest = (msg, includeAttachment=yes) ->
+  handleWatchRequest = (msg, includeAttachment=yes, remove=no) ->
     msg.finish?()
     [ __, ticket, person ] = msg.match
     person = if person is "me" or not person then msg.message.user.name else person
@@ -456,14 +472,19 @@
         jiraUser = user[0] if user and user.length is 1
         throw "Cannot find jira user <@#{slackUser.id}>" unless jiraUser

-        fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}/watchers",
-          method: "POST"
-          body: JSON.stringify jiraUser.name
+        fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}/watchers#{if remove then "?username=#{jiraUser.name}" else ""}",
+          method: if remove then "DELETE" else "POST"
+          body: JSON.stringify jiraUser.name unless remove
       .then ->
         fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}"
       .then (json) ->
+        if remove
+          text ="Removed <@#{slackUser.id}> as a :watch:er on <#{jiraUrl}/browse/#{ticket}|#{ticket}>"
+        else
+          text ="Added <@#{slackUser.id}> as a :watch:er on <#{jiraUrl}/browse/#{ticket}|#{ticket}>"
+
         send msg,
-          text: "Added <@#{slackUser.id}> as a :watch:er on <#{jiraUrl}/browse/#{ticket}|#{ticket}>"
+          text: text
           attachments: [ buildJiraAttachment json, no ] if includeAttachment
       .catch (error) ->
         send msg, "#{error}"
@@ -626,11 +647,15 @@

   robot.adapter.client.on "raw_message", (msg) ->
     robot.emit "jira_ticket_created_message", msg if msg.type is "message" and msg.user is robot.adapter.self.id and msg.text is TICKET_CREATED_TEXT
-    return unless msg.type is "reaction_added"
     return unless msg.item_user is robot.adapter.self.id
     return unless msg.user isnt robot.adapter.self.id
-
-    switch msg.item.channel[0]
+    if msg.type is "reaction_added"
+      handleReactionAdded msg
+    else if msg.type is "reaction_removed"
+      handleReactionRemoved msg
+
+  getTicketInChannelByTs = (channel, ts) ->
+    switch channel[0]
       when "G"
         endpoint = "groups"
       when "C"
@@ -639,9 +664,9 @@
         return

     params =
-      channel: msg.item.channel
-      latest: msg.item.ts
-      oldest: msg.item.ts
+      channel: channel
+      latest: ts
+      oldest: ts
       inclusive: 1
       count: 1
       token: process.env.HUBOT_SLACK_TOKEN
@@ -649,13 +674,35 @@
     fetch("https://slack.com/api/#{endpoint}.history#{buildQueryString params}")
     .then (json) ->
       message = json.messages?[0]
-      return unless message? and message.type is "message"
+      throw "Cannot find message at timestamp provided" unless message? and message.type is "message"
       attachment = message.attachments?[0]
-      return unless attachment?.title_link?
+      throw "Message does not contain an attachment with a title link" unless attachment?.title_link?
       ticket = attachment.title_link.split "#{jiraUrl}/browse/"
-      return unless ticket and ticket.length is 2
-      ticket = ticket[1]
-
+      throw "Cannot find jira ticket" unless ticket and ticket.length is 2
+      return ticket[1]
+    .catch (error) ->
+      robot.logger.info error
+
+  handleReactionRemoved = (msg) ->
+    getTicketInChannelByTs(msg.item.channel, msg.item.ts)
+    .then (ticket) ->
+      switch msg.reaction
+        when "watch"
+          handleWatchRequest
+            message: room: msg.item.channel
+            match: [ "", ticket, robot.adapter.client.getUserByID(msg.user).name ]
+          , no, yes
+        when "raising_hand"
+          handleUnassignRequest
+            message:
+              room: msg.item.channel
+              user: id: msg.user
+            match: [ "", ticket]
+          , no
+
+  handleReactionAdded = (msg) ->
+    getTicketInChannelByTs(msg.item.channel, msg.item.ts)
+    .then (ticket) ->
       switch msg.reaction
         when "+1", "-1"
           handleRankRequest


Fixed IE6 trim error

Fixed IE6 trim error

--- src/json.coffee
+++ src/json.coffee
@@ -3,8 +3,6 @@
   # ported from JQuery
   parse: (data) ->
     return null if typeof data != "string"
-
-    data = data.trim()

     if window? and window.JSON? and window.JSON.parse?
       return window.JSON.parse(data)


Sweet ASCII logo

Sweet ASCII logo


Use api namespace

Use api namespace


Small clean-up

Small clean-up

--- src/main.coffee
+++ src/main.coffee
@@ -84,6 +84,22 @@
 width = size
 maxRadius = size / 2

+$canvas     = $ '#picker canvas'
+$svg        = $ '#picker svg'
+
+ctx = $canvas[0].getContext '2d'
+contrasting = null
+
+H = 0
+S = 100
+L = 50
+scale = null
+sortedIntersections = []
+bounds = []
+shape = null
+
+
+

 toCart = (angle, radius) ->
@@ -161,17 +177,6 @@
   ret.sort()
   return ret

-$canvas     = $ '#picker canvas'
-$svg        = $ '#picker svg'
-
-$controlHue        = $ "#picker .control-hue"
-$controlSaturation = $ "#picker .control-saturation"
-$controlLightness  = $ "#picker .control-lightness"
-
-ctx = $canvas[0].getContext '2d'
-contrasting = null
-
-
 redrawSquare = (x, y, dim) ->
   vx = (x - 200) / scale
   vy = (y - 200) / scale
@@ -220,15 +225,6 @@
   ctx.closePath()
   ctx.fill()

-H = 0
-S = 100
-L = 50
-scale = null
-sortedIntersections = []
-bounds = []
-shape = null
-pointer = null
-
 makeBackground = ->

   background = d3.select("#picker svg").append("g")


trim messages > 50

trim messages > 50

--- src/main.coffee
+++ src/main.coffee
@@ -20,7 +20,6 @@
     chatApp.props.connections.push(team)

     for preferredChannel in config.channels
-      console.log preferredChannel
       [teamName, channelName] = preferredChannel.name.split("#")
       if teamName is team.props.team.name
         for channelId, channel of conn.client.channels
@@ -47,6 +46,7 @@
       user = team.props.connection.client.users[msg.user]
       if user?
         message = React.createElement Message, {key: msg.ts, msg: msg, user: user, channel: channel }
+        channel.props.messages.shift() if channel.props.messages.length > 50
         channel.props.messages.push(message)

         console.log "#{msg._client.team.name} / #{channel.props.name} / #{user.name} - #{msg.text}"


adds separator

adds separator

--- src/main.coffee
+++ src/main.coffee
@@ -20,6 +20,7 @@
 app = require('electron').app

 console.log('Starting Yakyak v' + app.getVersion() + '...')
+console.log('--------')
 app.disableHardwareAcceleration() # was using a lot of resources needlessly
 app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required')


:see_no_evil:

--- src/main.coffee
+++ src/main.coffee
@@ -76,9 +76,7 @@
       client.write(output)
       callback(true)

-    client.on 'error', (args...) ->
-      console.log 'error', args
-      callback(false)
+    client.on 'error', (args...) -> callback(false)

   listen = ->
     fs.unlinkSync socketPath if fs.existsSync(socketPath)


Add basic documentation

Add basic documentation

--- src/main.coffee
+++ src/main.coffee
@@ -1,10 +1,13 @@
-#document.body.addEventListener 'touchmove', (event) ->
-#	event.preventDefault()
+# Prevent iOS Safari from doing the scroll thing
+document.body.addEventListener 'touchmove', (event) ->
+	event.preventDefault()

+# Anonymous function to map paper to P
 ((P) ->
 	canvas = document.getElementById 'canvas'
 	P.setup canvas

+	# Set the background image
 	background = new P.Raster
 		source: '//hufkens.net/wp-content/uploads/2012/10/bumba-win8.png'
 		position: new P.Point P.view.center.x, .7 * P.view.center.y
@@ -13,6 +16,7 @@
 	background.onLoad = () ->
 		@size = new P.Size P.view.size.width, .7 * P.view.size.height

+	# The rectangle used for the cutout of the pieces
 	rectangle = new P.Rectangle 0, 0, P.view.size.width / 4, .7 * P.view.size.height / 4

 	###
@@ -22,10 +26,10 @@
 	pieces = []

 	for i in [0..1]
+
+		# The edge will serve as the mask for the piece
 		pieceEdge = new P.Shape.Rectangle rectangle
 		pieceEdge.set
-			strokeColor: 'black'
-			strokeWidth: 0
 			selected: true

 		pieceBackground = new P.Raster
@@ -35,6 +39,7 @@
 		pieceBackground.onLoad = () ->
 			@size = new P.Size P.view.size.width, .7 * P.view.size.height

+		# Create a Group using the mask and the background
 		pieceGroup = new P.Group pieceEdge, pieceBackground, pieceEdge.clone()
 		pieceGroup.clipped = true
 		pieceGroup.position.x = i * rectangle.width / 2 + rectangle.width / 2
@@ -44,6 +49,10 @@
 			edge: pieceEdge
 			group: pieceGroup

+	###
+	Mouse events
+	###
+
 	actualPiece = null
 	dragging = false

@@ -52,7 +61,6 @@
 		return if dragging

 		# Reset
-		piece.group.strokeWidth = 0 for piece in pieces
 		possiblePieces = []
 		actualPiece = null

@@ -72,6 +80,9 @@
 		actualPiece.group.bringToFront()

 	tool.onMouseUp = (event) ->
+
+		# Reset
+		piece.group.strokeWidth = 0 for piece in pieces
 		actualPiece = null
 		dragging = false


rename isHitTest to isIntersect

rename isHitTest to isIntersect

--- lib/_stationary.coffee
+++ lib/_stationary.coffee
@@ -342,6 +342,11 @@
                     ret = @sprite.intersect(motionObj.collider)
             else
                 ret = false
+        else if (@_type == MAP)
+            if (@intersectFlag)
+                ret = @sprite.hitTest(motionObj.x, motionObj.y)
+            else
+                ret = false
         else
             motionObj.sprite.type = 'AABB'
             @sprite.type = 'AABB'


small syntax change

small syntax change

--- lib/applyPolicy.coffee
+++ lib/applyPolicy.coffee
@@ -35,7 +35,7 @@
     else if rule.except?
       validateServices rule.except

-      for service in Object.keys services
+      for service of services

         # don't apply it to the exceptions, to any other filters, or to things not matching applyTo
         continue if service in rule.except
@@ -53,7 +53,7 @@

   # return the original services with the new filters prepended
   wrappedServices = {}
-  for name in Object.keys services
+  for name of services
     filters = for filter in policyMap[name]
       generateFilter filter, services[filter]
     wrappedServices[name] = services[name]


force maxItems for now

force maxItems for now

--- lib/atom-ternjs.coffee
+++ lib/atom-ternjs.coffee
@@ -84,6 +84,10 @@
     @disposables.push buffer.onDidStopChanging =>
       @callPreBuildSuggestions()
     @autocomplete.registerProviderForEditorView provider, editorView
+    # force maxItems for now
+    for view in @autocomplete.autocompleteViews
+      if view.editor.getGrammar().name is 'JavaScript'
+        view.maxItems = 250
     @providers.push provider

   callPreBuildSuggestions: ->


Only need to create a given mock method once, not once per .expects

--- src/mock.coffee
+++ src/mock.coffee
@@ -27,8 +27,7 @@
   expects: (method_name) ->
     @_check_expects_usage(method_name)
     @signatures.unshift( new MethodSignature(method_name) )
-    # TODO: use ?=, not =
-    @[ method_name ] = @_define_expected_method(method_name)
+    @[ method_name ] ?= @_define_expected_method(method_name)
     @_set_state("expects")
     @


Fix weird tab behaviour

Fix weird tab behaviour

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SnippetManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SnippetManager.coffee
@@ -138,7 +138,7 @@
 						caption: "\\begin{#{item.name}}..."
 						snippet: """
 							\\begin{#{item.name}}
-							#{item.whitespace || ''}$1
+							#{item.whitespace || ''}$0
 							\\end{#{item.name}}
 						"""
 						meta: "env"


Add missing path dependency

Add missing path dependency

--- lib/drive/drive.coffee
+++ lib/drive/drive.coffee
@@ -1,5 +1,6 @@
 os = require('os')
 fs = require('fs')
+path = require('path')
 _ = require('lodash-contrib')
 async = require('async')
 childProcess = require('child_process')


support rule names

support rule names

--- scripts-src/standalone_searcher.coffee
+++ scripts-src/standalone_searcher.coffee
@@ -1,7 +1,7 @@
 #!/usr/bin/env coffee
 fs = require "fs"
 {Cells, evaluateCellList, getDualTransform, splitPattern} = require "./cells"
-{parse} = require "./rules"
+{parse, NamedRules} = require "./rules"
 stdio = require "stdio"
 {mod, div} = require "./math_util"
 {Array2d, MargolusNeighborehoodField} = require "./reversible_ca"
@@ -63,8 +63,7 @@
     process.stderr.write "Output file not specified\n"
     process.exit 1
   outputPath = opts.args[0]
-
-  console.log opts
+
   #Now extract the values
   [width, height] = parseSizePair opts.size, [256, 256]
   [seedWidth, seedHeight] = parseSizePair opts['seed-size'], [width>>1, height>>1]
@@ -72,7 +71,15 @@

   seedPercent = if opts['seed-percent']? then parseInt(opts['seed-percent'], 10)*0.01 else 0.5

-  rule = parse opts.rule
+  if /(\d+,){15}\d+/.test opts.rule
+    rule = parse opts.rule
+  else
+    rule = NamedRules[opts.rule]
+    unless rule?
+      process.stderr.write "Incorrect rule. Must be either 16 comma-separated integers or a name. Availabele names are:\n"
+      for ruleName of NamedRules
+        process.stderr.write " - #{ruleName}\n"
+      process.exit 1
   maxSteps = if opts["max-steps"]? then parseInt(opts["max-steps"],10) else 3000
   console.log "Width: #{width},height: #{height}"
   console.log "Seed size: #{seedWidth} x #{seedHeight}"


Default to loading latest version

Default to loading latest version

--- src/api.coffee
+++ src/api.coffee
@@ -70,7 +70,7 @@

 start = ->
   apiHelper ExerciseActions, ExerciseActions.load, ExerciseActions.loaded, 'GET', (id) ->
-    id = if id.indexOf("@") is -1 then "#{id}@draft" else id
+    id = if id.indexOf("@") is -1 then "#{id}@latest" else id
     url: "/api/exercises/#{id}"

   apiHelper ExerciseActions, ExerciseActions.save, ExerciseActions.saved, 'PUT', (id) ->


:checkered-flag: Add `getResourcePath()` fallback for Windows

Adds fallback behavior for `getResourcePath()` on Windows
(roughly equivalent to the behavior on Linux and macOS).

This should allow apm to find an installation of Atom on the disk,
even when `apm` is not bundled with the Atom install it's looking for.
For example: when working in the apm git repo.

--- src/apm.coffee
+++ src/apm.coffee
@@ -3,6 +3,7 @@
 path = require 'path'
 npm = require 'npm'
 semver = require 'semver'
+glob = require 'glob'

 module.exports =
   getHomeDirectory: ->
@@ -49,6 +50,15 @@
         unless fs.existsSync(appLocation)
           appLocation = '/usr/share/atom/resources/app.asar'
         process.nextTick -> callback(appLocation)
+      else # Windows
+        pattern = "/Users/#{process.env.USERNAME}/AppData/Local/atom/app-+([0-9]).+([0-9]).+([0-9])/resources/app.asar"
+        glob pattern, null, (er, appLocations) ->
+          # appLocations is an array of '/absolute/paths/to/app.asar'.
+          # Array elements are sorted; Newer Atom installs are higher-versioned,
+          # So they should be sorted later in the array.
+          # If nothing was found, then appLocation is an empty array.
+          # er is an error object or null.
+          callback(appLocations.pop()) # Use the newest Atom install found (last element of appLocations).

   getReposDirectory: ->
     process.env.ATOM_REPOS_HOME ? path.join(@getHomeDirectory(), 'github')


Allow configuring the port

Allow configuring the port

--- src/app.coffee
+++ src/app.coffee
@@ -35,7 +35,7 @@
   server: os.hostname()
   driver: "memory"
   params: {}
-  address: null
+  address: null,

 if fs.existsSync(CONFIG_FILE)
   try
@@ -51,6 +51,8 @@
 server = config.server
 address = config.address or server
 useHTTPS = (if (config.key) then true else false)
+if not config.port
+  port = if useHTTPS then 443 else 80

 localURL.server = server
 localURL.protocol = (if (useHTTPS) then "https" else "http")
@@ -109,7 +111,7 @@
     app.hub = new Hub(localURL.server, db)
     app.feed = new Feed()
     if useHTTPS
-      app.listen 443, address
+      app.listen config.port, address
       bounce.listen 80, address
     else
-      app.listen 80, address
+      app.listen config.port, address


minor: typo fix

minor: typo fix

--- client/landing/site.landing/coffee/login/AppView.coffee
+++ client/landing/site.landing/coffee/login/AppView.coffee
@@ -15,7 +15,7 @@

 module.exports = class LoginView extends JView

-  RECATCHA_JS = 'https://www.google.com/recaptcha/api.js?onload=onRecaptchaloadCallback&render=explicit'
+  RECAPTCHA_JS = 'https://www.google.com/recaptcha/api.js?onload=onRecaptchaloadCallback&render=explicit'

   stop           = KD.utils.stopDOMEvent
   ENTER          = 13
@@ -415,7 +415,7 @@
         @recaptcha = new KDCustomHTMLView
           tagName    : 'script'
           attributes :
-            src      : RECATCHA_JS
+            src      : RECAPTCHA_JS
             async    : yes
             defer    : yes


improved options, optional jQuery and Semantic

jQuery, and Semantic not required, but will log warnings.

--- src/aui.coffee
+++ src/aui.coffee
@@ -1,7 +1,7 @@
 {React, jQuery} = global or window
-unless React? then throw new Error "window.React not found."
-unless jQuery? then throw new Error "window.jQuery not found."
-unless jQuery?.site?.settings?.modules? then console.warn 'No Semantic-UI window.jQuery.site.settings.modules found.'
+unless React? then throw new Error "Aui: window.React not found."
+unless jQuery? then console.warn "Aui: window.jQuery not found, Modules and Semantic-UI will be disabled."
+unless jQuery?.site?.settings?.modules? then console.warn 'Aui: No Semantic-UI window.jQuery.site.settings.modules found, Semantic will be disabled.'

 ## class <Aui/>
 # Aui class is the main wrapper class
@@ -9,38 +9,63 @@
 # finding props that === true or are on the `Aui.modules` list,
 # and merges them into the className of each element.
 Aui = React.createClass
-  render: -> Aui.classify (React.Children.only @props.children), @props
+  render: ->
+    Aui.classify (React.Children.only @props.children), @props
 Aui.Aui = Aui
+
+## object Aui.defaults
+# The default options Aui uses when no options or props are passed in.
+Aui.defaults =
+  # disables jQuery based Aui.modules from calling
+  disableModules: not jQuery?
+  # disables Semantic-UI specific features
+  disableSemantic: jQuery?.site?.settings?.modules?
+  # do not recursively Aui.classify children by default.
+  ignoreChildren: no
+
+## internal class `AuiOptions({options})`
+# defaults all Aui options
+class AuiOptions
+  constructor: (options) ->
+    unless options instanceof AuiOptions
+      return options
+    unless @ instanceof AuiOptions
+      return new AuiOptions options
+    options or= {}
+    for own key, value in Aui.defaults
+      @[key] = if options[key]? then options[key] else value

 ## mixin AuiMixin
 # !EXPIRIMENTAL
-# Automatically wraps your render function with an <Aui>tag</Aui>
+# Automatically wraps your render function with an <Aui>tag</Aui> with default props
 Aui.AuiMixin =
   componentWillMount: ->
     render = @render
     @render = ->
-      React.createElement Aui, render.apply @, arguments
+      Aui.classify render.apply @, arguments

 ## element `Aui.classify(ReactElement, {...props}, { boolean modules })`
 # finds all the props that === true or are on the `Aui.modules` list,
 # and merging them with the className prop.
 # `Aui.classify(<div ui grid><div column>content</div></div>)`
 # returns `<div ui grid className="ui grid"><div column className="column">content</div></div>`
-Aui.classify = (children, options = { modules: yes, recursive: yes }) ->
+Aui.classify = (children, options) ->
+  options = AuiOptions options
   React.Children.map children, (element) ->
     return element if element.type is Aui
     props = {}
     isModule = no
     classNames = {}
-    for name in "#{element.props?.className or ''}".split /\s+/g
-      classNames[name] = yes if name
+    if element.props.className
+      for name in "#{element.props.className}".split /\s+/g
+        classNames[name] = yes if name
     for own name, include of element.props or {}
       if jQuery and options.modules and (Array.isArray include) and name in Aui.modules
         isModule = classNames[name] = yes
       else if include is yes
         classNames[name] = yes
     classNames = Object.keys classNames
-    if options.recursive and element.props?.children?
+    if options.recursive and element.props.children?
       props.children = Aui.classify element.props.children, options
     if classNames.length
       props.className = classNames.join ' '
@@ -57,13 +82,13 @@
 # but in theory could be used to call any `window.jQuery.fn` function
 Aui.modules = jQuery?.site?.settings?.modules or []

-## class <Aui.Module/>
+## internal class <Aui.Module/>
+# This class provides support for `Aui.modules` and handles calling jQuery.fn[module] calls
 Aui.Module = React.createClass
   render: -> React.Children.only @props.children
   componentDidMount: -> @callModules @props.children.props
   componentWillReceiveProps: (props) -> @callModules props.children.props
   callModules: (props) ->
-    console.log $element[0]
     for module, args of props
       if (Array.isArray args) and (typeof jQuery.fn[module] is 'function') and (module in Aui.module)
         $element = jQuery React.findDOMNode @


scroll to selected item on showing drop down

--- src/chz.coffee
+++ src/chz.coffee
@@ -34,7 +34,7 @@

     move = (d) ->
       items = $scope.shownItems
-      activeIndex = (items.indexOf($scope.activeItem) || 0) + d
+      activeIndex = getActiveIndex() + d
       activeIndex = Math.min(Math.max(activeIndex,0), items.length - 1)
       $scope.activeItem = items[activeIndex]
       scrollIfNeeded(activeIndex)
@@ -90,8 +90,14 @@

     $scope.$watch 'search', search

+    $scope.$watch 'active', (value) ->
+      window.setTimeout((()-> scrollIfNeeded(getActiveIndex())) , 0) if value
+
     $scope.hideDropDown = ->
       $scope.active = false
+
+    getActiveIndex = ->
+      ($scope.shownItems.indexOf($scope.activeItem) || 0)

     # run
     search('')


+ welcome screen and fixed bug

+ welcome screen and fixed bug

--- src/cmd.coffee
+++ src/cmd.coffee
@@ -1,8 +1,14 @@
-# Starfruit - Command Line
+#
+#       _/_/_/    _/                              _/_/                      _/    _/
+#    _/        _/_/_/_/    _/_/_/  _/  _/_/    _/      _/  _/_/  _/    _/      _/_/_/_/
+#     _/_/      _/      _/    _/  _/_/      _/_/_/_/  _/_/      _/    _/  _/    _/
+#        _/    _/      _/    _/  _/          _/      _/        _/    _/  _/    _/
+# _/_/_/        _/_/    _/_/_/  _/          _/      _/          _/_/_/  _/      _/_/
 #
 # MIT Licensed
+# Copyright (c) 2014 Kan Kung-Yip
 #
-# Copyright (c) 2014 Kan Kung-Yip
+# Command Line Tool

 # Module dependencies
 fs = require 'fs'
@@ -23,9 +29,19 @@
   error: (str) -> '\x1b[31m' + str + '\x1b[0m'
   warning: (str) -> '\x1b[33m' + str + '\x1b[0m'
   over: (str) -> '\x1b[36m' + str + '\x1b[0m'
+  cmd: (str) -> '\x1b[32m' + str + '\x1b[0m'

 # cpus
 MAX_CPUS = os.cpus().length
+
+# welcome
+WELCOME = '\x1b[0;0H\x1b[K\x1b[J
+\n                        _
+\n    __ _|_   __   __  _|_  __        o _|_
+\n  __)   |_, (__( |  \'  |  |  \' (__(_ |  |_,
+\n
+\n
+'

 # helps
 USAGE_HELP = '\x1b[1mPilot [0.0.1]\x1b[0m
@@ -155,7 +171,8 @@
   console.log message + '\n'

 # Add server process
-add = (env, cpus = 1) ->
+add = (env, cpus) ->
+  if cpus? then cpus = parseInt cpus else cpus = 1
   num = (pid for pid of workers).length
   if num < MAX_CPUS
     if cpus > MAX_CPUS - num
@@ -166,16 +183,16 @@
     while cpus > 0
       start env
       cpus--
-    return console.log (if num > 1 then '%s processes are running' else '%s process is running'), style.int num
-  console.log 'Max running %s processes\nUse \'list\' command look all processes', style.int MAX_CPUS
+    return console.log (if num > 1 then '%s processes are running\n' else '%s process is running\n'), style.int num
+  console.log 'Max running %s processes, use %s command look all processes\n', style.int(MAX_CPUS), style.cmd('list')

 # Remove worker by pid of process
 remove = (pid) ->
-  return console.log 'Usage: remove <pid>' unless pid
-  return console.error style.error('Not found pid ' + pid) unless workers.hasOwnProperty pid
+  return console.log 'Usage: %s <pid>\n', style.cmd 'remove' unless pid
+  return console.error '%s\n', style.error 'Not found pid ' + pid unless workers.hasOwnProperty pid
   #workers[pid].auto = false
   workers[pid].process.kill 'SIGKILL'
-  console.log 'Process %s shutdown', style.int pid
+  console.log 'Process %s shutdown\n', style.int pid

 # Restart all process
 restart = ->
@@ -185,11 +202,11 @@
 quit = (resolve) ->
   switch resolve
     when 'yes', 'y' then shutdown 'Bye!'
-    when 'no', 'n' then return [null, '']
+    when 'no', 'n' then console.log ''; return [null, '']
     else return ['quit', style.warning 'ARE YOU SURE? (yes|y|no|n)']

 # Welcome screen
-process.stdout.write '\x1b[0;0H\x1b[K\x1b[J'
+process.stdout.write WELCOME

 # Getting user options
 argv = process.argv.slice 2
@@ -225,7 +242,7 @@
     # command action
     shell.confirm = false
     switch command
-      when 'add' then add env, parseInt argv[0]
+      when 'add' then add env, argv[0]
       when 'clear', 'cls' then process.stdout.write '\x1b[0;0H\x1b[K\x1b[J'
       when 'help', '?' then help SHELL_HELP
       when 'list', 'ls' then list()
@@ -233,5 +250,5 @@
       when 'restart' then restart()
       when 'quit', 'exit' then [confirm, message] = quit resolve
       # error command
-      else console.error style.error command + ': command not found' if command.length > 0
+      else console.error style.error command + ': command not found\n' if command.length > 0
     repl message, confirm


Tweak tool bar total width and trivial refactoring.

--- lib/finder-tool.coffee
+++ lib/finder-tool.coffee
@@ -45,20 +45,23 @@
     @name.style.width = @nameWidth + 'px'
     @size.style.width = @sizeWidth + 'px'
     @mdate.style.width = @mdateWidth + 'px'
-    @toolBar.style.width = @nameWidth + @sizeWidth + @mdateWidth + 200 + 'px'
+    @toolBar.style.width = @nameWidth + @sizeWidth + @mdateWidth + 106 + 'px'

     drag = null

+    getTargetRsz = (e) =>
+      return @name if e.target.id == 'name-rsz'
+      return @size if e.target.id == 'size-rsz'
+      return @mdate if e.target.id == 'mdate-rsz'
+      return null
+
     @subscriptions.add @subscribeTo @toolBar, '.rsz',
+      'dblclick': (e) =>
+        console.log "finder-tool: double click:", e.target.id, e if @debug
+        # XXX, you can invoke some function here...
       'mousedown': (e) =>
         console.log "finder-tool: drag:", e.target.id, e if @debug
-        if e.target.id == 'name-rsz'
-          target = @name
-        if e.target.id == 'size-rsz'
-          target = @size
-        if e.target.id == 'mdate-rsz'
-          target = @mdate
-        return if not target
+        return if not target = getTargetRsz(e)
         drag = {
           x: e.clientX,
           y: e.clientY,


Show only commits on this branch

Show only commits on this branch

Hiding from the rev-walker here is really slow on large repos. There’s
got to be a way to do this faster.

--- lib/git-history.coffee
+++ lib/git-history.coffee
@@ -4,7 +4,7 @@
 module.exports =
 class GitHistory
   commits: {}
-  numberCommits: 10
+  numberCommits: 50

   constructor: ->
     @repoPromise = Git.Repository.open(atom.project.getPaths()[0])
@@ -31,10 +31,15 @@
         walk(repo, walker, numberCommits - 1, callback)

     @repoPromise.then (repo) =>
-      repo.getHeadCommit().then (commit) =>
-        walker = repo.createRevWalk()
-        walker.simplifyFirstParent()
-        walker.push(commit.id())
-        walk repo, walker, @numberCommits, (commit) =>
-          @addCommit(commit)
-          commitCallback(commit)
+      walker = repo.createRevWalk()
+      walker.simplifyFirstParent()
+      walker.pushHead()
+
+      Promise.all([repo.getHeadCommit(), repo.getBranchCommit('master')]).then (commits) =>
+        console.log commits
+        Git.Merge.base(repo, commits[0], commits[1]).then (base) =>
+          console.log base
+          walker.hide(base)
+          walk repo, walker, @numberCommits, (commit) =>
+            @addCommit(commit)
+            commitCallback(commit)


now prohibit @import inside code block (chunk)

--- lib/file-import.coffee
+++ lib/file-import.coffee
@@ -144,7 +144,7 @@
 ###
 fileImport = (inputString, {filesCache, fileDirectoryPath, projectDirectoryPath, useAbsoluteImagePath, imageDirectoryPath, insertAnchors})->
   new Promise (resolve, reject)->
-    inblock = false
+    inBlock = false # inside code block

     helper = (i, lineNo=0, outputString="")->
       if i >= inputString.length
@@ -157,23 +157,24 @@
       end = inputString.length if end < 0
       line = inputString.substring i, end

+      if line.match(/^\s*```/)
+        inBlock = !inBlock
+
+      if inBlock
+        return helper(end+1, lineNo+1, outputString+line+'\n')
+
       if insertAnchors
-        if inblock
-          if line.match(inblock)
-            inblock = false
-        else if line.match /^(\#|\!\[|```[^`]|@import)/
+        if line.match /^(\#|\!\[|```[^`]|@import)/
           outputString += createAnchor(lineNo)
-          if line.match(/^```/)
-            inblock = /^```\s*$/
         else if subjectMatch = line.match /^\<!--\s+([^\s]+)/
           subject = subjectMatch[1]
           if subjects[subject]
             line = line.replace(subject, "#{subject} lineNo:#{lineNo} ")
             outputString += createAnchor(lineNo)

-      if importMatch = line.match /^\@import(\s+)\"([^\"]+)\";?/
-        whole = importMatch[0]
-        filePath = importMatch[2].trim()
+      if importMatch = line.match /^(\s*)\@import(\s+)\"([^\"]+)\";?/
+        outputString += importMatch[1]
+        filePath = importMatch[3].trim()

         leftParen = line.indexOf('{')
         config = null


Add theoremref support

Add theoremref support

This allows the package to find labels set by the \thlabel command from the theoremref package.

--- lib/find-labels.coffee
+++ lib/find-labels.coffee
@@ -5,7 +5,7 @@
 module.exports =
 FindLabels =
   getLabelsByText: (text, file = "") ->
-    labelRex = /\\label{([^}]+)}/g
+    labelRex = /\\(?:th)label{([^}]+)}/g
     matches = []
     while (match = labelRex.exec(text))
       matches.push {label: match[1]}


use ide-mode

use ide-mode

--- lib/idris-model.coffee
+++ lib/idris-model.coffee
@@ -16,7 +16,7 @@

   start: ->
     pathToIdris = atom.config.get("atom-language-idris.pathToIdris")
-    @process = spawn pathToIdris, ['--ideslave']
+    @process = spawn pathToIdris, ['--ide-mode']
     @process.on 'exit', @exited.bind(this)
     @process.on 'error', @exited.bind(this)
     @process.stdout.setEncoding('utf8').on 'data', @stdout


first version of main file

first version of main file

--- main.coffee
+++ main.coffee
@@ -1,5 +1,10 @@
+clientTM = require './lib/clientTransformManager'
+serverTM = require './lib/serverTransformManager'
+
 module.export =

-  server: ->
+  createServer: ->
+    new serverTM false, port

-  client: ->+  createClient: (url, name, color) ->
+    new clientTM url, name, color


don't always show error

don't always show error

--- main.coffee
+++ main.coffee
@@ -15,7 +15,8 @@
   console.log "Setting config: #{key}, #{value}"
   nconf.set key, value
   nconf.save (err) ->
-    console.log "Could not save config: #{err}"
+    if err
+      console.log "Could not save config: #{err}"

 app.on "window-all-closed", ->
   unless process.platform == "darwin"


forgot this

forgot this

--- main.coffee
+++ main.coffee
@@ -1,8 +0,0 @@
-hint = require './lib/hint'
-
-coffeePaths = process.argv[2..]
-if hint coffeePaths, true
-  process.exit 0
-else
-  process.exit 1
-


Hopefully proper fix for last commit

Hopefully proper fix for last commit

--- main.coffee
+++ main.coffee
@@ -192,7 +192,7 @@
   file = "#{__dirname}/uploads/#{req.params.fileid}"
   if /^[a-f0-9A-F]+$/.test(req.params.fileid) and fs.existsSync(file)
     db.mimetypeForFile req.params.fileid, (mimetype) ->
-      if 'html' in mimetype
+      if /html/i.test mimetype
         console.log("replacing html mimetype '#{mimetype}' with octet-stream")
         res.set 'Content-Type', 'application/octet-stream'
       else


Add UI for editing chats

Add UI for editing chats

--- src/smc-webapp/editor_chat.cjsx
+++ src/smc-webapp/editor_chat.cjsx
@@ -50,6 +50,7 @@
 class ChatActions extends Actions
     _syncdb_change: (changes) =>
         m = messages = @redux.getStore(@name).get('messages')
+        console.log("SYNC_DB_CHANGE", changes)
         for x in changes
             if x.insert
                 messages = messages.set(x.insert.date - 0, immutable.fromJS(x.insert))
@@ -75,10 +76,21 @@
     set_input: (input) =>
         @setState(input:input)

+    # Doesn't work
+    change_message: (message, new_content) =>
+        #key = message.get('date')
+        #console.log("CHANGE MESSAGE", key, new_content)
+
+        #@mergeState(messages:
+        #    "#{key}":
+        #        payload:
+        #            content:new_content)
+
     save_scroll_state: (position, height, offset) =>
         # height == 0 means chat room is not rendered
         if height != 0
             @setState(saved_position:position, height:height, offset:offset)
+

 # boilerplate setting up actions, stores, sync'd file, etc.
 syncdbs = {}
@@ -101,6 +113,8 @@
             else
                 v = {}
                 for x in syncdb.select()
+                    if x.edits
+                        x.edits = immutable.Stack(x.edits)
                     v[x.date - 0] = x
                 actions.setState(messages : immutable.fromJS(v))
                 syncdb.on('change', actions._syncdb_change)
@@ -114,6 +128,8 @@
         # {"sender_id":"f117c2f8-8f8d-49cf-a2b7-f3609c48c100","event":"chat","payload":{"content":"l"},"date":"2015-08-26T21:52:51.329Z"}
         message        : rtypes.object.isRequired  # immutable.js message object
         account_id     : rtypes.string.isRequired
+        content        : rtypes.string
+        timestamp      : rtypes.any
         sender_name    : rtypes.string
         user_map       : rtypes.object
         project_id     : rtypes.string    # optional -- improves relative links if given
@@ -122,6 +138,11 @@
         show_avatar    : rtypes.bool
         is_prev_sender : rtypes.bool
         is_next_sender : rtypes.bool
+        actions        : rtypes.object
+
+    getInitialState: ->
+        edited_message  : @props.message.get('payload')?.get('content') ? ''
+        show_edit_input : false

     shouldComponentUpdate: (next) ->
         return @props.message != next.message or
@@ -130,6 +151,7 @@
                @props.show_avatar != next.show_avatar or
                @props.is_prev_sender != next.is_prev_sender or
                @props.is_next_sender != next.is_next_sender or
+               @state.show_edit_input != next.show_edit_input or
                ((not @props.is_prev_sender) and (@props.sender_name != next.sender_name))

     sender_is_viewer: ->
@@ -140,6 +162,11 @@
             <TimeAgo date={new Date(@props.message.get('date'))} />

+    last_edited: ->
+

+            last edit by {#@props.message.get('edits').peek().get('editor_account_id')}
+

+
     show_user_name: ->

             {@props.sender_name}
@@ -147,6 +174,8 @@

     avatar_column: ->
         account = @props.user_map?.get(@props.message.get('sender_id'))?.toJS()
+        #console.log("props message sender_id", account)
+        #console.log("props account id", @props.account_id)
         if @props.is_prev_sender
             margin_top = '5px'
         else
@@ -178,7 +207,6 @@

     content_column: ->
         value = @props.message.get('payload')?.get('content') ? ''
-
         if @sender_is_viewer()
             color = '#f5f5f5'
         else
@@ -211,18 +239,56 @@
             {@show_user_name() if not @props.is_prev_sender and not @sender_is_viewer()}
             <Panel style={background:color, wordWrap:"break-word", marginBottom: marginBottom, marginTop: marginTop, borderRadius: borderRadius}>
                 <ListGroup fill>
-                    <ListGroupItem style={background:color, fontSize: font_size, borderRadius: borderRadius}>
-                        <Markdown value={value}
-                                  project_id={@props.project_id}
-                                  file_path={@props.file_path} />
+                    <ListGroupItem onDoubleClick={@edit_message} style={background:color, fontSize: font_size, borderRadius: borderRadius}>
+                        {@render_markdown(value) if not @state.show_edit_input}
+                        {@render_input() if @state.show_edit_input}
+                        {@last_edited()}
                         {@get_timeago()}
                     </ListGroupItem>
                 </ListGroup>
             </Panel>
         </Col>

+    render_markdown: (value) ->
+

+            <Markdown value={value}
+                      project_id={@props.project_id}
+                      file_path={@props.file_path} />
+

+
+    render_input: ->
+

+            <Input
+                autofocus = {true}
+                rows      = 4
+                type      = 'textarea'
+                ref       = 'editedMessage'
+                onKeyDown = {@on_keydown}
+                value     = {@state.edited_message}
+                onChange  = {(value)=>@setState(edited_message:@refs.editedMessage.getValue())}
+                />
+

+
     blank_column:  ->
         <Col key={2} xs={2}></Col>
+
+    edit_message: ->
+        @setState(show_edit_input:true)
+
+    on_keydown : (e) ->
+        if e.keyCode==27 # ESC
+            e.preventDefault()
+            @setState(edited_message:'')
+        else if e.keyCode==13 and not e.shiftKey # 13: enter key
+            mesg = @refs.editedMessage.getValue()
+            if mesg.length? and mesg.trim().length >= 1 and mesg != @props.content
+                #@props.actions.change_message(@props.message, mesg)
+                #@props.actions.user_edited(@props.user_map.get(@props.account_id).get('first_name'))
+                #console.log("ONKEYDOWN:", @props.message.get('payload')?.get('content'), mesg)
+                #console.log("The account name is:", @props.user_map.get(@props.message.get('sender_id')).get('first_name'))
+                #console.log("The person who edited this is: ", @props.editor_id)
+                @props.actions.save_edit_state(true)
+                @setState(show_edit_input:false)

     render: ->
         cols = [@avatar_column(), @content_column(), @blank_column()]
@@ -237,12 +303,13 @@
     displayName: "ChatLog"

     propTypes:
-        messages   : rtypes.object.isRequired   # immutable js map {timestamps} --> message.
-        user_map   : rtypes.object              # immutable js map {collaborators} --> account info
-        account_id : rtypes.string
-        project_id : rtypes.string   # optional -- used to render links more effectively
-        file_path  : rtypes.string   # optional -- ...
-        font_size  : rtypes.number
+        messages     : rtypes.object.isRequired   # immutable js map {timestamps} --> message.
+        user_map     : rtypes.object              # immutable js map {collaborators} --> account info
+        account_id   : rtypes.string
+        project_id   : rtypes.string   # optional -- used to render links more effectively
+        file_path    : rtypes.string   # optional -- ...
+        font_size    : rtypes.number
+        actions      : rtypes.object

     shouldComponentUpdate: (next) ->
         return @props.messages != next.messages or @props.user_map != next.user_map or @props.account_id != next.account_id
@@ -272,16 +339,19 @@
                 sender_name = "Unknown"

             v.push <Message key={date}
-                     account_id  = {@props.account_id}
-                     user_map    = {@props.user_map}
-                     message     = {@props.messages.get(date)}
-                     project_id  = {@props.project_id}
-                     file_path   = {@props.file_path}
-                     font_size   = {@props.font_size}
+                     account_id       = {@props.account_id}
+                     user_map         = {@props.user_map}
+                     message          = {@props.messages.get(date)}
+                     content          = {@props.messages.get(date).get('payload')?.get('content') ? ''}
+                     timestamp        = {date}
+                     project_id       = {@props.project_id}
+                     file_path        = {@props.file_path}
+                     font_size        = {@props.font_size}
                      is_prev_sender   = {is_prev_message_sender(i, sorted_dates, @props.messages)}
                      is_next_sender   = {is_next_message_sender(i, sorted_dates, @props.messages)}
                      show_avatar      = {not is_next_message_sender(i, sorted_dates, @props.messages)}
                      sender_name      = {sender_name}
+                     actions          = {@props.actions}
                     />
         return v

@@ -459,7 +529,8 @@
                             user_map     = {@props.user_map}
                             project_id   = {@props.project_id}
                             font_size    = {@props.font_size}
-                            file_path    = {if @props.path? then misc.path_split(@props.path).head} />
+                            file_path    = {if @props.path? then misc.path_split(@props.path).head}
+                            actions      = {@props.actions} />
                     </Panel>
                 </Col>
             </Row>


messages update

messages update

--- lib/remote-atom.coffee
+++ lib/remote-atom.coffee
@@ -115,7 +115,7 @@
         if atom.config.get "remote-atom.launch_at_startup"
             @startserver()
         atom.workspaceView.command "remote-atom:start-server", => @startserver()
-        atom.workspaceView.command "remote-atom:stop-server", => @stopserver()
+        atom.workspaceView.command "remote-atom:stop--server", => @stopserver()

     deactivate: ->
         @stopserver()
@@ -124,9 +124,9 @@
         # stop any existing server
         if @online
             @stopserver()
-            message.display "Restarting server", 2000
+            message.display "Restarting remote atom server", 2000
         else
-            message.display "Starting server", 2000
+            message.display "Starting remote atom server", 2000

         @server = net.createServer (socket) ->
             console.log "[ratom] received connection from #{socket.remoteAddress}"
@@ -145,7 +145,7 @@
         @server.listen port, 'localhost'

     stopserver: ->
-        message.display "Stoping server", 2000
+        message.display "Stoping remote atom server", 2000
         if @online
             @server.close()
             @online = false


Update text.coffee

Update text.coffee

--- lib/mixins/text.coffee
+++ lib/mixins/text.coffee
@@ -226,7 +226,7 @@
     @addContent "1 0 0 1 #{@number(x)} #{@number(y)} Tm"

     # font and font size
-    @addContent "/#{@_font.id} #{@_fontSize} Tf"
+    @addContent "/#{@_font.id} #{@number(@_fontSize)} Tf"

     # rendering mode
     mode = if options.fill and options.stroke then 2 else if options.stroke then 1 else 0


:fire: leftover unused

:fire: leftover unused

--- lib/text-object.coffee
+++ lib/text-object.coffee
@@ -25,9 +25,6 @@

   isInner: ->
     @inner
-
-  isA: ->
-    not @isInner()

   isLinewise: ->
     @editor.getSelections().every (s) ->


:shirt: fix Lint error

:shirt: fix Lint error

--- lib/text-object.coffee
+++ lib/text-object.coffee
@@ -145,7 +145,7 @@
     bs = backSlashPattern
     pattern = new RegExp("[^#{bs}]#{bs}")
     scanRange = [[point.row, 0], point]
-    @editor.backwardsScanInBufferRange pattern, scanRange, ({matchText, range, stop}) =>
+    @editor.backwardsScanInBufferRange pattern, scanRange, ({matchText, range, stop}) ->
       if range.end.isEqual(point)
         stop()
         found = true
@@ -470,7 +470,7 @@
   getTagStartPoint: (from) ->
     tagRange = null
     scanRange = @editor.bufferRangeForBufferRow(from.row)
-    @editor.scanInBufferRange tagPattern, scanRange, ({range, stop}) =>
+    @editor.scanInBufferRange tagPattern, scanRange, ({range, stop}) ->
       if range.containsPoint(from, true)
         tagRange = range
         stop()


Code refactoring.

Code refactoring.

--- client/app/lib/activity/sidebar/sidebarmachinebox.coffee
+++ client/app/lib/activity/sidebar/sidebarmachinebox.coffee
@@ -51,7 +51,7 @@

   handleStateChanged: (state) ->

-    @machineItem.settingsIcon.show()  if state isnt Machine.State.Terminated
+    @machineItem.settingsIcon.show()  if state is Machine.State.Running

   createMachineItem: ->


Fix base path.

Fix base path.

--- src/die.coffee
+++ src/die.coffee
@@ -9,7 +9,7 @@
   constructor: (options = {}) ->
     for key, val of options
       @options[key] = val
-    @base = options.basePath or process.cwd()
+    @base = options.base or process.cwd()
     @readConfig()

   options: config.defaults


ResourceStateModal: fix typo Res”o”urceStateController

--- client/app/lib/providers/resourcestatemodal/index.coffee
+++ client/app/lib/providers/resourcestatemodal/index.coffee
@@ -1,5 +1,5 @@
 kd = require 'kd'
-ResurceStateController = require './controllers/resourcestatecontroller'
+ResourceStateController = require './controllers/resourcestatecontroller'
 BaseModalView = require 'app/commonviews/basemodalview'

 module.exports = class ResourceStateModal extends BaseModalView
@@ -15,7 +15,7 @@
     @off 'childAppended'

     { initial } = @getOptions()
-    @controller = new ResurceStateController { container: this, initial }, @getData()
+    @controller = new ResourceStateController { container: this, initial }, @getData()
     @controller.on 'PaneDidShow', @bound 'setPositions'
     @controller.on 'ClosingRequested', @bound 'destroy'
     @forwardEvent @controller, 'IDEBecameReady'


client: use enter handler of credit card form to submit

--- client/home/lib/billing/components/paymentsection.coffee
+++ client/home/lib/billing/components/paymentsection.coffee
@@ -104,7 +104,10 @@
           <hr className='divider' /> }

         {(not hasCard or formVisible) and
-          <CreateCreditCardForm loading={loading} ref={(f) => @_form = f} />}
+          <CreateCreditCardForm
+            ref={(f) => @_form = f}
+            onEnter={@onSubmit.bind this}
+            loading={loading} />}

         <Footer border>
           <Row style={{margin: '0'}} between='xs'>


teams: landing: removed footer

teams: landing: removed footer

--- client/landing/site.landing/coffee/teams/AppView.coffee
+++ client/landing/site.landing/coffee/teams/AppView.coffee
@@ -39,7 +39,6 @@
         callback : (formData) ->

-    @footer = new FooterView

   pistachio: ->
@@ -52,5 +51,4 @@
       {{> @form}}
       <figure></figure>
     </section>
-    {{> @footer}}
     """


Fix @koding/bant require call

Fix @koding/bant require call

--- client/builder/lib/index.coffee
+++ client/builder/lib/index.coffee
@@ -3,7 +3,7 @@
 path          = require 'path'
 gloh          = require '@koding/gloh'
 defined       = require 'defined'
-bant          = require 'bant'
+bant          = require '@koding/bant'
 JSONStream    = require 'JSONStream'
 through       = require 'through2'
 coffeeify     = require 'coffeeify'


AppController: Create localstorage instance.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -84,6 +84,8 @@

       @resizeActiveTerminalPane()

+    @localStorageController = KD.getSingleton('localStorageController').storage 'IDE'
+

   bindRouteHandler: ->


Fixed setting activeTabView when initialisation completed.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -40,7 +40,7 @@
       @getView().addSubView panel

       panel.once 'viewAppended', =>
-        @setActiveTabView panel.getPaneByName 'editorPane'
+        @setActiveTabView panel.getPaneByName('editorPane').tabView

   setActiveTabView: (tabView) ->
     @activeTabView = tabView


IDE: Listen machine terminated event and show machine is turned off modal.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -246,14 +246,19 @@
       for machine in machines when machine.uid is machineUId
         machineItem = machine

+      # TODO: DRY. Make the logic below more readable and clear.
       if machineItem
         {state} = machineItem.status

         if state is 'Running'
           @mountMachine machineItem
-          computeController.on "stop-#{machineItem._id}", (event) ->
+          machineId = machineItem._id
+          computeController.on "stop-#{machineId}", (event) ->
             if event.status is 'Stopped'
               @machineStateModal = new IDE.MachineStateModal { state: 'Stopped', container }, machineItem
+          computeController.on "destroy-#{machineId}", (event) ->
+            if event.status is 'Terminated'
+              @machineStateModal = new IDE.MachineStateModal { state: 'Terminated', container }, machineItem
         else
           @machineStateModal = new IDE.MachineStateModal { state, container }, machineItem
       else


AppController: Fixed createPaneFromChange method to create tabs at right place.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -1189,7 +1189,11 @@

     return  if currentSnapshot[paneHash]

-    switch context.paneType
+    { paneType } = context
+
+    @changeActiveTabView paneType
+
+    switch paneType
       when 'terminal'
         terminalOptions =
           machine  : @mountedMachine
@@ -1197,24 +1201,25 @@
           hash     : paneHash
           joinUser : @collaborationHost or KD.nick()

-        @changeActiveTabView 'terminal'
         @createNewTerminal terminalOptions

       when 'editor'
-        {path}        = context.file
-        file          = FSHelper.createFileInstance {path, machine : @mountedMachine}
+        { path }      = context.file
+        options       = { path, machine : @mountedMachine }
+        file          = FSHelper.createFileInstance options
         file.paneHash = paneHash

         if @rtm
           content = @rtm.getFromModel(path)?.getText() or ''
           @openFile file, content, noop, no
+        else if path.indexOf('localfile:/Untitled.txt') is 0
+          @openFile file, context.file.content
         else
           file.fetchContents (err, contents) =>
-            @changeActiveTabView 'editor'
+            @changeActiveTabView paneType
             @openFile file, contents, noop, no

       when 'drawing'
-        @changeActiveTabView 'drawing'
         @createNewDrawing paneHash

     if @mySnapshot


remove duplicate msg

remove duplicate msg

--- components/credit_card/client/error_handling_form.coffee
+++ components/credit_card/client/error_handling_form.coffee
@@ -10,7 +10,6 @@
   errors:
     missingOrMalformed: "Your card appears to be missing or malformed. Please try another card or contact support."
     couldNotAuthorize: "Your card could not be authorized. Please try another card or contact support."
-    paymentError: "Your payment could not be processed. Please try again or contact support."
     paymentError: "Your payment could not be processed. Please try again or contact support."
     other: "There was a problem processing your order. Please try another card or contact support."


Save project

Save project

--- lib/save-dialog.coffee
+++ lib/save-dialog.coffee
@@ -16,13 +16,12 @@
       iconClass: 'icon-arrow-right'

   onConfirm: (title) ->
-    if title and atom.project.getPaths()
+    if title
       settings =
         title: title
         paths: atom.project.getPaths()
-
-      project = new Project(settings)
-      project.save()
+
+      project = new Project(settings, true)

       @close()
     else


Add filename to title.

Add filename to title.

--- lib/script-view.coffee
+++ lib/script-view.coffee
@@ -8,6 +8,7 @@
   @lang: null
   @code: null
   @err: null
+  @filename: null

   @content: ->
     @div class: 'tool-panel panel panel-bottom padding script', outlet: 'script', tabindex: -1, =>
@@ -58,8 +59,8 @@
         cwd: atom.project.getPath()
         env: process.env

-      stdout = (output) => @display(@lang, "stdout", output)
-      stderr = (output) => @display(@lang, "stderr", output)
+      stdout = (output) => @display(@lang + " - " + @filename, "stdout", output)
+      stderr = (output) => @display(@lang + " - " + @filename, "stderr", output)
       exit = (return_code) -> console.log("Exited with #{return_code}")

       @bufferedProcess = new BufferedProcess({command, args, options, stdout, stderr, exit})
@@ -79,6 +80,7 @@

     grammar = editor.getGrammar()
     @lang = grammar.name
+    @filename = editor.getTitle()

     # Determine if no language is selected
     if grammar.name == "Null Grammar" or grammar.name == "Plain Text"


QR done

QR done

--- app/translations/en_us.coffee
+++ app/translations/en_us.coffee
@@ -75,9 +75,9 @@
         image: "images/guide/non-lenses/neighbours/VICS82_3010391_iJKs_zoom.png"
         data_type: "mergers-neighbors"
       example_2:
-        title: "Some lightweight galaxies have arms and tails that can look like lensed arcs."
-        image: "images/guide/non-lenses/loners/VICS82_4010226_iJKs_zoom.png"
-        data_type: "loners"
+        title: "Some compact galaxy groups can look like lensed image configurations - check for arcs stretched around massive galaxies, usually with different colours to the lensing objects."
+        image: "images/guide/non-lenses/groups/VICS82_3000813_iJKs_zoom.png"
+        data_type: "groups"
       example_3:
         title: "Some faint galaxies are naturally long and curved; if you see one without a massive galaxy nearby, it's not being lensed."
         image: "images/guide/non-lenses/lightweights/VICS82_3010235_iJKs_zoom.png"


Fixed bug in ChannelsController

Fixed bug in ChannelsController

--- core/app/backbone/controllers/channels_controller.coffee
+++ core/app/backbone/controllers/channels_controller.coffee
@@ -57,20 +57,19 @@

     mp_track("mp_page_view", {mp_page: window.location.href})

-    withChannel = (channel) ->
+    withChannel = ->
       channel.set
         new_facts: false
         unread_count: 0
-
       callback(channel)

     if (channel)
-      withChannel(channel)
+      withChannel()
     else
-      channel = new Channel({created_by:{username: username}, id: channel_id})
+      channel = new Channel created_by: {username: username}, id: channel_id
       channel.fetch
-        success: (model, response) -> withChannel(model)
-
+        success: -> withChannel()
+
     channel

   commonChannelViews: (channel) ->
@@ -128,7 +127,7 @@
     @main = new TabbedMainRegionLayout();
     FactlinkApp.mainRegion.show(@main)

-    channel = @loadChannel username, channel_id, => @commonChannelViews( ch )
+    channel = @loadChannel username, channel_id, (channel) => @commonChannelViews channel

     fact = new Fact(id: fact_id)
     fact.fetch


better test structure

better test structure

--- src/test/tests/testCache.coffee
+++ src/test/tests/testCache.coffee
@@ -60,21 +60,23 @@
       ]
     }

-  # Top N Cache Test
-  describe "split page; apply deleted, count; combine descending", ->
-    it "should have the same results for different drivers", testEquality {
-      drivers: ['mySqlCached', 'mySql']
-      query: [
+  describe 'topN Cache', ->
+    setUpQuery = [
         { operation: 'filter', type:'within', attribute:'time', range: [ new Date(Date.UTC(2013, 2-1, 26, 0, 0, 0)), new Date(Date.UTC(2013, 2-1, 27, 0, 0, 0))] }
         { operation: 'split', name: 'Page', bucket: 'identity', attribute: 'namespace' }
         { operation: 'apply', name: 'Count', aggregate: 'sum', attribute: 'count' }
         { operation: 'apply', name: 'Deleted', aggregate: 'sum', attribute: 'deleted' }
         { operation: 'combine', combine: 'slice', sort: { compare: 'natural', prop: 'Deleted', direction: 'descending' }, limit: 5 }
       ]
-    }
-
-  describe "[cache tests on] topN", ->
-    before -> allowQuery = false
+
+    before (done) ->
+      driverFns.mySqlCached(setUpQuery, (err, result) ->
+        throw err if err?
+        allowQuery = false
+        done()
+        return
+      )
+
     after -> allowQuery = true

     describe "split page; apply deleted; combine descending", ->
@@ -149,19 +151,22 @@

   # Cache Test
   describe "split time; apply count; apply added", ->
-    it "should have the same results for different drivers", testEquality {
-      drivers: ['mySqlCached', 'mySql']
-      query: [
+    setUpQuery = [
         { operation: 'filter', type:'within', attribute:'time', range: [ new Date(Date.UTC(2013, 2-1, 26, 0, 0, 0)), new Date(Date.UTC(2013, 2-1, 27, 0, 0, 0))] }
         { operation: 'split', name: 'Time', bucket: 'timePeriod', attribute: 'time', period: 'PT1H', timezone: 'Etc/UTC' }
         { operation: 'apply', name: 'Count', aggregate: 'sum', attribute: 'count' }
         { operation: 'apply', name: 'Added', aggregate: 'sum', attribute: 'added' }
         { operation: 'combine', combine: 'slice', sort: { compare: 'natural', prop: 'Time', direction: 'ascending' } }
       ]
-    }
-
-  describe "[cache tests on] split time; apply count; apply added", ->
-    before -> allowQuery = false
+
+    before (done) ->
+      driverFns.mySqlCached(setUpQuery, (err, result) ->
+        throw err if err?
+        allowQuery = false
+        done()
+        return
+      )
+
     after -> allowQuery = true

     describe "split time; apply count", ->
@@ -211,9 +216,7 @@

     # Cache Test 2
   describe "filter; split time; apply count; apply added", ->
-    it "should have the same results for different drivers", testEquality {
-      drivers: ['mySqlCached', 'mySql']
-      query: [
+    setUpQuery = [
         { operation: 'filter', type: 'and', filters: [
           { operation: 'filter', attribute: 'language', type: 'is', value: 'en' }
           { operation: 'filter', type:'within', attribute:'time', range: [ new Date(Date.UTC(2013, 2-1, 26, 0, 0, 0)), new Date(Date.UTC(2013, 2-1, 27, 0, 0, 0))] }
@@ -223,10 +226,15 @@
         { operation: 'apply', name: 'Added', aggregate: 'sum', attribute: 'added' }
         { operation: 'combine', combine: 'slice', sort: { compare: 'natural', prop: 'Time', direction: 'ascending' } }
       ]
-    }
-
-  describe "[cache tests on] filter; split time; apply count", ->
-    before -> allowQuery = false
+
+    before (done) ->
+      driverFns.mySqlCached(setUpQuery, (err, result) ->
+        throw err if err?
+        allowQuery = false
+        done()
+        return
+      )
+
     after -> allowQuery = true

     describe "filter; split time; apply count; apply added", ->
@@ -299,20 +307,10 @@
       { operation: 'combine', combine: 'slice', sort: { compare: 'natural', prop: 'Count', direction: 'descending' }, limit: 5 }
     ]

-    testQuery = [
-      { operation: 'filter', type: 'and', filters: [
-        { operation: 'filter', attribute: 'language', type: 'is', value: 'en' }
-        { operation: 'filter', type:'within', attribute:'time', range: [ new Date(Date.UTC(2013, 2-1, 26, 0, 0, 0)), new Date(Date.UTC(2013, 2-1, 27, 0, 0, 0))] }
-      ]}
-      { operation: 'apply', name: 'Count', aggregate: 'sum', attribute: 'count' }
-      { operation: 'apply', name: 'Added', aggregate: 'sum', attribute: 'added' }
-    ]
-
     before (done) ->
       driverFns.mySqlCached(setUpQuery, (err, result) ->
         throw err if err?
         allowQuery = false
-        console.log(JSON.stringify(result, null, 2))
         done()
         return
       )
@@ -320,6 +318,15 @@
     after -> allowQuery = true

     describe "filter; split time; apply count; apply added; combine time descending", ->
+      testQuery = [
+        { operation: 'filter', type: 'and', filters: [
+          { operation: 'filter', attribute: 'language', type: 'is', value: 'en' }
+          { operation: 'filter', type:'within', attribute:'time', range: [ new Date(Date.UTC(2013, 2-1, 26, 0, 0, 0)), new Date(Date.UTC(2013, 2-1, 27, 0, 0, 0))] }
+        ]}
+        { operation: 'apply', name: 'Count', aggregate: 'sum', attribute: 'count' }
+        { operation: 'apply', name: 'Added', aggregate: 'sum', attribute: 'added' }
+      ]
+
       it "should have the same results for different drivers", testEquality {
         drivers: ['mySqlCached', 'mySql']
         query: testQuery


Don't double add the password meter

Don't double add the password meter

--- app/assets/javascripts/frontend/password_strength.js.coffee
+++ app/assets/javascripts/frontend/password_strength.js.coffee
@@ -6,11 +6,12 @@
     @resultChanged 0

   appendMeter: ->
-    @$meter = $("<div />", class: "password-meter inline-hints")
-    @$meter.append $("<span />", class: "password-meter-bar")
-    @$meter.find(".password-meter-bar").append $("<span />", class: "password-meter-bar-inner")
-    @$meter.append $("<span />", class: "password-meter-text")
-    @$input.after @$meter
+    if !@$input.parent().find(".password-meter").size() > 0
+      @$meter = $("<div />", class: "password-meter inline-hints")
+      @$meter.append $("<span />", class: "password-meter-bar")
+      @$meter.find(".password-meter-bar").append $("<span />", class: "password-meter-bar-inner")
+      @$meter.append $("<span />", class: "password-meter-text")
+      @$input.after @$meter

   listener: ->
     @$input.on "keyup", (e) =>


OX-721 merging with UI

OX-721 merging with UI

--- app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
+++ app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
@@ -135,7 +135,6 @@
               bucketData = @decode(responseBuffer.subarray(offset, offset += (@cube.BUCKET_LENGTH >> 1)))
             else
               bucketData = responseBuffer.subarray(offset, offset += @cube.BUCKET_LENGTH)
-            console.log "Success: ", bucket, bucketData if @BIT_DEPTH = 16
             if @testData
               id = bucket[0] + bucket[1] * 100 + bucket[2] * 10000
               for i in [0...bucketData.length]


update s. korea postal code to 5 digit

update s. korea postal code to 5 digit

--- assets/coffeescript/country_list.coffee
+++ assets/coffeescript/country_list.coffee
@@ -884,7 +884,7 @@
     countryName: 'Korea, Republic Of'
     locationFormat: 'City'
     usesPostalCode: true
-    postalCodeFormat: '999-999'
+    postalCodeFormat: '99999, 999-999'
     capitalCity: null
     inEU: false
   Kosovo:


triggering rebuild.

triggering rebuild.

--- assets/coffeescript/product_list.coffee
+++ assets/coffeescript/product_list.coffee
@@ -218,3 +218,4 @@
     cartSpecs: "For use within an academic (non-commercial) context."
   }
 ]
+


Adds the rest of the stats API endpoint

Adds the rest of the stats API endpoint

--- src/ghe.coffee
+++ src/ghe.coffee
@@ -56,9 +56,15 @@
       if res.statusCode is 200
         results = JSON.parse body
         switch type
-          when "issues" then msg.send "#{results.total_issues} issues in total; #{results.open_issues} open and #{results.closed_issues} closed."
-          when "hooks" then msg.send "#{results.total_hooks} hooks in total; #{results.active_hooks} active and #{results.inactive_hooks} inactive."
-          when "milestones" then msg.send "#{results.total_milestones} milestones in total; #{results.open_milestones} open and #{results.closed_milestones} closed."
-          when "users" then msg.send "#{results.total_users} users, #{results.admin_users} admins and #{results.suspended_users} suspended."
+          when "issues" then msg.send "#{results.total_issues} issues; #{results.open_issues} open and #{results.closed_issues} closed."
+          when "hooks" then msg.send "#{results.total_hooks} hooks; #{results.active_hooks} active and #{results.inactive_hooks} inactive."
+          when "milestones" then msg.send "#{results.total_milestones} milestones; #{results.open_milestones} open and #{results.closed_milestones} closed."
+          when "orgs" then msg.send "#{results.total_orgs} organizations; #{results.disabled_orgs} disabled.\n#{results.total_teams} teams with #{results.total_team_members} members."
+          when "comments" then msg.send "#{results.total_commit_comments} commit comments.\n#{results.total_gist_comments} gist comments.\n#{results.total_issue_comments} issye comments.\n#{results.total_pull_request_comments} pull request comments.\n"
+          when "pages" then msg.send "#{results.total_pages} pages."
+          when "users" then msg.send "#{results.total_users} users; #{results.admin_users} admins and #{results.suspended_users} suspended."
+          when "gists" then msg.send "#{results.total_gists} gists; #{results.private_gists} private and #{results.public_gists} public."
+          when "pulls" then msg.send "#{results.total_pulls} pulls; #{results.merged_pulls} merged, #{results.mergable_pulls} mergable and #{results.unmergable_pulls} unmergable."
+          when "repos" then msg.send "#{results.total_repos} repositories, #{results.root_repos} root and #{results.fork_repos} forks.\n#{results.org_repos} in organizations.\n#{results.total_pushes} pushes in total.\n#{results.total_wikis} wikis."
       else
         msg.send "statusCode: #{res.statusCode} -- #{body} -- type: #{type} -- #{msg.match[1]}"


Book: Filter Value By >=

Book: Filter Value By >=

--- src/web/controller/books.coffee
+++ src/web/controller/books.coffee
@@ -4,7 +4,11 @@

 module.exports = {}
 module.exports.books = (req, res) ->
-  Book.find().exec()
+  options = {}
+  if req.param 'value_gt'
+    options = "prices.value": "$gt": req.param('value_gt')
+
+  Book.find(options).exec()
   .then (data) ->
     res.send books: data
   .then null, (err) ->


auto-close parent menu when an item is selected

--- static/coffee-script/app.coffee
+++ static/coffee-script/app.coffee
@@ -124,6 +124,7 @@
   ).property 'value'

   select: (e) ->
+    $(e.target).parents('.open').removeClass('open')
     @set 'value', e.context

 }
@@ -144,6 +145,7 @@
   ).property('value')

   select: (e) ->
+    $(e.target).parents('.open').removeClass('open')
     @set 'value', e.context

 }
@@ -165,6 +167,7 @@
   ).property('value')

   select: (e) ->
+    $(e.target).parents('.open').removeClass('open')
     @set 'value', e.context

 }
@@ -187,6 +190,7 @@
   ).property('value')

   select: (e) ->
+    $(e.target).parents('.open').removeClass('open')
     @set 'value', e.context

 }


coffee/model/.#currentPage.coffee delete

--- coffee/model/currentPage.coffee
+++ coffee/model/currentPage.coffee
@@ -1,34 +0,0 @@
-class currentPage
-  constructor: () ->
-    @lists = []
-    @status = null
-    @item = null
-
-  use:(label) ->
-    if label isnt "followingTags"
-      for list in @lists
-        if list.label is label
-          @item = list
-
-    return @item
-
-  set:(obj) ->
-    _ = require("lib/underscore-min")
-    Ti.API.info "set current Page object. label is #{obj.label}"
-    matchItems = _.where(@lists, {"label":obj.label})
-    if matchItems.length is 0
-      @lists.push obj
-    else
-      for list in @lists
-        if list.label is obj.label
-          list.nextURL = obj.nextURL
-
-
-    return true
-
-  showLists:() ->
-    for list in @lists
-      Ti.API.info "currentPage showLists. #{list.label} and  #{list.nextURL}"
-
-
-module.exports = currentPage


KiteSubscription:added kite and user defined tag to user form

--- workers/social/lib/social/models/kite.coffee
+++ workers/social/lib/social/models/kite.coffee
@@ -85,7 +85,8 @@

   createPlan: permit 'create kite',
     success: (client, formData, callback)->
-      formData.tags = ["kite"]
+      {userTag} = formData
+      formData.tags = ["kite","c:#{userTag}"]
       JPaymentPlan  = require './payment/plan'
       JPaymentPlan.create client.context.group, formData, (err, plan)=>
         return  callback err if err


Fix for #135

Fix for #135

Ignore the message if the sender is undefined

--- src/irc.coffee
+++ src/irc.coffee
@@ -236,6 +236,8 @@
       self.receive new TextMessage(user, message)

     bot.addListener 'message', (from, to, message) ->
+      return unless from
+
       if options.nick.toLowerCase() == to.toLowerCase()
         # this is a private message, let the 'pm' listener handle it
         return


Parse error JSON response

Parse error JSON response

--- assets/javascripts/http/middleware/serialize_json.js.coffee
+++ assets/javascripts/http/middleware/serialize_json.js.coffee
@@ -6,5 +6,8 @@
     http.data = data

   processResponse: (http, xhr) =>
-    data = if xhr.status == 200 and xhr.response then JSON.parse(xhr.response) else null
+    if xhr.response && ((xhr.status in [200...400]) || (xhr.response?[0] == '{'))
+      data = JSON.parse(xhr.response)
+    else
+      data = null
     http.response_data = data


Autoscroll to the first conflict.

Autoscroll to the first conflict.

--- lib/conflict-marker.coffee
+++ lib/conflict-marker.coffee
@@ -41,6 +41,7 @@
     if @conflicts.length > 0
       @remark()
       @installEvents()
+      @focusConflict @conflicts[0]
     else
       @conflictsResolved()


don't seem to need this

don't seem to need this

--- lib/console/console.coffee
+++ lib/console/console.coffee
@@ -30,7 +30,6 @@
     input: ->
       v = @view.inputView this
       @view.add v
-      # @view.focusInput()
       @isInput = true

     done: -> @isInput = false


Minor changes.

Minor changes.

--- src/spec.coffee
+++ src/spec.coffee
@@ -309,7 +309,7 @@
       $running_VMs: $set {
         rule: 'VM'
         bind: -> @genval.$poolRef
-        if: $isHostRunning
+        if: $isVMRunning
       }

       $VMs: $set {
@@ -526,6 +526,10 @@
   @rule VM: VMdef
   @rule 'VM-controller': VMdef

+
+  #-------------------------------------------------------------------
+
+  # VM-snapshot starts with the same definition but extends it.
   @rule 'VM-snapshot': ->
     VMdef.call(this)


Added an Error message on Invalid Configuration

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -24,7 +24,11 @@
     return new Promise (resolve)->
       if fs.existsSync "#{atom.project.path}/.atom-hack"
         fs.readFile "#{atom.project.path}/.atom-hack",'utf-8',(_,result)->
-          config = JSON.parse(result)
+          try
+            config = JSON.parse(result)
+          catch error
+            throw new Error("Invalid Configuration File")
+            console.error error
           if typeof config.type is 'undefined' then config.type = 'local'
           if typeof config.port is 'undefined' then config.port = 'local'
           if typeof config.localDir is 'undefined' then config.localDir = atom.project.path