Don't prefetch region cache while it's already prefetching

--- lib/client.coffee
+++ lib/client.coffee
@@ -45,8 +45,10 @@
 		@rpcTimeout = 30000
 		@pingTimeout = 30000
 		@zkStart = "init"
+		@_zkStartListener = []
 		@rootRegionZKPath = options.rootRegionZKPath or '/meta-region-server'
-		@prefetchRegionCacheList = {}
+		@_prefetchRegionCacheList = {}
+		@_prefetchRegionCacheListInProgress = {}
 		@ensureZookeeperTrackers (err) =>
 			@emit 'error', err if err

@@ -91,9 +93,15 @@

 	ensureZookeeperTrackers: (cb) =>
 		return cb() if @zkStart is "done"
-		@once "ready", cb
+		@_zkStartListener.push cb
 		return if @zkStart is "starting"
 		@zkStart = "starting"
+
+		@once 'ready', () ->
+			loop
+				callback = @_zkStartListener.pop()
+				return unless callback
+				callback()

 		@zk.once "connected", (err) =>
 			if err
@@ -556,9 +564,19 @@

 	prefetchRegionCache: (table, cb) =>
-		return cb() if @prefetchRegionCacheList[table] or utils.bufferCompare(table, hconstants.META_TABLE_NAME) is 0
+		return cb() if @_prefetchRegionCacheList[table] or utils.bufferCompare(table, hconstants.META_TABLE_NAME) is 0
 		debug "prefetchRegionCache for table: #{table}"

+		prefetchEvent = "prefetch-#{table}"
+		return @_prefetchRegionCacheListInProgress[prefetchEvent].push cb if @_prefetchRegionCacheListInProgress[prefetchEvent]
+
+		@once prefetchEvent, () =>
+			loop
+				callback = @_prefetchRegionCacheListInProgress[prefetchEvent].pop()
+				return unless callback
+				callback()
+
+		@_prefetchRegionCacheListInProgress[prefetchEvent] = []
 		startRow = @createRegionName table, null, hconstants.ZEROS, no
 		stopRow = @createRegionName utils.bufferIncrement(table), null, hconstants.ZEROS, no

@@ -572,15 +590,17 @@
 				return done err if err

 				unless regionRow.row
-					@prefetchRegionCacheList[table] = yes
+					@_prefetchRegionCacheList[table] = yes
 					work = no
 					return done()

 				region = @_parseRegionInfo regionRow
 				@cacheLocation table, region if region
 				done()
-		, (err) ->
+		, (err) =>
 			console.log err if err
+			@emit prefetchEvent
+			delete @_prefetchRegionCacheListInProgress[prefetchEvent]
 			cb()

@@ -665,6 +685,6 @@
 			for regionName, region of regions
 				if region.server.toString() is serverName
 					delete @cachedRegionLocations[table]
-					@prefetchRegionCacheList[table] = no
-
-
+					@_prefetchRegionCacheList[table] = no
+
+


Add snippets for cout/cerr with endl

Add snippets for cout/cerr with endl

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -100,10 +100,18 @@
     'prefix': 'cout'
     'body': "cout << $1;"

+  'cout endl':
+    'prefix': 'coute'
+    'body': "cout << $1 << endl;"
+
   'cerr':
     'prefix': 'cerr'
     'body': "cerr << $1;"

+  'cerr endl':
+    'prefix': 'cerre'
+    'body': "cerr << $1 << endl;"
+
   'operator (=) ':
     'prefix': 'op='
     'body': "${1:Class}& operator=(const ${1:Class}& other)$2"


Add kv snippet for JSON

Add kv snippet for JSON

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -1223,3 +1223,11 @@
   'Key Binding (no selector)':
     'prefix': 'keyns'
     'body': '\'${1:keystroke}\': \'${2:command}\''
+
+# JSON
+
+'.source.json':
+
+  'key-value pair':
+    'prefix': 'kv'
+    'body': '\"${1:key}\": ${2:null},$3'


a comment

a comment

--- backend/app.coffee
+++ backend/app.coffee
@@ -51,7 +51,7 @@
 # Delete user
 app.delete '/api/users/:id', routes.deleteUser

-# check whether
+# Check whether user exists or not
 app.get '/api/checkuser/:username', routes.checkUser

 console.log "API is starting on port #{PORT}"


Removing extra country layer.

Removing extra country layer.

--- manualMerge.coffee
+++ manualMerge.coffee
@@ -5,9 +5,9 @@
 vectorMap = JSON.parse(fs.readFileSync "merged_subunits.topo.json")

 vectorMap.objects.countries2.geometries.forEach (d) -> vectorMap.objects.countries.geometries.push d
+delete vectorMap.objects.countries2

 output = JSON.stringify(vectorMap,null,0)
 fs.writeFileSync('worldMap.topo.json', output, 'utf8')
-
 puts = (error, stdout, stderr) -> sys.puts(stdout)
 exec("cp worldMap.topo.json testbed/data/maps/", puts);


slow down cycler and reverse direction

slow down cycler and reverse direction

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -369,10 +369,11 @@
       @frameSlider.attr 'disabled', false
     else
       images = document.querySelectorAll(".frame-image")
-      sources = (img.getAttribute('href') for img in images)
+      sources = (img.getAttribute('href') for img in images).reverse()
       @cc = new ChannelCycler(sources)
       @subjectContainer.append(@cc.canvas)
       @cc.start()
+      @cc.period = 600
       @playButton.attr 'disabled', true
       @frameSlider.attr 'disabled', true
     @cycling = !@cycling


Add support for language-markdown package

The [language-markdown](https://github.com/burodepeper/language-markdown) package promises to add support for more than just GFM. This includes the new CommonMark syntax, as well as front-matters, and a bunch of other goodies. As such, this package should apply to it, too.

--- lib/config.coffee
+++ lib/config.coffee
@@ -134,6 +134,7 @@
     # filetypes markdown-writer commands apply
     grammars: [
       'source.gfm'
+      'text.md'
       'source.litcoffee'
       'text.plain'
       'text.plain.null-grammar'


Update translations.coffee

Update translations.coffee

--- addon/locales/zh-tw/translations.coffee
+++ addon/locales/zh-tw/translations.coffee
@@ -209,12 +209,12 @@
   "plan_delivery":
     "title" : "安排遞送服務"
     "fastest" :
-      "include_coupon_discount": "Includes ${{cost}} promotional discount."
-      "promo_discount": "Promotional discount"
+      "include_coupon_discount": "加入 ${{cost}} 優惠折扣。"
+      "promo_discount": "優惠折扣"
       "title" : "最快速"
       "info" : "<br/>方便快捷，推介使用！"
       "cost" : "費用由港元${{cost}}起"
-      "discount_cost": "From ${{cost}}"
+      "discount_cost": "由 ${{cost}} 起"
       "booking_button" : "預約時間"
       "learn_more": "了解詳情"
       "gogovan_charges": "預約GoGoVan"


Import components via globbing

Import components via globbing


Add space to gogovan option text

Add space to gogovan option text

--- addon/locales/zh-tw/translations.coffee
+++ addon/locales/zh-tw/translations.coffee
@@ -299,7 +299,7 @@
       "porterage" : "需協助搬運（需與司機商量）"
       "longer_goods": "Goods longer than 6ft (183cm)"
       "longer_goods_6ft": "longer than 6ft +$10"
-      "longer_goods_6ft_tall": "longer than 6ft &taller than 2ft(60cm) +$20"
+      "longer_goods_6ft_tall": "longer than 6ft & taller than 2ft(60cm) +$20"
       "extra_time_charge" : "額外時間（已包括15分鐘搬運時間，超過15分鐘可能需要額外收費）"
       "get_quote" : "報價"
       "date": "日期"


Fix article bug

Fix article bug

--- app/api/article.coffee
+++ app/api/article.coffee
@@ -110,7 +110,8 @@
           article.voteEntries = voteEntries
           return article
       else
-        throw new Error('voteEntries is not an array')
+        # throw new Error('voteEntries is not an array')
+        console.log 'emptyyy'
     else if article.type == 1
       # 해보고 싶어요
       # Add voteEntry with predefined data.


more permissive arguments for select, include, values

--- lib/cursor.coffee
+++ lib/cursor.coffee
@@ -29,23 +29,23 @@
   limit: (limit) -> @_cursor.$limit = limit; return @
   sort: (sort) -> @_cursor.$sort = sort; return @

-  whiteList: (keys) ->
-    keys = [keys] unless _.isArray(keys)
+  whiteList: (args) ->
+    keys = _.flatten(arguments)
     @_cursor.$white_list = if @_cursor.$white_list then _.intersection(@_cursor.$white_list, keys) else keys
     return @

-  select: (keys) ->
-    keys = [keys] unless _.isArray(keys)
+  select: (args) ->
+    keys = _.flatten(arguments)
     @_cursor.$select = if @_cursor.$select then _.intersection(@_cursor.$select, keys) else keys
     return @

-  include: (keys) ->
-    keys = [keys] unless _.isArray(keys)
+  include: (args) ->
+    keys = _.flatten(arguments)
     @_cursor.$include = if @_cursor.$include then _.intersection(@_cursor.$include, keys) else keys
     return @

-  values: (keys) ->
-    keys = [keys] unless _.isArray(keys)
+  values: (args) ->
+    keys = _.flatten(arguments)
     @_cursor.$values = if @_cursor.$values then _.intersection(@_cursor.$values, keys) else keys
     return @


slightly less confusing formatting

slightly less confusing formatting

--- app/coffee/game.coffee
+++ app/coffee/game.coffee
@@ -242,8 +242,8 @@
               then "placement #{@props.hoverColor}" \
               else ""

-    makermaker = (kind, className) -> (i, j) ->
-      React.DOM.div {className: className, key: kind}
+    makermaker = (kind, className) ->
+      (i, j) -> React.DOM.div {className: className, key: kind}
     makers = {}
     for own kind, className of classNames
       makers[kind] = makermaker(kind, className)
@@ -352,22 +352,22 @@
     subDivs = [div {key: "blackpris"},
                    ["Black prisoners: ",
                     span {key: 2, className: "prisoners black"},
-                    prisoners.black],
+                         prisoners.black],
                div {key: "whitepris"},
                    ["White prisoners: ",
                     span {key: 2, className: "prisoners white"},
-                    prisoners.white]
+                         prisoners.white]
               ]

     if scores?
       subDivs.push div {key: "blackscore"},
                        ["Black score: ",
                         span {key: 2, className: "score black"},
-                        scores.black]
+                             scores.black]
       subDivs.push div {key: "whitescore"},
                        ["White score: ",
                         span {key: 2, className: "score white"},
-                        scores.white]
+                             scores.white]

     div {className: "score_block"}, subDivs


Fix entity event playing when no handlers are set

--- lib/entity.coffee
+++ lib/entity.coffee
@@ -33,7 +33,7 @@

     @constructor._checkDependencies()
     eventHandlers = Entity.eventHandlers[event.name] || []
-
+    return callback null if eventHandlers.length is 0
     pending = 0
     errors = []
     for eventHandler in eventHandlers


Add /Premium route

Add /Premium route

--- app/core/Router.coffee
+++ app/core/Router.coffee
@@ -24,8 +24,7 @@
       return @routeDirectly('HomeView', [])

     'about': go('AboutView')
-    'premium': go('PremiumFeaturesView')
-
+
     'account': go('account/MainAccountView')
     'account/settings': go('account/AccountSettingsRootView')
     'account/unsubscribe': go('account/UnsubscribeView')
@@ -152,6 +151,9 @@
     'play/spectate/:levelID': go('play/SpectateView')
     'play/:map': go('play/CampaignView', { redirectStudents: true, redirectTeachers: true })

+    'premium': go('PremiumFeaturesView')
+    'Premium': go('PremiumFeaturesView')
+
     'preview': go('HomeView')

     'privacy': go('PrivacyView')


Add remUnusedLines to generic error

Add remUnusedLines to generic error

--- lib/errors.coffee
+++ lib/errors.coffee
@@ -172,6 +172,7 @@
         [_, _, lineNum] = lines.shift().match(at_re)
         ar = /^------>\s\x1b\[32m([^\x1b]*)\x1b\[33m(\u23CF)\x1b\[31m([^\x1b]*)/
         [_, green, _, red] = lines.shift().match(ar)
+        lines = remUnusedLines(lines)
         colstart = green.length - 1
         colend = colstart + red.length + 2
         {


Unit to time signatures #169

Unit to time signatures #169

--- dl/test/zcrypto.coffee
+++ dl/test/zcrypto.coffee
@@ -3,6 +3,8 @@
 Aes = require '../src/ecc/aes'
 PrivateKey = require '../src/ecc/key_private'
 PublicKey = require '../src/ecc/key_public'
+Signature = require '../src/ecc/signature'
+
 Long = require('../src/common/bytebuffer').Long

 secureRandom = require '../src/common/secureRandom'
@@ -12,6 +14,12 @@
 th = require './test_helper'

 describe "crypto", ->
+
+    it "sign fast", ->
+        @timeout(10000)
+        private_key = PrivateKey.fromSeed "1"
+        for i in [0...10] by 1
+            Signature.signBuffer (new Buffer i), private_key

     it "memo encryption", ->
         sender = PrivateKey.fromSeed "1"


Remove full view if we leave the dataexplorer

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -817,7 +817,8 @@
             $('.dataexplorer_container').css 'margin', '0px 0px 0px 20px'
             $('.change_size').val 'Smaller view'

-       destroy: =>
+        destroy: =>
+            @display_normal()
             @input_query.destroy()
             @data_container.destroy()
             window.conn.close()


Replace existential operator with 'isnt undefined' in the callback called by next(). Related to issue #109

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -691,7 +691,8 @@
                 return false

             # if it's a valid result and we have not reach the maximum of results displayed
-            if data? and  @current_results.length < @limit
+            # A valid result can be null, so we don't use the coffescript's existential operator
+            if data isnt undefined and  @current_results.length < @limit
                 @current_results.push data
                 return true
             else
@@ -715,7 +716,7 @@

                 @results_view.render_metadata metadata

-                if data? #there is nore data
+                if data isnt undefined #there is nore data
                     @skip_value += @current_results.length
                     @current_results = []
                     @current_results.push data
@@ -752,7 +753,7 @@
                     return false

                 # if it's a valid result and we have not reach the maximum of results displayed
-                if data? and  @current_results.length < @limit
+                if data isnt undefined and  @current_results.length < @limit
                     @current_results.push data
                     return true
                 else
@@ -775,7 +776,7 @@

                     @results_view.render_metadata metadata

-                    if data? #there is nore data
+                    if data isnt undefined #there is nore data
                         @skip_value += @current_results.length
                         @current_results = []
                         @current_results.push data


Code in JavaScript!

Code in JavaScript!

--- minimongoid.coffee
+++ minimongoid.coffee
@@ -30,12 +30,10 @@
       @id = null

   @mongoize: (attributes) ->
-    taken = {}
     for name, value of attributes
-      continue if name in ['_id', '_type']
-      taken[name] = value
+      delete attributes[name] if name in ['_id', '_type']

-    taken
+    attributes

   @demongoize: @mongoize


Update UI on atom command

Update UI on atom command

--- lib/changes/changes-element.coffee
+++ lib/changes/changes-element.coffee
@@ -35,6 +35,9 @@
     uri: @getURI()
     width: @width()

+  update: ->
+    @model.updateRepository()
+
   # Some UI actions need to focus the StatusListElement after completion. The
   # components are loosely coupled so we'll use this view as a DOM event bus.
   # XXX this stuff should get removed!


KDFramework: event emitter comments

KDFramework: event emitter comments

--- client/Framework/core/KDEventEmitter.coffee
+++ client/Framework/core/KDEventEmitter.coffee
@@ -6,7 +6,7 @@

 class KDEventEmitter

-  # listeners will be put inside @KDEventEmitterEvents[className]
+  # static listeners will be put here
   _e = {}

   _registerEvent = (registry, eventName, callback)->
@@ -49,11 +49,8 @@
     #{event.replace(/\./g,'\\.').replace(/\*/g, '((?:\\w+\\.?)*)')}
     $///

-  #
   # STATIC METHODS
-  #####################
-  # user is able to do ClassName.on .emit
-  #
+  # to enable ClassName.on or ClassName.emit

   @emit: ->
     # slice the arguments, 1st argument is the event name,
@@ -73,13 +70,19 @@
     _off _e, eventName, callback
     return this

+  # INSTANCE METHODS
+  # to enable anInstance.on or anInstance.emit (anInstance being new ClassName)
+
   constructor:->
+    # static listeners will be put here
     @_e = {}

   emit:(eventName, args...)->
     @_e[eventName] ?= []

     listenerStack = []
+
+    # event wildcards are disabled until implemented in a performant way

     # for own eventToBeFired of @_e
     #   continue if eventToBeFired is eventName
@@ -88,9 +91,7 @@
     #     listenerStack = listenerStack.concat @_e[eventToBeFired].slice(0)

     listenerStack = listenerStack.concat @_e[eventName].slice(0)
-
-    listenerStack.forEach (listener)=>
-      listener.apply @, args
+    listenerStack.forEach (listener)=> listener.apply @, args

   on  :(eventName, callback) -> _on  @_e, eventName, callback
   off :(eventName, callback) -> _off @_e, eventName, callback


vm: fetch VMs by invoking `JVM::fetchVmsByContext` from `VirtualizationController#fetchVMs`

--- client/Main/VirtualizationController.coffee
+++ client/Main/VirtualizationController.coffee
@@ -221,7 +221,7 @@

     return  if not force and (waiting.push callback) > 1

-    KD.remote.api.JVM.fetchVms (err, vms)=>
+    KD.remote.api.JVM.fetchVmsByContext (err, vms)=>
       @vms = vms  unless err
       if force
       then callback err, vms


Activity: threshold throttle times reduced

--- client/Social/Activity/AppController.coffee
+++ client/Social/Activity/AppController.coffee
@@ -27,9 +27,8 @@

     @appStorage = appStorageController.storage 'Activity', '2.0'

-    warn 'dock.getView().show()'
-
-    @on 'LazyLoadThresholdReached', @getView().bound 'lazyLoadThresholdReached'
+    @on 'LazyLoadThresholdReached',    KD.utils.throttle 200, @getView().bound 'lazyLoadThresholdReached'
+    @on 'TopLazyLoadThresholdReached', KD.utils.throttle 200, @getView().bound 'topLazyLoadThresholdReached'

   post: (options = {}, callback = noop) ->


Missed one dependency for json-schema

Missed one dependency for json-schema

--- lib/plunks.coffee
+++ lib/plunks.coffee
@@ -1,4 +1,4 @@
-schema = require("json-schema")
+schema = require("./json-schema")
 mime = require("mime")
 _ = require("underscore")._


add alien to congrat modal

add alien to congrat modal

--- client/home/lib/welcome/welcomemodal.coffee
+++ client/home/lib/welcome/welcomemodal.coffee
@@ -35,6 +35,11 @@
       cssClass : 'Congratulation-modal'
       content : "
Congratulations. You have completed all items on your onboarding list.
"

+
+    kd.View::addSubView.call modal, new kd.CustomHTMLView
+      cssClass : 'alien'
+
+
     modal.addSubView new kd.CustomHTMLView
       cssClass : 'image-wrapper'


hackity hack

hackity hack

--- client/landing/app/MainApp/fs/fsitem.coffee
+++ client/landing/app/MainApp/fs/fsitem.coffee
@@ -123,11 +123,10 @@
     for own key, value of options
       @[key] = value
     super
-    console.log 'slkjflsfd', @getSingleton('kiteController')
-    KDObject.on "singleton.kiteController.registered",=>
-      console.log 'hppp'
-      @kiteController = @getSingleton('kiteController')
-
+    @kiteController = @getSingleton('kiteController')
+    unless @kiteController?
+      KDObject.on "singleton.kiteController.registered",=>
+        @kiteController = @getSingleton('kiteController')

   remove:(callback)->


gulp: add versions to the sprites when building

--- client/landing/site.landing/gulpfile.coffee
+++ client/landing/site.landing/gulpfile.coffee
@@ -36,9 +36,9 @@

 gulp.task 'sprites', ['sprites@1x', 'sprites@2x'], ->

-gulp.task 'sprites@1x', (req 'task.sprites').bind this, 1
+gulp.task 'sprites@1x', (req 'task.sprites').bind this, 1, argv['koding-version']

-gulp.task 'sprites@2x', ['sprites@1x'], (req 'task.sprites').bind this, 2
+gulp.task 'sprites@2x', ['sprites@1x'], (req 'task.sprites').bind this, 2, argv['koding-version']

 # IMAGE MINIFICATION


Escape toets toevoegen

Escape toets toevoegen

--- app/assets/javascripts/modal.js.coffee
+++ app/assets/javascripts/modal.js.coffee
@@ -15,6 +15,9 @@

 $(document).on 'click', '[data-dismiss="modal"]', ->
   hideModal()
+
+$(document).on 'keyup', 'body.overlay', (e) ->
+  hideModal() if e.keyCode == 27

 $(document).on 'click', '.modal', (e) ->
   e.stopPropagation()


only colors highlighted words in visualization

--- app/assets/javascripts/prism.js.coffee
+++ app/assets/javascripts/prism.js.coffee
@@ -82,4 +82,7 @@
     # Clicking on a facet sets the color
     $("li.vis_facet").click ->
         current_color = $("input", this).val()
-        d3.selectAll("span.word").data(frequencies[current_color]).transition().style("font-size", (d) -> (16+12*d) + "px" ).style("color", current_color)
+        d3.selectAll("span.word").data(frequencies[current_color]).transition().style("font-size", (d) -> (16+12*d) + "px" ).style("color", (d) -> if (d > 0)
+        	current_color
+        else
+        	"black")


Add name validations to user's model

Add name validations to user's model

--- app/models/user.coffee
+++ app/models/user.coffee
@@ -30,8 +30,14 @@
       columnName: 'hash'
     firstName:
       type: 'string'
+      required: true
+      minLength: 3
+      maxLength: 100
     lastName:
       type: 'string'
+      required: true
+      minLength: 3
+      maxLength: 100
     city:
       model: 'city'
     role:


Patron requested

Patron requested

--- client/stack-editor/lib/editor/index.coffee
+++ client/stack-editor/lib/editor/index.coffee
@@ -98,7 +98,9 @@

     @secondaryActions.addSubView new CustomLinkView
       cssClass : 'HomeAppView--button secondary fr'
-      title    : 'DOCUMENTATION'
+      attributes :
+        style  : 'color: #67a2ee;'
+      title    : 'CLICK HERE TO READ STACK SCRIPT DOCS'
       href     : 'http://www.koding.com/docs'

     @tabView.unsetClass 'kdscrollview'


Tests: Teams: Fixed seeTeamNameOnsideBar test.

--- client/test/lib/helpers/teamshelpers.coffee
+++ client/test/lib/helpers/teamshelpers.coffee
@@ -159,9 +159,11 @@
   seeTeamNameOnsideBar: (browser, name) ->

     sidebarSelector = '.with-sidebar [testpath=main-sidebar]'
+    listSelector    = "#{sidebarSelector} .SidebarListItem"

     browser
       .waitForElementVisible  sidebarSelector, 20000
+      .waitForElementVisible  listSelector, 20000
       .assert.containsText    sidebarSelector, name


OX-55 create basic UI elements

OX-55 create basic UI elements

--- app/assets/javascripts/view/gui.coffee
+++ app/assets/javascripts/view/gui.coffee
@@ -22,13 +22,22 @@
                 lockZoom: data.lockZoom
                 inverseX: data.mouseInversionX == 1
                 inverseY: data.mouseInversionY == 1
+
                 routeClippingDistance: data.routeClippingDistance
                 displayCrosshairs: data.displayCrosshair
                 #FIXME: Why do I have to do this?
                 interpolation : if typeof data.interpolation isnt "undefined" then data.interpolation else true
+
                 displayPrevXY : data.displayPreviewXY
                 displayPrevYZ : data.displayPreviewYZ
                 displayPrevXZ : data.displayPreviewXZ
+
+                activeTreeID : 1
+                newTree : -> alert "Create New Tree"
+                deleteActiveTree : -> alert "Delete Active Tree"
+
+                activeNodeID : 1
+                deleteActiveNode : -> alert "Delete Active Node"
               }
     @gui  = new dat.GUI({autoPlace: false})

@@ -80,10 +89,32 @@
                           .name("Display XZ-Plane")
                           .onChange(@setDisplayPreviewXZ)

+    fTrees = @gui.addFolder("Trees")
+    (fTrees.add @settings, "activeTreeID")
+                          .min(1)
+                          .step(1)
+                          .name("Active Tree ID")
+                          #.onChange(@setDisplayPreviewXY)
+    (fTrees.add @settings, "newTree")
+                          .name("Create New Tree")
+    (fTrees.add @settings, "deleteActiveTree")
+                          .name("Delete Active Tree")
+
+    fNodes = @gui.addFolder("Nodes")
+    (fNodes.add @settings, "activeNodeID")
+                          .min(1)
+                          .step(1)
+                          .name("Active Node ID")
+                          #.onChange(@setDisplayPreviewXY)
+    (fNodes.add @settings, "deleteActiveNode")
+                          .name("Delete Active Node")
+
     fPosition.open()
     fControls.open()
     fView.open()
     fSkeleton.open()
+    fTrees.open()
+    fNodes.open()

   setPosFromString : (posString) =>
     stringArray = posString.split(",")


comment syntax...

comment syntax...

--- lib/runner.coffee
+++ lib/runner.coffee
@@ -28,14 +28,14 @@
     ), 500

-    // create a fixer element that forces chrome to load font styles
-    // contains *r*egular, *b*old, *i*talic and i in b
+    # create a fixer element that forces chrome to load font styles
+    # contains *r*egular, *b*old, *i*talic and i in b
     fixerProto = Object.create(HTMLElement::)
     fixerProto.createdCallback = ->
       @innerHTML = "regular<b>bold<i>italic<i></b><i>italic<i>"
       return

-    fixer = document.registerElement("fonts-fixer",
+    fixer = document.registerElement("font-fixer",
       prototype: fixerProto
     )


adding task execution time by name of exec

--- lib/server.coffee
+++ lib/server.coffee
@@ -279,6 +279,7 @@
 									jobItem._status = "completed"
 									jobItem._lastchange = ~~((new Date).getTime() / 1000)
 									datalog.legauge 'task_completed', c.node.name
+									datalog.legauge 'task_exectime', c.timeTaken, [ 'exec:' +  (jobItem.exec.split("/").pop()).split(" ")[0]] if c.timeTaken?
 					else
 						if c.startup
 							console.log 'Server Reconnected ... ', c.startup
@@ -640,6 +641,7 @@
 					if error
 						console.log 'error', c._id, cmd1, cmd, error
 					#check if there is a file output
+					started = ~~((new Date).getTime() / 1000)
 					file_ret = if fs.existsSync(env.TMPDIR + c._id + '_out.json') then fs.readFileSync(process.env.TMPDIR + c._id + '_out.json', 'utf8') else null
 					#sending back the status
 					s_wc.send JSON.stringify(
@@ -647,6 +649,7 @@
 						stdout: stdout
 						error: error
 						status: 3
+						timeTaken: ~~((new Date).getTime() / 1000) - started
 						node:
 							name: os.hostname()
 							loadavg: os.loadavg()


Consistency

Consistency

--- app/assets/javascripts/coders.js.coffee
+++ app/assets/javascripts/coders.js.coffee
@@ -5,7 +5,7 @@

 $('body.coders.show').ready ->
-    $('#history-table').dataTable
+    $('#history-table').DataTable
         order: [[0, 'desc']]
         processing: true
         serverSide: true
@@ -20,9 +20,7 @@
         ]
         language: window.default_pagination_language

-
-
-    $('#bounty-table').dataTable
+    $('#bounty-table').DataTable
         order: [[0, 'desc']]
         processing: true
         serverSide: true
@@ -33,6 +31,7 @@
             className: "text-right"
         ]
         language: window.default_pagination_language
+

 $('body.scoreboard.index').ready ->
     table = $('#scoreboard').DataTable


Use new namespace for terminal

Use new namespace for terminal

--- app/assets/javascripts/player.js.coffee
+++ app/assets/javascripts/player.js.coffee
@@ -2,7 +2,7 @@
   constructor: (cols, lines, data, time) ->
     @minDelay = 0.01
     @speed = 1.0
-    @terminal = new SP.Terminal(cols, lines)
+    @terminal = new AsciiIo.Terminal(cols, lines)
     @interpreter = new AsciiIo.AnsiInterpreter(@terminal)
     @data = data
     @time = time


Make animation faster

Make animation faster

Its a bit slow now that it has the easing stage.

--- app/assets/javascripts/static.js.coffee
+++ app/assets/javascripts/static.js.coffee
@@ -14,13 +14,13 @@
         $target_count = parseInt($this.text().replace(/\,/g, ''))

         jQuery(counter: 0).animate { counter: $target_count },
-          duration: 500 + index * 200
+          duration: 400 + index * 200
           easing: "easeInOutQuint"
           step: ->
             $this.text Math.ceil(@counter).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
           complete: ->
             jQuery(counter: $target_count).animate { counter: (3 + $target_count) },
-              duration: 1500 + index * 600
+              duration: 1300 + index * 600
               easing: "easeOutQuint"
               step: ->
                 $this.text Math.ceil(@counter).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")


Handles rubocop internal error

Handles rubocop internal error

--- src/beautifiers/rubocop.coffee
+++ src/beautifiers/rubocop.coffee
@@ -63,6 +63,9 @@
       onStdin: (stdin) -> stdin.end text
     }).then((stdout) =>
       @debug("rubocop output", stdout)
+      # Rubocop output an error if stdout is empty
+      return text if stdout.length == 0
+
       result = stdout.split("====================\n")
       result[result.length - 1]
     )


Comment out run block in app.coffee

Comment out run block in app.coffee

--- app/scripts/app.coffee
+++ app/scripts/app.coffee
@@ -83,44 +83,44 @@
         })
     $urlRouterProvider.otherwise('/')
     $sessionStorageProvider.set('session', {id: null, userId: null, userRole: null})
-  .run ($rootScope, $state, $window, $sessionStorage, OAuth, AuthService, AUTH_EVENTS) ->
-    #console.log($sessionStorage.session.userRole)
-    $rootScope.$on('oauth:error', (event, rejection) ->
-      # Ignore `invalid_grant` error - should be catched on `LoginController`.
-      if 'invalid_grant' is rejection.data.error
-        return
+  # .run ($rootScope, $state, $window, $sessionStorage, OAuth, AuthService, AUTH_EVENTS) ->
+  #   #console.log($sessionStorage.session.userRole)
+  #   $rootScope.$on('oauth:error', (event, rejection) ->
+  #     # Ignore `invalid_grant` error - should be catched on `LoginController`.
+  #     if 'invalid_grant' is rejection.data.error
+  #       return

-      # Refresh token when a `invalid_token` error occurs.
-      if 'invalid_token' is rejection.data.error
-        return OAuth.getRefreshToken()
+  #     # Refresh token when a `invalid_token` error occurs.
+  #     if 'invalid_token' is rejection.data.error
+  #       return OAuth.getRefreshToken()

-      # Redirect to `/login` with the `error_reason`.
-      return $window.location.href = '/login?error_reason=' + rejection.data.error
-    )
-    $rootScope.$on('$stateChangeStart', (event, state) ->
-      authorizedRoles = state.data.authorizedRoles
-      if not AuthService.isAuthorized(authorizedRoles)
-        event.preventDefault();
-        if AuthService.isAuthenticated()
-          # user is not allowed
-          $rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
-        else
-          # user is not logged in
-          $rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
-    )
-    $rootScope.$on(AUTH_EVENTS.loginSuccess, (event, state) ->
-      #console.log('Login captured.')
-      #console.log($sessionStorage.session.userRole)
-      $state.go('user')
-    )
-    $rootScope.$on(AUTH_EVENTS.notAuthorized, (event, state) ->
-      #console.log('Not authorized captured.')
-      $state.go('home')
-    )
-    $rootScope.$on(AUTH_EVENTS.notAuthenticated, (event, state) ->
-      #console.log('Not authenticated captured.')
-      $state.go('login')
-    )
+  #     # Redirect to `/login` with the `error_reason`.
+  #     return $window.location.href = '/login?error_reason=' + rejection.data.error
+  #   )
+  #   $rootScope.$on('$stateChangeStart', (event, state) ->
+  #     authorizedRoles = state.data.authorizedRoles
+  #     if not AuthService.isAuthorized(authorizedRoles)
+  #       event.preventDefault();
+  #       if AuthService.isAuthenticated()
+  #         # user is not allowed
+  #         $rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
+  #       else
+  #         # user is not logged in
+  #         $rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
+  #   )
+  #   $rootScope.$on(AUTH_EVENTS.loginSuccess, (event, state) ->
+  #     #console.log('Login captured.')
+  #     #console.log($sessionStorage.session.userRole)
+  #     $state.go('user')
+  #   )
+  #   $rootScope.$on(AUTH_EVENTS.notAuthorized, (event, state) ->
+  #     #console.log('Not authorized captured.')
+  #     $state.go('home')
+  #   )
+  #   $rootScope.$on(AUTH_EVENTS.notAuthenticated, (event, state) ->
+  #     #console.log('Not authenticated captured.')
+  #     $state.go('login')
+  #   )
   # .controller 'AppController', ($scope, AuthService, USER_ROLES) ->
   #     $scope.currentUser = null
   #     $scope.userRoles = USER_ROLES


Allow the play-empty animation to be spammed

--- app/scripts/app.coffee
+++ app/scripts/app.coffee
@@ -73,9 +73,11 @@

   onPlayEmpty: ->
       $body = $ "body"
-      $body.addClass "play-empty"
-      clearTimeout(@playEmptyTimeout) if @playEmptyTimeout
-      @playEmptyTimeout = setTimeout (-> $body.removeClass "play-empty"), 1000
+      $body.removeClass("play-empty")
+      _.defer =>
+        $body.addClass "play-empty"
+        clearTimeout(@playEmptyTimeout) if @playEmptyTimeout
+        @playEmptyTimeout = setTimeout (-> $body.removeClass "play-empty"), 1000

   onClickExport: (e) ->
     e.preventDefault()


remove console.logs

remove console.logs

--- app/coffee/HealthCheckController.coffee
+++ app/coffee/HealthCheckController.coffee
@@ -24,14 +24,10 @@
 				opts = getOpts("/tag")
 				opts.json = true
 				request.get opts, (err, res, body)->
-					console.log body, tagName
-
 					if res.statusCode != 200
 						return cb("status code not 200, its #{res.statusCode}")

 					hasTag = _.some body, (tag)->
-						console.log tag.name == tagName, tagName
-						console.log _.contains(tag.project_ids, project_id.toString()), tag.project_ids, project_id
 						tag.name == tagName and _.contains(tag.project_ids, project_id.toString())
 					if hasTag
 						cb()


add cinematic action log field in levelSession schema (#708)

* add cinematic action log field in levelSession schema

* minor review fixes

--- app/schemas/models/level_session.coffee
+++ app/schemas/models/level_session.coffee
@@ -71,6 +71,14 @@
       additionalProperties: c.object {},
         contentType: c.shortString { enum: ['cinematic', 'cutscene-video', 'interactive'] }
         complete: { type: 'boolean' }
+        cinematicActionLog:
+          description: 'tracks action log of user - how much time user spent, did user skip prompt'
+          type: 'array'
+          items: c.object {},
+            skip: { type: 'boolean', description: 'did user skip prompt before it got played till completion?' }
+            prompt: { type: 'string', description: 'hash of text shown in prompt' }
+            spent: { type: 'integer', description: 'time spent on prompt in seconds' }
+            next: { type: 'string', enum: ['F', 'B'], description: 'did user go Forward(F) or Backward(B)?' }
     scripts: c.object {},
       ended:
         type: 'object'
@@ -165,27 +173,27 @@

   codeLanguage:
     type: 'string'
-
+
   codeConcepts:
     type: 'array'
     items:
-      type: 'string'
+      type: 'string'

   playtime:
     type: 'number'
     title: 'Playtime'
     description: 'The total playtime on this session in seconds'
-
+
   hintTime:
     type: 'number'
     title: 'Hint Time'
     description: 'The total time hints viewed in seconds'
-
+
   timesCodeRun:
     type: 'number'
     title: 'Times Code Run'
     description: 'The total times the code has been run'
-
+
   timesAutocompleteUsed:
     type: 'number'
     title: 'Times Autocomplete Used'


All snippets are equal, but some are more equal than others.

--- src/completers/snippets.coffee
+++ src/completers/snippets.coffee
@@ -125,7 +125,7 @@
           content: s.content  # Used internally by Zatanna, not by ace autocomplete
           caption: caption
           snippet: snippet
-          score: fuzzScore
+          score: fuzzScore * s.importance ? 1.0
           meta: s.meta or (if s.tabTrigger and not s.name then s.tabTrigger + '\u21E5' else 'snippets')
     , @
     # console.log 'Zatanna snippet completions', completions


Captions show during search

Captions show during search

--- app/scripts/alchemy/core/search.coffee
+++ app/scripts/alchemy/core/search.coffee
@@ -19,11 +19,15 @@
         searchBox = d3.select("#search input")

         searchBox.on("keyup", ()->
+            input = searchBox[0][0].value.toLowerCase()
+
             d3.selectAll(".node").classed("inactive", false)
+
+            d3.selectAll("text").attr("style", ->
+                "display: inline;" if input != "")

             d3.selectAll(".node")
               .classed("inactive", (node) ->
-                input = searchBox[0][0].value.toLowerCase()

                 hidden = node.caption
                              .toLowerCase()
@@ -42,7 +46,7 @@

                         targetHidden or sourceHidden
                     )
-
+
                 hidden

                 )


Update darwin.cson

Update darwin.cson

Missed a spot

--- menus/darwin.cson
+++ menus/darwin.cson
@@ -217,6 +217,7 @@
     {label: 'Split Down', command: 'pane:split-down'}
     {label: 'Split Left', command: 'pane:split-left'}
     {label: 'Split Right', command: 'pane:split-right'}
+    {label: 'Close Pane', command: 'pane:close'}
     {type: 'separator'}
   ]
   'atom-pane': [


Domains: Fixed domains filter

Domains: Fixed domains filter

Thanks @scorphus

--- app/scripts/controllers/domains.coffee
+++ app/scripts/controllers/domains.coffee
@@ -1,21 +1,21 @@
 'use strict'

 class DomainsCtrl
-  constructor: (@scope, @window, @interval, @DomainsFcty, @MostCommonViolationsFcty, @WebSocketFcty, @NamedRouteService, @localStorage, @gettextCatalog) ->
+  constructor: (@scope, @window, @interval, @filter, @DomainsFcty, @MostCommonViolationsFcty, @WebSocketFcty, @NamedRouteService, @localStorage, @gettextCatalog) ->
     @domainsVisible = false
     @groupsVisible = true
     @mostFrequentVisible = false

     @storage = @localStorage
     @prefsFilter = @storage.prefsFilter
-    @order = 'is_active'
+    @order = '-is_active'
     @order_options = [
-      {text: @gettextCatalog.getString('sort by'), order:'is_active'},
-      {text: @gettextCatalog.getString('pages'), order:'details.pageCount'},
-      {text: @gettextCatalog.getString('violations'), order:'details.violationCount'},
-      {text: @gettextCatalog.getString('error %'), order:'details.errorPercentage'},
-      {text: @gettextCatalog.getString('avg resp time'), order:'details.averageResponseTime'}]
-    @order_select = @order_options[0]
+      {text: @gettextCatalog.getString('is active'), order:'-is_active'},
+      {text: @gettextCatalog.getString('pages'), order:'-details.pageCount'},
+      {text: @gettextCatalog.getString('violations'), order:'-details.violationCount'},
+      {text: @gettextCatalog.getString('error %'), order:'-details.errorPercentage'},
+      {text: @gettextCatalog.getString('avg resp time'), order:'-details.averageResponseTime'}]
+    @order_select = {text: @gettextCatalog.getString('is active'), order:'-is_active'}

     @getDomainData()
     @getMostCommonViolations()
@@ -56,12 +56,7 @@
     for domain in @domainList
       domain.details = _.find domainsDetails, {id: domain.id}

-    @domains = _.sortBy @domainList, (domain) =>
-      order_splitted = @order.split '.'
-      if order_splitted.length == 2
-        domain[order_splitted[0]][order_splitted[1]]
-      else
-        domain[order_splitted[0]]
+    @domains = @filter('orderBy')(@domainList,  @order)

     @loadedDomains = @domains.length

@@ -116,6 +111,6 @@

 angular.module('holmesApp')
-  .controller 'DomainsCtrl', ($scope, $window, $interval, DomainsFcty, MostCommonViolationsFcty, WebSocketFcty, $NamedRouteService, $localStorage, gettextCatalog) ->
+  .controller 'DomainsCtrl', ($scope, $window, $interval,  $filter, DomainsFcty, MostCommonViolationsFcty, WebSocketFcty, $NamedRouteService, $localStorage, gettextCatalog) ->

-    $scope.model = new DomainsCtrl($scope, $window, $interval, DomainsFcty, MostCommonViolationsFcty, WebSocketFcty, $NamedRouteService, $localStorage, gettextCatalog)
+    $scope.model = new DomainsCtrl($scope, $window, $interval, $filter, DomainsFcty, MostCommonViolationsFcty, WebSocketFcty, $NamedRouteService, $localStorage, gettextCatalog)


cleanup further #88

cleanup further #88

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -64,6 +64,7 @@
   itemsByProvider: null # Used to cache result
   lastNarrowQuery: ''
   modifiedState: null
+  readOnly: false

   isModified: ->
     @modifiedState
@@ -112,8 +113,9 @@
     @currentItemIndicator = new CurrentItemIndicator(@editor)
     @grammar = new Grammar(@editor, includeHeaderRules: @provider.includeHeaderGrammar)

+    @disposables.add @onDidMoveToItemArea  => @setReadOnly(true)
+
     @disposables.add(
-      @onDidMoveToItemArea(@onMoveToItemArea.bind(this))
       @registerCommands()
       @observeChange()
       @observeStopChanging()
@@ -129,6 +131,17 @@
     @editorElement.component?.setInputEnabled(false)
     @editorElement.classList.add('read-only')
     @vmpActivateNormalMode() if @vmpIsInsertMode()
+
+  setReadOnly: (readOnly) ->
+    @readOnly = readOnly
+    if @readOnly
+      @editorElement.component?.setInputEnabled(false)
+      @editorElement.classList.add('read-only')
+      @vmpActivateNormalMode() if @vmpIsInsertMode()
+    else
+      @editorElement.component.setInputEnabled(true)
+      @editorElement.classList.remove('read-only')
+      @vmpActivateInsertMode() if @vmpIsNormalMode()

   observeStopChangingActivePaneItem: ->
     atom.workspace.onDidStopChangingActivePaneItem (item) =>
@@ -526,10 +539,7 @@
   moveToPrompt: ({startInsert}={}) ->
     @withIgnoreCursorMove =>
       @editor.setCursorBufferPosition(@getPromptRange().end)
-      if startInsert
-        @editorElement.component.setInputEnabled(true)
-        @editorElement.classList.remove('read-only')
-        @vmpActivateInsertMode() if @vmpIsNormalMode()
+      @setReadOnly(false) if startInsert
       @emitDidMoveToPrompt()

   isNormalItemRow: (row) ->


Add notification icon

Add notification icon

--- modules/ui.coffee
+++ modules/ui.coffee
@@ -39,6 +39,7 @@

 exports.showNotification = showNotification = (body) ->
   notification = new Notification 'Poi',
+    icon: 'icon.png'
     body: body
   notification.onclick = ->
     notification.close()


updated high speed, high res world cameras, 120hz eye upgrade

--- assets/coffeescript/product_list.coffee
+++ assets/coffeescript/product_list.coffee
@@ -24,7 +24,7 @@
     cartlabel: "World camera: high resolution"
     cartSpecs: "Max resolution: 1910x1080 @30fps. FOV 90 degrees diagonal with auto focus lens. 120ms latency"
     dimensions: 0
-    weight: 5
+    weight: 17 # logitec c930e camera
     img: "../media/images/config_images/pupil-config-world-1080p.jpg"
     video: "#"
     order: 2
@@ -40,7 +40,7 @@
     cartlabel: "World camera: high speed"
     cartSpecs: "Variable Resolution: 1920x1080 @30fps, 1280x720 @60fps, 640x480 @120fps. FOV 100deg diag (lenses are exchangeable: purchase alternate lenses below). 5.7ms latency."
     dimensions: 0
-    weight: 5
+    weight: 5 # 120hz eye camera world, new
     img: "../media/images/config_images/pupil-config-world-120hz.jpg"
     video: "#"
     order: 3
@@ -113,7 +113,7 @@
     cartlabel: "Camera upgrade: 120hz eye camera upgrade"
     cartSpecs: "Resolution: 640x480 @120fps. IR illumination."
     dimensions: 0
-    weight: 31 # 12 headset frame, 7 harness cable, 8 cable clip weight, 4 eye camera
+    weight: 4 #this is an upgrade to 120hz eye camera, weight is only for new 120hz eye camera
     img: "http://placehold.it/800x500/444444"
     video: "#"
     order: 1


remove w30 from configurator

remove w30 from configurator

--- assets/coffeescript/product_list.coffee
+++ assets/coffeescript/product_list.coffee
@@ -7,20 +7,20 @@
       title_cart: "World camera: no world camera"
       description_cart: "No world camera"
       weight: 0
-    w30:
-      id: 'w30'
-      title_store: "high resolution"
-      description_store: "World camera with beautiful image quality. We recommend this option when you want aesthetically pleasing footage, and are not concerned with size, weight, or speed."
-      tech_specs:
-        sensor: "1910x1080 @30fps"
-        fov: "90 degrees diagonal with auto focus lens."
-        latency: "127.7ms"
-        audio: "Record audio with built-in stereo microphone"
-      title_cart: "World camera: high resolution"
-      description_cart: "Max resolution: 1910x1080 @30fps. FOV 90 degrees diagonal with auto focus lens. 120ms latency."
-      weight: 17
-      title_video: "Download sample videos"
-      link_video: "https://drive.google.com/file/d/0B4Q4b_aBOfU-QW1ncXpkdS02R1k/view?usp=sharing"
+    # w30:
+    #   id: 'w30'
+    #   title_store: "high resolution"
+    #   description_store: "World camera with beautiful image quality. We recommend this option when you want aesthetically pleasing footage, and are not concerned with size, weight, or speed."
+    #   tech_specs:
+    #     sensor: "1910x1080 @30fps"
+    #     fov: "90 degrees diagonal with auto focus lens."
+    #     latency: "127.7ms"
+    #     audio: "Record audio with built-in stereo microphone"
+    #   title_cart: "World camera: high resolution"
+    #   description_cart: "Max resolution: 1910x1080 @30fps. FOV 90 degrees diagonal with auto focus lens. 120ms latency."
+    #   weight: 17
+    #   title_video: "Download sample videos"
+    #   link_video: "https://drive.google.com/file/d/0B4Q4b_aBOfU-QW1ncXpkdS02R1k/view?usp=sharing"
     w120:
       id: 'w120'
       title_store: "high speed"


update oculus product listing

update oculus product listing

--- assets/coffeescript/product_list.coffee
+++ assets/coffeescript/product_list.coffee
@@ -149,7 +149,7 @@
     description_cart: "For use within an academic (non-commercial) context."

 get_vr_ar_product_ids = ->
-  return ['hololens_w120_e200b','htcvivepro_e120b', 'htcvive_e120b','oculusdk2_e120b','epsonbt300_w120_e200b']
+  return ['hololens_w120_e200b', 'htcvive_e120b','epsonbt300_w120_e200b']

 get_additional_product_ids = ->
   return ['camera_e200','pm_motoz2','support_6','support_12']


restoring attributes used in the rowDetail views

--- jsapp/xlform_model_view/view.row.coffee
+++ jsapp/xlform_model_view/view.row.coffee
@@ -70,6 +70,8 @@

         @listView = new $viewChoices.ListView(model: cl, rowView: @).render()

+      @cardSettingsWrap = @$('.card__settings').eq(0)
+      @defaultRowDetailParent = @cardSettingsWrap.find('.card__settings__fields--question-options').eq(0)
       for [key, val] in @model.attributesArray() when key is 'label' or key is 'type'
         view = new $viewRowDetail.DetailView(model: val, rowView: @)
         if key == 'label' and @model.get('type').get('value') == 'calculate'


Warn if command requires an active file

Warn if command requires an active file

--- lib/controllers/config-controller.coffee
+++ lib/controllers/config-controller.coffee
@@ -92,10 +92,25 @@

   runProcess: =>
     filePath = null;
-    pane = atom.workspace.getActivePaneItem();
-
-    if typeof pane?.getPath is "function"
-      filePath = pane.getPath();
+    textEditor = atom.workspace.getActiveTextEditor();
+
+    if textEditor?
+      filePath = textEditor.getPath();
+
+    # Do not run the command if it depends on a file when there isn't one.
+    if !filePath? and @inputUsesFileVariables()
+      notifOptions = {};
+      notifOptions["dismissable"] = true;
+
+      # filePath is null if there aren't any text editors.
+      # filePath is undefined if there is a text editor open, but it hasn't been saved before.
+      if filePath == null
+        notifOptions["detail"] = "This command requires an open file to be active in the workspace.";
+      else
+        notifOptions["detail"] = "The file needs to be saved before this command can be executed on it.";
+
+      atom.notifications.addWarning("Cannot execute #{@config.namespace}: #{@config.action}", notifOptions);
+      return;

     @runProcessWithFile(filePath);

@@ -155,6 +170,23 @@

     main.hidePanel();

+  # Return true if any of the inputs use any of the {file*} variables.
+  inputUsesFileVariables: ->
+    r = new RegExp("{file.*?}","g");
+
+    if r.test(@config.command) || r.test(@config.cwd)
+      return true;
+
+    if @config.env?
+      for key, val of @config.env
+        if r.test(val)
+          return true;
+
+    if @config.input? and r.test(@config.input)
+      return true;
+
+    return false;
+
   # Returns a string describing the output target.
   getTargetDescription: ->
     if @config.stream


setLogging must be static

setLogging must be static

--- server/collection-security.coffee
+++ server/collection-security.coffee
@@ -1,5 +1,5 @@
 class CollectionSecurity
-  setLogging: (value) ->
+  @setLogging: (value) ->
     @_logging = value
   @_log: (msg, level = 'debug') ->
     console[level] msg if @_logging or level is 'error'


Remove unnecessary reparse

Remove unnecessary reparse

Calling `setActiveChildren` already calls `setParsedChildren`, so it's not necessary to call it again inside `arrange`.

--- core/jquery.shapeshift.coffee
+++ core/jquery.shapeshift.coffee
@@ -222,7 +222,7 @@
     render: (reparse = false, trigger_drop_finished) ->
       @setActiveChildren() if reparse
       @setGridColumns()
-      @arrange(reparse, trigger_drop_finished)
+      @arrange(false, trigger_drop_finished)

     # ----------------------------
     # setGrid:


Map Input: Now it's possible to remove the value from the input. Also implemented 'checkValue' to throw error if the value is wrong. See #54435

--- src/elements/Map/MapInput.coffee
+++ src/elements/Map/MapInput.coffee
@@ -83,12 +83,17 @@
 			iconName: @__selectedMarkerOptions.iconName
 			iconColor: @__selectedMarkerOptions.iconColor

-		if stringCoordinates
-			parsedPosition = CUI.util.parseCoordinates(stringCoordinates)
-			objectValue.position = if parsedPosition then parsedPosition else ""
-		else
-			objectValue.position = @__getPosition()
+		parsedPosition = CUI.util.parseCoordinates(stringCoordinates)
+		objectValue.position = if parsedPosition then parsedPosition else ""
+
 		return objectValue
+
+	checkValue: (v) ->
+		if CUI.util.isEmpty(v)
+			return true
+		if @__checkInput(v)
+			return true
+		throw new Error("#{@__cls}.setValue(value): Value needs to be valid coordinates.")

 	render: ->
 		super()
@@ -143,6 +148,7 @@
 			handle_focus: false
 			placement: "se"
 			pane:
+				padded: true
 				content: @__buildIconPopoverContent()
 			onHide: ->
 				iconPopover.destroy()
@@ -183,9 +189,8 @@
 				options
 		)

-		form = new CUI.Form(
+		form = new CUI.Form
 			fields: [iconSelect, iconColorSelect]
-		)
 		form.start()
 		form


use `settings` instead of `atom.config.get`

--- lib/motions/general-motions.coffee
+++ lib/motions/general-motions.coffee
@@ -152,7 +152,7 @@
   # MoveRight can be combined with Delete, which will indicate it by the presence of options.deleting
   moveCursor: (cursor, count=1, options={}) ->
     _.times count, =>
-      goingToNextLine = atom.config.get('vim-mode.wrapLeftRightMotion')
+      goingToNextLine = settings.wrapLeftRightMotion()

       if not cursor.isAtEndOfLine() # the line is not empty
         cursor.moveRight()


Add support for artist endpoint in gravity

--- node/server.coffee
+++ node/server.coffee
@@ -148,6 +148,9 @@
 gravity.post '/api/v1/me/unsubscribe*', (req, res) ->
   res.send [fabricate 'user']

+gravity.get '/api/v1/artist/:id', (req, res) ->
+  res.send fabricate 'artist'
+
 gravity.post '/api/v1/me/artwork_inquiry_request', (req, res) ->
   res.send fabricate 'user'


fix typo

fix typo

--- lib/browse.coffee
+++ lib/browse.coffee
@@ -1,7 +1,7 @@
 {CompositeDisposable} = require 'atom'

 # Dependencies
-{exec} = require('child_process')
+{execSync} = require('child_process')
 fs = require 'fs'
 shell = require 'shell'


Let the toggle toggle

Let the toggle toggle

--- lib/styles/default/behavior.coffee
+++ lib/styles/default/behavior.coffee
@@ -259,7 +259,8 @@
   #
   # The blur event doesn't give us the previous event, sadly, so we first trap mousedown events
   lastMousedownTimestamp = null
-  nav$.mousedown (evt) -> lastMousedownTimestamp = evt.timeStamp
+  nav$.mousedown (evt) ->
+    lastMousedownTimestamp = evt.timeStamp unless evt.target == toggle$[0]

   # And we refocus search if we are within a very short duration between the last mousedown in nav$.
   search$.blur (evt) ->


remove index code from asset package

remove index code from asset package

--- assets/articles.coffee
+++ assets/articles.coffee
@@ -2,11 +2,8 @@
 require('backbone').$ = $
 articleIndex = require '../apps/articles/client/articles.coffee'
 articleShow = require '../apps/articles/client/article.coffee'
-articleShowAndIndex = require '../apps/articles/client/index.coffee'

 $ ->
-  articleShowAndIndex.init()
-
   if location.pathname is '/articles'
     articleIndex.init()
   else if location.pathname.match '/article/.*'


Socialapi: we dont need this part

Socialapi: we dont need this part

--- client/app/lib/util/doXhrRequest.coffee
+++ client/app/lib/util/doXhrRequest.coffee
@@ -28,15 +28,8 @@
       }

-    # >=400 - http errors
-    if xhr.status >= 400
-      return callback {
-        message : response.description
-        code    : xhr.status
-      }
-
-
-    if xhr.status not in [200, 304]
+    # >=300 - http errors
+    if xhr.status >= 300
       return callback {
         message : response.description
         code    : xhr.status


IDEView: Don't kill terminal session if pane is detached.

--- client/ide/views/tabview/ideview.coffee
+++ client/ide/views/tabview/ideview.coffee
@@ -48,7 +48,7 @@
     @tabView.on 'PaneRemoved', (tab) =>
       { view } = tab.pane

-      if view instanceof IDE.TerminalPane
+      if view instanceof IDE.TerminalPane and not tab.isDetached
         sessionId = view.session or view.webtermView.sessionId
         @terminateSession @mountedMachine, sessionId


erase coma

erase coma

--- coffee/lightbox-plans.controller.coffee
+++ coffee/lightbox-plans.controller.coffee
@@ -71,8 +71,8 @@
         @subscriptionsService.selectMyPlan(plan).then(@._onSuccessSelectPlan.bind(this))

         if !@.myPlan || (!@.myPlan.current_plan.amount_month && !@.myPlan.current_plan.amount_year)
-            google_conversion_id = @config.get("google_adwords_conversion_id"),
-            google_conversion_label = @config.get("google_adwords_conversion_label"),
+            google_conversion_id = @config.get("google_adwords_conversion_id")
+            google_conversion_label = @config.get("google_adwords_conversion_label")

             if google_conversion_id && google_conversion_label
                 window.google_trackConversion({


Change fallback for UX Markup from xml back to html

--- src/languages/ux_markup.coffee
+++ src/languages/ux_markup.coffee
@@ -2,7 +2,7 @@

   name: "UX Markup"
   namespace: "ux"
-  fallback: ['xml']
+  fallback: ['html']

   ###
   Supported Grammars


Fixed give_diagnose() after coffeefying

Fixed give_diagnose() after coffeefying

--- lib/doctor.coffee
+++ lib/doctor.coffee
@@ -8,9 +8,9 @@
     @reports = {}

   give_diagnose: ->
-    Promise.all @diagnose_db_connection()
-      .finally ->
-        @print_report report for report in @reports when report
+    @diagnose_db_connection()
+      .finally =>
+        @print_report report for problem, report of @reports

   diagnose_db_connection: ->
     @_db.connect()


Allow for a memory sync to use a model_name

--- memory_sync.coffee
+++ memory_sync.coffee
@@ -7,12 +7,12 @@

 class MemorySync
   constructor: (@model_type) ->
-    throw new Error("Missing url for model") unless @url = _.result(@model_type.prototype, 'url')
-
     @store = {}

     # publish methods and sync on model
-    @model_type.model_name = Utils.parseUrl(@url).model_name
+    unless @model_type.model_name # model_name can be manually set
+      throw new Error("Missing url for model") unless @url = _.result(@model_type.prototype, 'url')
+      @model_type.model_name = Utils.parseUrl(@url).model_name
     @model_type._sync = @
     @model_type._schema = new Schema(@model_type)


client: fix bug where topics added to sidebar don't reply state

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -134,9 +134,11 @@

       return KD.showError err  if err

-      item = @addItem channel, yes
+      channel.isParticipant    = yes
       channel.participantCount = participantCount
       channel.emit 'update'
+
+      item = @addItem channel, yes
       item.setUnreadCount unreadCount

@@ -149,6 +151,7 @@
     # @removeItem id

     socialapi.cacheable typeConstant, id, (err, channel) =>
+      channel.isParticipant    = no
       channel.participantCount = participantCount
       channel.emit 'update'


Fix: Missing "new" operator

Fix: Missing "new" operator

--- lib/kernel.coffee
+++ lib/kernel.coffee
@@ -26,7 +26,7 @@

     getPluginWrapper: ->
         unless @pluginWrapper?
-            @pluginWrapper = HydrogenKernel(this)
+            @pluginWrapper = new HydrogenKernel(this)

         return @pluginWrapper


Kite : create new helper while registering kite singleton

--- client/landing/app/MainApp/kite/kitecontroller.coffee
+++ client/landing/app/MainApp/kite/kitecontroller.coffee
@@ -66,6 +66,7 @@

     # new API:
     @kiteInstances = {}
+    @helper = new KiteHelper

   addKite: (name, channel) ->


:fire: unused #441

:fire: unused #441

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -450,13 +450,6 @@
   # closing [)\]"'], followed by EOL or whitespace
   sentenceRegex: /([\.!?][\s\]\)'"]*\s)\S/

-  # [FIXME] unused
-  moveToEndOfString: (point, string) ->
-    lines = string.split('\n')
-    row = lines.length - 1
-    column = lines[row].length
-    point.traverse([row, column])
-
   moveCursor: (cursor) ->
     point = cursor.getBufferPosition()
     @countTimes =>


#200 remove leftover

#200 remove leftover

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -328,11 +328,6 @@
     else
       cursorPoint

-  scanWordRange: (from, pattern, fn) ->
-    scanRange = [[from.row, 0], @editor.getEofBufferPosition()]
-    @editor.scanInBufferRange pattern, scanRange, (event) ->
-      fn(event)
-
   moveCursor: (cursor) ->
     return if cursorIsAtVimEndOfFile(cursor)
     # cache


Main: minor code styling

Main: minor code styling

--- client/Main/maincontroller/maincontroller.coffee
+++ client/Main/maincontroller/maincontroller.coffee
@@ -32,9 +32,9 @@

   createSingletons:->

-    KD.registerSingleton "mainController",            this
-
-    KD.registerSingleton "kontrol",                   new KodingKontrol
+    KD.registerSingleton 'mainController',            this
+
+    KD.registerSingleton 'kontrol',                   new KodingKontrol

     KD.registerSingleton 'appManager',   appManager = new ApplicationManager
     KD.registerSingleton 'notificationController',    new NotificationController


GroupStackSettings: show a warning when there is no credentials, pass credentials as is

--- client/admin/lib/views/groupstacksettings.coffee
+++ client/admin/lib/views/groupstacksettings.coffee
@@ -108,6 +108,17 @@

         {credentials, stackTemplate} = data

+        if not credentials or credentials.length is 0
+          @addSubView new kd.CustomHTMLView
+            partial  : "You don't have any credentials, please add
+                       one Amazon credential first."
+          @addSubView new kd.ButtonView
+            title    : 'Credentials'
+            callback : ->
+              kd.singletons.router.handleRoute '/Account/Credentials'
+
+          return
+
         @createEditorPane stackTemplate?.template?.content or defaultTemplate
         @createCredentialsBox credentials

@@ -166,7 +177,7 @@

     { JCredential, JStackTemplate } = remote.api

-    credentials = [publicKeys.aws] # TODO Make it work with other providers ~ GG
+    credentials = publicKeys

     if stackTemplate
       stackTemplate.update {machines, template, credentials}, (err) =>


Do not show any notification comes from a guest

--- client/app/MainApp/notificationcontroller.coffee
+++ client/app/MainApp/notificationcontroller.coffee
@@ -118,6 +118,10 @@
       KD.remote.api[subject.constructorName][method] args, callback

     KD.remote.cacheable actor.constructorName, actor.id, (err, actorAccount)=>
+      # Ignore all guest notifications
+      # https://app.asana.com/0/1177356931469/7014047104322
+      return  if actorAccount.type is 'unregistered'
+
       fetchSubjectObj (err, subjectObj)=>
         actorName = KD.utils.getFullnameFromAccount actorAccount
         options.title = switch actionType


FinderTreeController: Override expandFolder/collapseFolder methods to emit real time changes.

--- client/ide/finder/idefindertreecontroller.coffee
+++ client/ide/finder/idefindertreecontroller.coffee
@@ -25,3 +25,22 @@
     path            = FSHelper.plainPath path

     appManager.tell 'IDE', 'createNewTerminal', { machine, path }
+
+
+  collapseFolder: (nodeView, callback) ->
+
+    super nodeView, callback
+
+    @emit 'FolderCollapsed', @getFolderPath_ nodeView
+
+
+  expandFolder: (nodeView, callback) ->
+
+    super nodeView, callback
+
+    @emit 'FolderExpanded', @getFolderPath_ nodeView
+
+
+  getFolderPath_: (nodeView) ->
+
+    return FSHelper.plainPath nodeView.getData().path


seeks to fix a race condition (amqp 404 warning from the go broker)

--- client/landing/app/MainApp/account-mixins.coffee
+++ client/landing/app/MainApp/account-mixins.coffee
@@ -83,7 +83,7 @@
         nickname  = delegate?.profile.nickname ?
                     if delegate.guestId then "guest#{delegate.guestId}" ?
                     'unknown'
-        channelName = "#{Bongo.createId 128}.#{nickname}.kite-#{kiteName}"
+        channelName = "#{Bongo.createId 128}.#{nickname}.#{kiteName}"
         namesCache[kiteName] = channelName
         return channelName

@@ -97,25 +97,26 @@
         channel.cycleChannel = -> cycleChannel.call this
         channels[channelName] = channel
         channel.once 'broker.subscribed', ->
-          onReady channel, -> callback channel
+          onReady channel, ->
+            callback channel
+            i = setInterval ->
+              now = Date.now()
+              isUnresponsive = channel.lastPong? and (now - channel.lastPong > 6000)
+              cycleChannel.call channel  if isUnresponsive
+              channel.publish JSON.stringify
+                method      : 'ping'
+                arguments   : []
+                callbacks   : {}
+            , 5000
+            channel.stopPinging =-> clearInterval i
         channel.setAuthenticationInfo
           serviceType : 'kite'
           name        : "kite-#{kiteName}"
           clientId    : KD.remote.getSessionToken()
         channel.on "message", messageHandler.bind channel, kiteName
-        i = setInterval ->
-          now = Date.now()
-          isUnresponsive = channel.lastPong? and (now - channel.lastPong > 6000)
-          cycleChannel.call channel  if isUnresponsive
-          channel.publish JSON.stringify
-            method      : 'ping'
-            arguments   : []
-            callbacks   : {}
-        , 5000
-        channel.stopPinging =-> clearInterval i

       tellKite =(options, callback=->)->
         scrubber = new Scrubber localStore
         args = [options, callback]
-        {method} = options
-        request options.kiteName, method, args
+        {method, kiteName} = options
+        request kiteName, method, args


Visual/edit modes: fix broken due to stray uncommented comment.

--- content_scripts/mode_visual_edit.coffee
+++ content_scripts/mode_visual_edit.coffee
@@ -111,10 +111,6 @@

     else
       @selection.modify @alterMethod, movement...
-
-  # # Return a simple camparable value which will be different for different selections.
-  # hashSelection: ->
-  #   [ @element?.selectionStart, @selection.toString().length ].join "/"

   # Return a simple camparable value which depends on various aspects of the selection which may change when
   # the selection changes.  This is used to detect, after a movement, whether the selection has changed.
@@ -266,7 +262,7 @@
   # Yank the selection; always exits; either deletes the selection or collapses it; returns the yanked text.
   yank: (args = {}) ->
     @yankedText = @selection.toString()
-    console.log "yank:", @yankedText if @debug  This is used to detect, after a movement, whether the selection has changed.
+    console.log "yank:", @yankedText if @debug

     if args.deleteFromDocument or @options.deleteFromDocument
       @selection.deleteFromDocument()


updates

updates

--- coffee/github_commits/twdc-github-commits.coffee
+++ coffee/github_commits/twdc-github-commits.coffee
@@ -12,6 +12,8 @@

 # THe regexp to parse the Link header for pagination
 LINK_REGEXP = /<([^>]+)>; rel="(next|last)"/g
+
+# Parses the value of the Link header returned by GitHub
 parse_link_header = (link_header)->
   return {} unless link_header
   o = {}
@@ -24,23 +26,13 @@
   o

-  #Link: <https://api.github.com/user/repos?page=3&per_page=100>; rel="next",
-  # <https://api.github.com/user/repos?page=50&per_page=100>; rel="last"
-
 connector_base.init_connector
   template: require('./source.jade')

   fields: [
-    {type: 'string', key: 'username', selector: "#username"}
-    {type: 'string', key: 'reponame', selector: "#reponame"}
+    { key: 'username', selector: "#username"}
+    { key: 'reponame', selector: "#reponame"}
   ]
-
-  validator: (data)->
-    return false unless data.username
-    return false unless data.reponame
-    return false unless is_valid_github_string(data.username)
-
-    true

   columns: (connection_data)->
     return {
@@ -85,7 +77,6 @@
           }

         has_more =  if link_headers.next then true else false
-        console.log "Has more: #{has_more}", link_headers
         tableau.dataCallback( out, link_headers.next, has_more)

       error: (xhr, ajaxOptions, thrownError)->


Fixup.

Fixup.

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

 if process.env.NODE_ENV is 'production'
 	console.log 'Loading New Relic...'
-	require 'newrelic'
+	newrelic = require 'newrelic'
 	console.log 'Loaded New Relic.'

 dbConnection = anyDB.createPool config.DATABASE_URL, min: 2, max: 20


Fix proposal filter by type or filter by tag

--- lib/routes.coffee
+++ lib/routes.coffee
@@ -88,23 +88,22 @@

   @route 'proposals',
-    path: '/proposals/:limit?'
-    waitOn: ->
-      limit = @params.limit || 1000 # TODO: Limit
+    path: '/proposals'
+    waitOn: ->
       q = {}
-      if filterType = @params.filterType
-        q.type = filterType
-      if filterTag = @params.filterTag
-        q.tags = filterTag
-      Meteor.subscribe('proposals', q, {limit: limit, createdAt: -1})
+      q.type = @params.query?.filterType if @params.query?.filterType
+      q.tags = @params.query?.filterTag if @params.query?.filterTag
+      Meteor.subscribe('proposals', q, {createdAt: -1})
     tempalte: 'proposals'
     fastRender: true
     data: ->
       page: 'proposals'
-      proposals: Proposal.all(createdAt: -1)
-      limit: parseInt(@params.limit || 10)
-      filterType: @params.filterType
-      filterTag: @params.filterTag
+      q = {}
+      q.type = @params.query?.filterType if @params.query?.filterType
+      q.tags = @params.query?.filterTag if @params.query?.filterTag
+      proposals: Proposal.where(q, {createdAt: -1})
+      filterType: @params.query?.filterType
+      filterTag: @params.query?.filterTag
     onAfterAction: ->
       document.title = "Proposals | Reversim Summit 2015"


Added inputProvider to runner, some refactoring

--- lib/runner.coffee
+++ lib/runner.coffee
@@ -4,23 +4,22 @@
 class Runner
   bufferedProcess: null

-  constructor: (@runOptions, @emitter = new Emitter) ->
+  constructor: (@runOptions, @inputProvider = null, @emitter = new Emitter) ->

   run: (command, extraArgs, codeContext) ->
     @startTime = new Date()
-    @command = command
-
-    args = @args(codeContext, extraArgs)
-    exit = @onExit
-    stdout = @stdoutFunc
-    stderr = @stderrFunc
-    options = @options()

     @bufferedProcess = new BufferedProcess({
-      command, args, options, stdout, stderr, exit
+      command,
+      @args(codeContext, extraArgs),
+      @options(),
+      @stdoutFunc,
+      @stderrFunc,
+      @onExit
     })

-    @stdinFunc(@bufferedProcess.process.stdin) if @stdinFunc
+    if @inputProvider
+      @inputProvider.write(@bufferedProcess.process.stdin)

     @bufferedProcess.onWillThrowError(@createOnErrorFunc(command))

@@ -36,10 +35,8 @@
   onDidWriteToStderr: (callback) =>
     @emitter.on 'did-write-to-stderr', callback

-  stdinFunc: null
-
   destroy: ->
-    @eitter.dispose()
+    @emitter.dispose()

   getCwd: ->
     cwd = @runOptions.workingDirectory
@@ -71,7 +68,6 @@
     (nodeError) =>
       @bufferedProcess = null
       @emitter.emit 'did-not-run', { command: command }
-      @view.showUnableToRunError(command)
       nodeError.handle()

   onDidNotRun: (callback) =>
@@ -85,4 +81,4 @@
     args = (@runOptions.cmdArgs.concat extraArgs).concat @runOptions.scriptArgs
     if not @runOptions.cmd? or @runOptions.cmd is ''
       args = codeContext.shebangCommandArgs().concat args
-
+    args


it drove me crazy that cloud 9 always showed an error there.

--- lib/server.coffee
+++ lib/server.coffee
@@ -42,8 +42,7 @@

   respond: (res, content) ->
       data = JSON.stringify(
-        content['data'] ?
-        passphrase: content['phrase'], code: content['code']
+        content['data'] ? passphrase: content['phrase'], code: content['code']
       )

       res.writeHead content['code'],


change int to float for student progress display

--- common/lib/xmodule/xmodule/js/src/capa/display.coffee
+++ common/lib/xmodule/xmodule/js/src/capa/display.coffee
@@ -42,7 +42,7 @@
     progress = "(#{detail} points)"
     if status == 'none' and detail? and detail.indexOf('/') > 0
         a = detail.split('/')
-        possible = parseInt(a[1])
+        possible = parseFloat(a[1])
         if possible == 1
             # i18n
             progress = "(#{possible} point possible)"


fix(copy): Sending draft => Sending message. Resolves #1275

--- src/flux/tasks/send-draft.coffee
+++ src/flux/tasks/send-draft.coffee
@@ -47,7 +47,7 @@
     super

   label: ->
-    "Sending draft..."
+    "Sending message..."

   shouldDequeueOtherTask: (other) ->
     other instanceof SendDraftTask and other.draft.clientId is @draft.clientId


- added more tests for various option parsers etc:  * seems to me they can be split into two main categories : "parseParamInto", and  "parseOption", the later using the former...

--- optParse.coffee
+++ optParse.coffee
@@ -33,7 +33,7 @@
   # into a vector with equal x,y,z, if a boolean is passed and is true, take defaultvalue, otherwise defaultvalue2
   if optionname of options
     if options[optionname] == false or options[optionname] == true
-      doCenter = parseOptionAsBool(options,optionname,false)
+      doCenter = parseOptionAsBool(options,optionname, false)
       if doCenter
         options[optionname]=defaultValue
       else
@@ -176,13 +176,20 @@
   stuff.toString(2)

 parseCenter = (options, optionname, defaultValue, defaultValue2, vectorClass) ->
-  # Parse a "center" option and force into a THREE.Vector3. If a scalar is passed it is converted
+  # Parse a "center" option and force into a THREE.Vector3/THREE.Vector2. If a scalar is passed it is converted
   # into a vector with equal x,y,z, if a boolean is passed and is true, take defaultvalue, otherwise defaultvalue2
+  #if defaultValue
+
+  if vectorClass == THREE.Vector3
+    defaultValue = parseParamAs3dVector(defaultValue)
+    defaultValue2 = parseParamAs3dVector(defaultValue2)
+
   if optionname of options
     centerOption = options[optionname]
     if centerOption instanceof Array
-      newDefaultValue = new vectorClass(defaultValue)
-      newDefaultValue2 = new vectorClass(defaultValue2)
+
+      newDefaultValue = new vectorClass(defaultValue.x, defaultValue.y, defaultValue.z)
+      newDefaultValue2 = new vectorClass(defaultValue2.x, defaultValue2.y, defaultValue2.z)
       for component, index  in centerOption
         if typeof component is 'boolean'
           if index is 0
@@ -200,6 +207,7 @@
         else
           options[optionname]=defaultValue

+  console.log "default",defaultValue,"default2",defaultValue2
   result = parseOption(options, optionname, defaultValue)
   result = new vectorClass(result)
   result
@@ -796,13 +804,13 @@
 module.exports.parseOptionAs2DVector = parseOptionAs2DVector
 module.exports.parseOptionAsFloat = parseOptionAsFloat
 module.exports.parseOptionAsInt = parseOptionAsInt
-module.exports.parseOptionAsInt = parseOptionAsBool
+module.exports.parseOptionAsBool = parseOptionAsBool
 module.exports.parseOptionAsLocations = parseOptionAsLocations
 module.exports.parseCenter = parseCenter

 #added
 module.exports.parseParamAs3dVector = parseParamAs3dVector
-
+module.exports.parseParamAs2dVector = parseParamAs2dVector

 #needed ??


Bugfix: allow changing route position more than once.

--- src/views/route-settings.coffee
+++ src/views/route-settings.coffee
@@ -283,6 +283,7 @@
             ev.stopPropagation()
             @_reset()
             position = @pendingPosition
+            position.clear()
             switch $(ev.currentTarget).attr 'data-route-node'
                 when 'start' then @model.setOrigin position
                 when 'end' then @model.setDestination position


fix param

fix param

--- app.coffee
+++ app.coffee
@@ -22,7 +22,7 @@
       if !err
         args = (
           'in0': result.loginReturn
-          'in1': resp.query.project
+          'in1': req.query.project
         )
         client.getVersions args, (err, result) ->
           console.log result


removed credentials for cors

removed credentials for cors

--- app.coffee
+++ app.coffee
@@ -18,7 +18,7 @@
 app.use express.bodyParser()
 app.use express.methodOverride()
 app.use express.session secret: process.env.SESSION_SECRET
-app.use cors({origin: true, credentials: true, headers: ['X-Requested-With']})
+app.use cors({origin: true, headers: ['X-Requested-With']})

 app.use passport.initialize()
 app.use passport.session()


Small typo in last commit, trying to make privateAuth work

--- app.coffee
+++ app.coffee
@@ -34,7 +34,7 @@
   app.use privateAuth

 if process.env.NODE_ENV is 'staging'
-  console.log staging
+  console.log 'staging'
   app.use privateAuth

 # Routes


Reduce code duplication

Reduce code duplication

--- app.coffee
+++ app.coffee
@@ -378,6 +378,11 @@
 			# and finally call the callback
 			(callback true) if callback?

+askForCredentials = (req, res) ->
+	res.type 'txt'
+	res.setHeader 'WWW-Authenticate', 'Basic realm="Please provide proper username and password to access this package"'
+	res.send 401, 'Please provide proper username and password to access this package'
+
 app.all '/', (req, res) ->
 	callback = (username) ->
 		host = config.basePath ? "#{req.protocol}://#{req.header 'host'}"
@@ -495,11 +500,9 @@
 				else if result
 					callback req.auth.username
 				else
-					res.setHeader 'WWW-Authenticate', 'Basic realm="Invalid password or username"'
-					res.send 401, 'Invalid password or username'
+					askForCredentials req, res
 		else
-			res.setHeader 'WWW-Authenticate', 'Basic realm="Invalid password or username"'
-			res.send 401, 'Invalid password or username'
+			askForCredentials req, res
 	else
 		callback ''

@@ -515,8 +518,7 @@
 					res.sendfile "#{config.packageFolder}/#{req.params[0]}/#{req.params[1].toLowerCase()}.tar", (err) -> res.send 404, '404 Not Found' if err?
 				else
 					logger.log "notice", "#{username} tried to download #{req.params[0]}/#{req.params[1].toLowerCase()}"
-					res.setHeader 'WWW-Authenticate', 'Basic realm="Please provide proper username and password to access this package"'
-					res.send 401, 'Please provide proper username and password to access this package'
+					askForCredentials req, res
 			else
 				res.send 404, '404 Not Found'

@@ -529,13 +531,9 @@
 				if result
 					callback req.auth.username
 				else
-					res.setHeader 'WWW-Authenticate', 'Basic realm="Please provide proper username and password to access this package"'
-					res.send 401, 'Please provide proper username and password to access this package'
-					return
+					askForCredentials req, res
 		else
-			res.setHeader 'WWW-Authenticate', 'Basic realm="Please provide proper username and password to access this package"'
-			res.send 401, 'Please provide proper username and password to access this package'
-			return
+			askForCredentials req, res
 	else
 		callback ''


fix log message

fix log message

--- app.coffee
+++ app.coffee
@@ -125,7 +125,7 @@
 		logger.log "server will stop accepting connections"

 server.listen port, host, ->
-	logger.log("filestore store listening on #{host}:#{port}")
+	logger.log("filestore listening on #{host}:#{port}")

 process.on 'SIGTERM', () ->
 	logger.log("filestore got SIGTERM, shutting down gracefully")


coffeenize rest of app.coffee

coffeenize rest of app.coffee

--- app.coffee
+++ app.coffee
@@ -34,38 +34,36 @@

 app.use('/', routes)
 app.use('/pages', pages)
-`
-// catch 404 and forward to error handler
-app.use(function(req, res, next) {
-    var err = new Error('Not Found');
-    err.status = 404;
-    next(err);
-});

-// error handlers
+# catch 404 and forward to error handler
+app.use((req, res, next) ->
+  err = new Error('Not Found')
+  err.status = 404
+  next(err)
+)

-// development error handler
-// will print stacktrace
-if (app.get('env') === 'development') {
-    app.use(function(err, req, res, next) {
-        res.status(err.status || 500);
-        res.render('error', {
-            message: err.message,
-            error: err
-        });
-    });
-}
+# error handlers

-// production error handler
-// no stacktraces leaked to user
-app.use(function(err, req, res, next) {
-    res.status(err.status || 500);
+# development error handler
+# will print stacktrace
+if app.get('env') is 'development'
+  app.use((err, req, res, next) ->
+    res.status(err.status or 500)
     res.render('error', {
-        message: err.message,
-        error: {}
-    });
-});
+      message: err.message,
+      error: err
+    })
+  )
+
+# production error handler
+# no stacktraces leaked to user
+app.use((err, req, res, next) ->
+  res.status(err.status or 500)
+  res.render('error', {
+    message: err.message,
+    error: {}
+  })
+)

-module.exports = app;
-`
+module.exports = app


whoops

whoops

--- lib/sorter.coffee
+++ lib/sorter.coffee
@@ -52,7 +52,6 @@
 					text = text.slice(0, -1)
 				else
 					end = ''
-					style="top: 1px; bottom: 2px;" a="test"

 				# map
 				separations = [


Highlight newest option and small clean up

--- jsapp/xlform/src/view.choices.coffee
+++ jsapp/xlform/src/view.choices.coffee
@@ -51,6 +51,7 @@
         i = @model.options.length
         @addEmptyOption("Option #{i+1}")
         @model.getSurvey()?.trigger('change')
+        @$el.children().eq(0).children().eq(i).find('input.option-view-input').select()
       )

       @$el.append(btn)
@@ -163,7 +164,6 @@
         if index >= localOptionView.length
           $(this.el).parent().siblings().find('div.editable-wrapper').eq(0).focus()

-        localOptionView.eq(index).focus()
         localOptionView.eq(index).select()

     remove: ()->


Moving auto-indent specs to appropriate places

--- spec/app/edit-session-spec.coffee
+++ spec/app/edit-session-spec.coffee
@@ -2370,7 +2370,6 @@
               expect(editSession.indentationForBufferRow(6)).toBe editSession.indentationForBufferRow(5)

           it "does not indent the line preceding the newline", ->
-            config.set("editor.autoIndent", true)
             editSession.setCursorBufferPosition([2, 0])
             editSession.insertText('  var this-line-should-be-indented-more\n')
             expect(editSession.indentationForBufferRow(1)).toBe 1
@@ -2383,7 +2382,7 @@

       describe "when inserted text matches a decrease indent pattern", ->
         describe "when the preceding line matches an increase indent pattern", ->
-          it "decreases the indentation of the line to match that of the preceding line", ->
+          it "decreases the indentation to match that of the preceding line", ->
             editSession.setCursorBufferPosition([1, Infinity])
             editSession.insertText('\n    ')
             expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
@@ -2391,12 +2390,17 @@
             expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1)

         describe "when the preceding line doesn't match an increase indent pattern", ->
-          it "decreases the indentation of the line to be one level below that of the preceding line", ->
+          it "decreases the indentation to be one level below that of the preceding line", ->
             editSession.setCursorBufferPosition([3, Infinity])
             editSession.insertText('\n    ')
             expect(editSession.indentationForBufferRow(4)).toBe editSession.indentationForBufferRow(3)
             editSession.insertText('}', autoDecreaseIndent: true)
             expect(editSession.indentationForBufferRow(4)).toBe editSession.indentationForBufferRow(3) - 1
+
+          it "doesn't break when decreasing the indentation on a row that has no indentation", ->
+            editSession.setCursorBufferPosition([12, Infinity])
+            editSession.insertText("\n}; # too many closing brackets!", autoDecreaseIndent: true)
+            expect(editSession.lineForBufferRow(13)).toBe "}; # too many closing brackets!"

       describe "when inserted text does not match a decrease indent pattern", ->
         it "does not the indentation", ->
@@ -2468,19 +2472,6 @@
       expect(editSession.lineForBufferRow(3)).toBe "      inside=true"
       expect(editSession.lineForBufferRow(4)).toBe "    }"
       expect(editSession.lineForBufferRow(5)).toBe "    i=1"
-
-  describe ".autoDecreaseIndentForRow()", ->
-      it "doesn't outdent the first and only row", ->
-        editSession.selectAll()
-        editSession.insertText("  }")
-        editSession.autoDecreaseIndentForBufferRow(0)
-        expect(editSession.lineForBufferRow(0)).toBe "  }"
-
-      it "doesn't outdent a row that is already fully outdented", ->
-        editSession.selectAll()
-        editSession.insertText("var i;\n}")
-        editSession.autoDecreaseIndentForBufferRow(1)
-        expect(editSession.lineForBufferRow(1)).toBe "}"

   describe ".destroy()", ->
     it "destroys all markers associated with the edit session", ->


Spec :lipstick:

Spec :lipstick:

--- spec/app/edit-session-spec.coffee
+++ spec/app/edit-session-spec.coffee
@@ -2203,28 +2203,30 @@
       editSession.cutSelectedText()

     describe "editor.autoIndent", ->
-      describe "when `indent` is called", ->
-        it "auto-indents line if editor.autoIndent is true", ->
-          editSession.setCursorBufferPosition([1, 30])
-          editSession.insertText("\n ")
-          expect(editSession.lineForBufferRow(2)).toBe " "
-
-          config.set("editor.autoIndent", true)
-          editSession.indent()
-          expect(editSession.lineForBufferRow(2)).toBe "    "
-
-        it "does not auto-indent line if editor.autoIndent is false", ->
-          editSession.setCursorBufferPosition([1, 30])
-          editSession.insertText("\n ")
-          expect(editSession.lineForBufferRow(2)).toBe " "
-
-          config.set("editor.autoIndent", false)
-          editSession.indent()
-          expect(editSession.lineForBufferRow(2)).toBe "   "
+      describe "when editor.autoIndent is false (default)", ->
+        describe "when `indent` is triggered", ->
+          it "does not auto-indent the line", ->
+            editSession.setCursorBufferPosition([1, 30])
+            editSession.insertText("\n ")
+            expect(editSession.lineForBufferRow(2)).toBe " "
+
+            config.set("editor.autoIndent", false)
+            editSession.indent()
+            expect(editSession.lineForBufferRow(2)).toBe "   "

       describe "when editor.autoIndent is true", ->
         beforeEach ->
           config.set("editor.autoIndent", true)
+
+        describe "when `indent` is triggered", ->
+          it "auto-indents the line", ->
+            editSession.setCursorBufferPosition([1, 30])
+            editSession.insertText("\n ")
+            expect(editSession.lineForBufferRow(2)).toBe " "
+
+            config.set("editor.autoIndent", true)
+            editSession.indent()
+            expect(editSession.lineForBufferRow(2)).toBe "    "

         describe "when a newline is added", ->
           describe "when the line preceding the newline adds a new level of indentation", ->


Take the scrollbar width into account in specs

--- spec/editor-component-spec.coffee
+++ spec/editor-component-spec.coffee
@@ -258,7 +258,7 @@
           editor.setText "a line that wraps "
           editor.setSoftWrap(true)
           runSetImmediateCallbacks()
-          componentNode.style.width = 16 * charWidth + 'px'
+          componentNode.style.width = 16 * charWidth + editor.getVerticalScrollbarWidth() + 'px'
           component.measureHeightAndWidth()
           runSetImmediateCallbacks()

@@ -2003,7 +2003,7 @@
       expect(componentNode.querySelectorAll('.line')).toHaveLength(4 + lineOverdrawMargin + 1)

       gutterWidth = componentNode.querySelector('.gutter').offsetWidth
-      componentNode.style.width = gutterWidth + 14 * charWidth + 'px'
+      componentNode.style.width = gutterWidth + 14 * charWidth + editor.getVerticalScrollbarWidth() + 'px'
       advanceClock(component.domPollingInterval)
       runSetImmediateCallbacks()
       expect(componentNode.querySelector('.line').textContent).toBe "var quicksort "


fix issue where jQuery ready event gets called twice

--- src/main/coffee/SimpleMVC.coffee
+++ src/main/coffee/SimpleMVC.coffee
@@ -290,7 +290,7 @@
             location.replace(urlWithoutBase + "#" + uri)
         this.triggerEvent("navigated", uri)

-    navigate: (uri, callRouteFn = false) ->
+    navigate: (uri, callRouteFn = false, addState = true) ->
         shortUri = uri.replace(new RegExp("^" + this.baseUrl), "")
         ret = false
         foundMatch = false
@@ -300,8 +300,8 @@
                 foundMatch = matches?
                 ret = v.apply(this, matches.slice(1)) if matches? && callRouteFn
                 ret = matches? && !callRouteFn if !callRouteFn
-        this.addNewState(uri) if ret
+        this.addNewState(uri) if ret && addState
         ret

-    start: (callRouteFn = true) =>
-        this.navigate(location.pathname, callRouteFn)+    start: () =>
+        this.navigate(location.pathname, true, false)