remove helper method typeIsArray

remove helper method typeIsArray

--- src/onedollar.coffee
+++ src/onedollar.coffee
@@ -151,4 +151,4 @@

     console.log '=', length

-    return length
+    return length


adding in mocked getCurrentQuery in component testing

--- test/components/helpers/component-testing.coffee
+++ test/components/helpers/component-testing.coffee
@@ -11,6 +11,7 @@
 ROUTER = null
 CURRENT_ROUTER_PARAMS = null
 CURRENT_ROUTER_PATH   = null
+CURRENT_ROUTER_QUERY = null
 # Mock a router for the context
 beforeEach ->
   sandbox = sinon.sandbox.create()
@@ -19,6 +20,7 @@
   ROUTER.isActive = sandbox.spy()
   ROUTER.transitionTo = sandbox.spy()
   ROUTER.getCurrentPath = sandbox.spy( -> CURRENT_ROUTER_PATH )
+  ROUTER.getCurrentQuery = sandbox.spy( -> CURRENT_ROUTER_QUERY )
   ROUTER.match = sandbox.spy()
   ROUTER.getCurrentParams = sandbox.spy( -> CURRENT_ROUTER_PARAMS )
 afterEach ->
@@ -40,6 +42,7 @@
   renderComponent: (component, options = {}) ->
     options.props ||= {}
     CURRENT_ROUTER_PARAMS = options.routerParams or {}
+    CURRENT_ROUTER_QUERY = options.routerQuery or {}
     CURRENT_ROUTER_PATH   = options.routerPath   or '/'
     root = document.createElement('div')
     promise = new Promise( (resolve, reject) ->


fix tests

fix tests

--- test/dummy/spec/javascripts/spec/files.js.coffee
+++ test/dummy/spec/javascripts/spec/files.js.coffee
@@ -25,7 +25,6 @@
       file.done (info) ->
         for prop in ['uuid', 'name', 'size', 'isImage', 'isStored']
           expect(info[prop]).toBe utils.toFileInfo(kitty)[prop]
-        expect(info.preview).toEqual jasmine.any(Function)

   it "should failed to be created if server responded with error", ->


:memo:

:memo:

--- src/config.coffee
+++ src/config.coffee
@@ -687,7 +687,7 @@
   #
   # Returns a {Promise} that is either resolved or rejected according to the
   # `{Promise}` returned by `callback`. If `callback` throws an error, a
-  # rejected {Promise} will be returned.
+  # rejected {Promise} will be returned instead.
   transactAsync: (callback) ->
     @beginTransaction()
     endTransaction = (resolveOrReject) => (args...) =>


Use setTimeout to set ticker

Use setTimeout to set ticker

--- views/components/main/parts/countdown-timer.cjsx
+++ views/components/main/parts/countdown-timer.cjsx
@@ -3,20 +3,23 @@

 class Ticker
   constructor: ->
-    @intervalId = null
+    @counting = false
     @callbacks = new Map()
   tick: () =>
     now = Date.now()
     @callbacks.forEach (f) -> f(now)
+  count: () =>
+    return if !@counting
+    @tick()
+    setTimeout @count, 1000
   start: ->
-    @intervalId = setInterval @tick, 1000
+    @counting = true
+    @count()
   stop: ->
-    @callbacks.clear()
-    clearInterval @intervalId
-    @intervalId = null
+    @counting = false
   reg: (key, func) ->
     @callbacks.set key, func
-    @start() if !@intervalId?
+    @start() if !@counting
   unreg: (key) ->
     @callbacks.delete key
     @stop() if @callbacks.size is 0


Made this countdowns run smoother

Made this countdowns run smoother

--- views/components/main/parts/countdown-timer.cjsx
+++ views/components/main/parts/countdown-timer.cjsx
@@ -64,12 +64,15 @@
   stopTick: ->
     ticker.unreg @props.countdownId
   tick: (currentTime) ->
+    actualRemaining = @constructor.getTimeRemaining(@state.completeTime, currentTime)
+    @timeRemaining = actualRemaining if Math.abs(@timeRemaining - actualRemaining) > 2
     @timeRemaining = @constructor.getTimeRemaining(@state.completeTime, currentTime)
     @stopTick() if @timeRemaining < 1
     if @state.completeTime >= 0
       @textLabel.textContent = resolveTime @timeRemaining if @textLabel?
       @props.tickCallback(@timeRemaining) if @props.tickCallback?
       @props.completeCallback() if @timeRemaining < 1 and @props.completeCallback?
+    @timeRemaining--
   render: ->
      @textLabel = ref}>{resolveTime @timeRemaining}


adding hsl methods

adding hsl methods

--- test/color.coffee
+++ test/color.coffee
@@ -251,6 +251,11 @@
   equals darkColor.g(), 24
   equals darkColor.b(), 12

+test "#saturate", ->
+  color = Color(45, 26, 100)
+
+  ok color.saturate(0.5)
+
 test "Color.mix", ->
   color1 = Color(33, 55, 100, 1)
   color2 = Color(50, 90, 200, 0.5)


comment

comment

--- perf/trades.coffee
+++ perf/trades.coffee
@@ -1,4 +1,4 @@
-# This tests does trades in pairs to push funds back and forth between accounts
+# This test does trades in pairs to push funds back and forth between accounts
 # 2 trades will be made in each iteration
 ITERATIONS = 10000


Referral System: add referral system back

--- client/Account/AppController.coffee
+++ client/Account/AppController.coffee
@@ -28,8 +28,9 @@
     develop :
       title : "Develop"
       items : [
-        { slug : 'SSH',  title : "SSH keys",    listHeader: "Your SSH Keys",    listType: "keys" }
-        { slug : 'Keys', title : "Koding Keys", listHeader: "Your Koding Keys", listType: "kodingKeys" }
+        { slug : 'SSH',       title : "SSH keys",           listHeader: "Your SSH Keys",          listType: "keys" }
+        { slug : 'Keys',      title : "Koding Keys",        listHeader: "Your Koding Keys",       listType: "kodingKeys" }
+        { slug : 'Referral',  title : "Referral System",    listHeader: "Your Referral Options",  listType: "referralSystem" }
       ]
     danger  :
       title : "Danger"


websockets support. much better :wolf:

websockets support. much better :wolf:

--- config/env/development.coffee
+++ config/env/development.coffee
@@ -25,27 +25,27 @@
   # origin: 'http://localhost:8000'

 models:
-  # connection: 'localDiskDb'
-  connection: 'MongoHeroku'
+  connection: 'localDiskDb'
+  # connection: 'MongoHeroku'
   migrate: 'alter'

 sockets:
-  # adapter: 'memory'
-  adapter: 'redis'
-  db: 'redis-sailor-twitter-backend',
-  host: 'pub-redis-16675.eu-west-1-1.1.ec2.garantiadata.com',
-  port: 16675,
-  pass: process.env.SAILS_SOCKET_PASSWORD
+  adapter: 'memory'
+  # adapter: 'redis'
+  # db: 'redis-sailor-twitter-backend',
+  # host: 'pub-redis-16675.eu-west-1-1.1.ec2.garantiadata.com',
+  # port: 16675,
+  # pass: process.env.SAILS_SOCKET_PASSWORD

 session:
-  # adapter: 'memory'
-  adapter: 'redis'
-  db: 'redistogo'
-  host: 'jack.redistogo.com'
-  port: 10816
-  pass: process.env.SAILS_SESSION_PASSWORD
-  ttl: 150
-  prefix: 'sess:'
+  adapter: 'memory'
+  # adapter: 'redis'
+  # db: 'redistogo'
+  # host: 'jack.redistogo.com'
+  # port: 10816
+  # pass: process.env.SAILS_SESSION_PASSWORD
+  # ttl: 150
+  # prefix: 'sess:'

 log:
   level: 'info'


Fix printconfig command

Fix printconfig command

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>

--- config/generateRunFile.coffee
+++ config/generateRunFile.coffee
@@ -613,7 +613,8 @@

     elif [ "$1" == "printconfig" ]; then

-      printconfig $@
+      shift
+      echo ${!1}

     elif [[ "$1" == "log" || "$1" == "logs" ]]; then


home/stacks: show disabled users stacks section in stacks page

--- client/home/lib/stacks/index.coffee
+++ client/home/lib/stacks/index.coffee
@@ -5,6 +5,7 @@
 HomeStacksCreate = require './homestackscreate'
 HomeStacksTeamStacks = require './homestacksteamstacks'
 HomeStacksPrivateStacks = require './homestacksprivatestacks'
+HomeStacksDisabledUsers = require './homestacksdisableduserstacks'
 HomeStacksDrafts = require './homestacksdrafts'
 HomeStacksTabHandle = require './homestackstabhandle'

@@ -50,7 +51,7 @@

     { mainController, computeController, reactor } = kd.singletons

-    kd.singletons.mainController.ready =>
+    mainController.ready =>
       @createStacksViews()
       @createVMsViews()
       @createCredentialsViews()
@@ -79,8 +80,11 @@

   createStacksViews: ->

-    EnvironmentFlux.actions.loadTeamStackTemplates()
-    EnvironmentFlux.actions.loadPrivateStackTemplates()
+    { reactor } = kd.singletons
+    { actions, getters } = EnvironmentFlux
+
+    actions.loadTeamStackTemplates()
+    actions.loadPrivateStackTemplates()

     @stacks.addSubView view = new HomeStacksCreate

@@ -93,6 +97,27 @@

     @stacks.addSubView headerize 'Private Stacks'
     @stacks.addSubView sectionize 'Private Stacks', HomeStacksPrivateStacks, { delegate : this }
+
+    @stacks.addSubView disabledUsersHeader = headerize 'Disabled User Stacks'
+    @stacks.addSubView disabledUsersSection = sectionize 'Disabled User Stacks', HomeStacksDisabledUsers, { deletage: this }
+
+    showDisabledUsers = ->
+      disabledUsersHeader.show()
+      disabledUsersSection.show()
+
+    hideDisabledUsers = ->
+      disabledUsersHeader.hide()
+      disabledUsersSection.hide()
+
+
+    cleanObserver = reactor.observe getters.disabledUsersStackTemplates, (templates) ->
+      if templates.size
+      then showDisabledUsers()
+      else hideDisabledUsers()
+
+    hideDisabledUsers()
+
+    @once 'KDObjectWillBeDestroyed', cleanObserver

     @stacks.addSubView headerize 'Drafts'
     @stacks.addSubView sectionize 'Drafts', HomeStacksDrafts, { delegate : this }


About: copy changed

About: copy changed

--- client/landing/About/AppView.coffee
+++ client/landing/About/AppView.coffee
@@ -120,8 +120,8 @@
         {{> @devrim}}
         {{> @sinan}}

-
About Koding

-
Founded by Devrim & Sinan in 2009

+
About Koding

+
Social development in your browser

Koding is a developer community and cloud development environment where developers come together and code in the browser – with a real development server to run their code. Developers can work, collaborate, write and run apps without jumping through hoops and spending unnecessary money.


Route: removed Verify route

Route: removed Verify route

--- client/landing/Login/AppView.coffee
+++ client/landing/Login/AppView.coffee
@@ -370,10 +370,6 @@
       mainView.$().css "opacity", 1

       firstRoute = KD.getSingleton("router").visitedRoutes.first
-
-      if firstRoute and /^\/Verify/.test firstRoute
-        firstRoute = "/"
-

       if firstRoute and /^\/Reset/.test firstRoute
         firstRoute = "/"


Preselect in textbox. Try to fix keypress problem on mobile.

--- src/sekritweb.coffee
+++ src/sekritweb.coffee
@@ -31,6 +31,7 @@
     require -> ciphertext
     require -> ciphertext.length >= 46
     sjcl.decrypt password, sekrit2crypt ciphertext
+

   null
 )
@@ -66,6 +67,10 @@
       window.location.hash = '#' + (createCirkle friendly)
     catch e
       alert e
+
+  $('textarea[readonly]').mouseenter ->
+    $(@).select()
+    #$(@)[0].setSelectionRange 0, 9999

 main = ->
   $content = $ '#content'
@@ -103,7 +108,7 @@
     $('#have-circkle').slideDown()
     cirkle = new Cipher cirkleString
     $msgIn = $ '#msg-in'
-    $msgIn.keypress ->
+    $msgIn.keyup ->
       afterTick ->
         $('#sekrit-out').text cirkle.encrypt $msgIn.val().trim()
         $('#secret-out-wrapper').slideDown()


Got the y-axis to remain positive.

Got the y-axis to remain positive.

It turns out it was going negative because of something I'd done; undoing it made things better.

--- src/lib/rickshaw_strip_chart.coffee
+++ src/lib/rickshaw_strip_chart.coffee
@@ -71,7 +71,7 @@
       height: 300
       renderer: 'line'
       series: @values
-      min: -50 # 'auto'
+      min: 'auto'
     })
     xAxis = new Rickshaw.Graph.Axis.X({
       graph: @chart
@@ -83,12 +83,6 @@
       graph: @chart
       xFormatter: (year) -> "Year #{year}"
       yFormatter: (numCranes) -> "#{Math.round(numCranes)} cranes"
-    })
-    x_axis = new Rickshaw.Graph.Axis.X({
-      graph: @chart
-    })
-    y_axis = new Rickshaw.Graph.Axis.Y({
-      graph: @chart
     })

   drawChart: ->


Improved clarity of Editor not responding actions

Changed "Close" to "Force Close" because "Close" might imply closing the Alert/Dialog instead of the editor, whereas "Force Close" better describes the action that button will take.

--- src/main-process/atom-window.coffee
+++ src/main-process/atom-window.coffee
@@ -160,7 +160,7 @@

       chosen = dialog.showMessageBox @browserWindow,
         type: 'warning'
-        buttons: ['Close', 'Keep Waiting']
+        buttons: ['Force Close', 'Keep Waiting']
         message: 'Editor is not responding'
         detail: 'The editor is not responding. Would you like to force close it or just keep waiting?'
       @browserWindow.destroy() if chosen is 0


adds empty hosts to initial Assetfile

adds empty hosts to initial Assetfile

--- tasks/init.coffee
+++ tasks/init.coffee
@@ -40,4 +40,9 @@
   rollbar:
     accessToken: process.env.ROLLBAR_ACCESS_TOKEN
     version: process.env.ECRU_COMMIT
+
+  hosts: [
+    # '//your.site.or.cdn'
+  ]
+
 '''


Fix unintended unload prevention for spec windows

--- src/main-process/atom-window.coffee
+++ src/main-process/atom-window.coffee
@@ -194,7 +194,7 @@

   prepareToUnload: ->
     if @isSpecWindow()
-      return Promise.resolve()
+      return Promise.resolve(true)
     @lastPrepareToUnloadPromise = new Promise (resolve) =>
       callback = (event, result) =>
         if BrowserWindow.fromWebContents(event.sender) is @browserWindow


remove service protection

remove service protection

--- lib/Vein.coffee
+++ lib/Vein.coffee
@@ -28,9 +28,10 @@
     # TODO: middleware here
     return res.error "Invalid message" unless res.valid is true
     return res.error "Invalid service" unless @services[res.req.service]?
-    try
-      @services[res.req.service] res, res.req.args...
-    catch err
-      return res.error err
+    @services[res.req.service] res, res.req.args...
+    #try
+    #  @services[res.req.service] res, res.req.args...
+    #catch err
+    #  return res.error err

 module.exports = Vein


Simplify implementation of auth header parsing

--- lib/auth.coffee
+++ lib/auth.coffee
@@ -19,18 +19,10 @@

 exports.parseAuthHeader = (authHeader) ->
-  res = {}
-  i = authHeader.indexOf(' ')
-  res['method'] = authHeader.slice(0, i)
-
-  options = authHeader.slice(i + 1)
-  regex = /([a-z]+)="([a-zA-Z0-9\/\.@\-\+,= ]+)"/g
-  while (r = regex.exec(options)) != null
-    res[r[1]] = r[2]
-
-  regex = /([a-z]+)=([a-zA-Z0-9\/\.@\-\+]+)/g
-  while (r = regex.exec(options)) != null
-    res[r[1]] = r[2]
+  res = {method: authHeader.slice(0, authHeader.indexOf(' '))}
+  re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi
+  while match = re.exec(authHeader)
+    res[match[1]] = match[2] or match[3]
   return res


back string escaping

back string escaping

--- lib/both.coffee
+++ lib/both.coffee
@@ -57,7 +57,7 @@
     searchQuery = _.extend {}, configEntry.mongoQuery

     if configEntry.searchString
-      escapedSearchString = configEntry.searchString #@_escapeRegExpStr(configEntry.searchString)
+      escapedSearchString = @_escapeRegExpStr(configEntry.searchString)
       searchQuery.$or = []
       configEntry.fields.forEach (fieldName) ->
         $orEntry = {}


hub/robots.txt: blacklist haproxy

hub/robots.txt: blacklist haproxy

--- src/smc-hub/hub_http_server.coffee
+++ src/smc-hub/hub_http_server.coffee
@@ -112,6 +112,7 @@
                   Disallow: /projects/*
                   Disallow: /*/raw/
                   Disallow: /*/port/
+                  Disallow: /haproxy
                   ''')
         res.end()


Refactor #decreaseListLevel

Refactor #decreaseListLevel

--- src/trix/models/composition.coffee
+++ src/trix/models/composition.coffee
@@ -256,18 +256,15 @@
       @removeCurrentAttribute(attribute)

   decreaseListLevel: ->
-    position = @getPosition()
-    locationRange = @getLocationRange()
-    {index} = locationRange
-    level = @getBlock().getAttributeLevel()
-
-    for block in @document.getBlocks().slice(index + 1)
-      if block.isListItem() and block.getAttributeLevel() > level
-        index++
-      else
-        break
-
-    locationRange.end.index = index
+    {index} = @getLocationRange()
+    endIndex = index
+    attributeLevel = @getBlock().getAttributeLevel()
+
+    while block = @document.getBlockAtIndex(endIndex + 1)
+      break unless block.isListItem() and block.getAttributeLevel() > attributeLevel
+      endIndex++
+
+    locationRange = new Trix.LocationRange [index, 0], [endIndex, 0]
     @document.removeLastListAttributeAtLocationRange(locationRange)

   canIncreaseBlockAttributeLevel: ->


Remove deletion of node directory

Remove deletion of node directory

This directory is no longer at the root of the repo

--- tasks/clean-task.coffee
+++ tasks/clean-task.coffee
@@ -7,7 +7,6 @@
     rm grunt.config.get('atom.buildDir')
     rm '/tmp/atom-coffee-cache'
     rm '/tmp/atom-cached-atom-shells'
-    rm 'node'
     rm 'atom-shell'

   grunt.registerTask 'clean', 'Delete all the build files', ->


[dist] Removed `console.log` errors and moved to proper 500 page on uploads

--- main.coffee
+++ main.coffee
@@ -133,9 +133,6 @@
 					articles: files

 app.post '/admin/uploads', users.check, (req, res) ->
-	console.log(req.body)
-	console.log(req.files)
-
 	slug = sluggify req.param 'title'
 	filename = sluggify req.files.upload.name
 	uploads.save {
@@ -147,9 +144,10 @@

 	}, req.files.upload, (err, docs) ->
 		if (err)
-			console.warn("ERROR UPLOADING: ", err)
+      console.log(':: CouchPress :: ERROR -> \n' + err)
+      res.redirect '/500'
 		else
-			res.redirect('/admin/uploads')
+			res.redirect '/admin/uploads'

 app.get '/admin/settings', users.check, (req, res) ->
   adminConfig = require('./config').admin
@@ -229,6 +227,16 @@
 app.get '/admin/*', users.check, (req, res) ->
   renderError res, "<b>404:</b> Are you sure that's the right URL?"

+app.get '/500', (req, res) ->
+  res.render front + 'view'
+    locals:
+      title: config.site.title + ' / 500'
+      article:
+        title: '500'
+        created_at: ''
+        body: "Internal Server Error"
+      config: config
+
 app.get '*', (req, res) ->
   res.render front + 'view'
     locals:


Use close instead of exit

Use close instead of exit

stdio might still be open when exit is called (http://nodejs.org/api/child_process.html#child_process_event_exit). With close you are
guaranteed that there will be no more output.

--- build/tasks/task-helpers.coffee
+++ build/tasks/task-helpers.coffee
@@ -39,7 +39,7 @@
     proc = childProcess.spawn(options.cmd, options.args, options.opts)
     proc.stdout.on 'data', (data) -> stdout.push(data.toString())
     proc.stderr.on 'data', (data) -> stderr.push(data.toString())
-    proc.on 'exit', (exitCode, signal) ->
+    proc.on 'close', (exitCode, signal) ->
       error = new Error(signal) if exitCode != 0
       results = {stderr: stderr.join(''), stdout: stdout.join(''), code: exitCode}
       grunt.log.error results.stderr if exitCode != 0


fixed test indentation

fixed test indentation

--- test/color.coffee
+++ test/color.coffee
@@ -1,408 +1,407 @@
-( ->
-
-equalEnough = (actual, expected, tolerance=3, message) ->
-  message ||= "" + expected + " within " + tolerance + " of " + actual
-  ok(expected + tolerance >= actual && expected - tolerance <= actual, message)
-
-module "Color"
-
-test "r, g, b default to 0, a defaults to 1", ->
-  color = Color()
-
-  equalEnough color.r, 0
-  equalEnough color.g, 0
-  equalEnough color.b, 0
-  equalEnough color.a, 1
-
-test "accepts a single array of length 3", ->
-  color = Color([255, 1, 2])
-
-  equalEnough color.r, 255
-  equalEnough color.g, 1
-  equalEnough color.b, 2
-
-test "accepts a single array of length 4", ->
-  color = Color([255, 4, 3, 0.8])
-
-  equalEnough color.r, 255
-  equalEnough color.g, 4
-  equalEnough color.b, 3
-  equalEnough color.a, 0.8
-
-test "accepts an array of length 3 as the first argument and an alpha value as the second", ->
-  color = Color([100, 5, 3], 0.4)
-
-  equalEnough color.r, 100
-  equalEnough color.g, 5
-  equalEnough color.b, 3
-  equalEnough color.a, 0.4
-
-test "accepts an array of length 4 as the first argument and an alpha value. The alpha value passed as the second argument overrides the alpha in the array", ->
-  color = Color([4, 20, 90, 0.1], 0.6)
-
-  equalEnough color.r, 4
-  equalEnough color.g, 20
-  equalEnough color.b, 90
-  equalEnough color.a, 0.6
-
-test "accepts an rgb string as the first argument and an alpha value as the second", ->
-  color = Color('rgb(255, 40, 30)', 0.4)
-
-  equalEnough color.r, 255
-  equalEnough color.g, 40
-  equalEnough color.b, 30
-  equalEnough color.a, 0.4
-
-test "accepts an rgba string as the first argument and an alpha value as the second. The alpha value takes precedence over the alpha from the rgba string", ->
-  color = Color('rgba(30, 20, 15, 0.8)', 0.35)
-
-  equalEnough color.r, 30
-  equalEnough color.g, 20
-  equalEnough color.b, 15
-  equalEnough color.a, 0.35
-
-test "accepts an hsl string as the first argument and an alpha value as the second", ->
-  color = Color('hsl(63, 0.4, 0.3)', 0.21)
-
-  equalEnough color.r, 104
-  equalEnough color.g, 107
-  equalEnough color.b, 46
-  equalEnough color.a, 0.21
-
-test "accepts an hsla string as the first argument and an alpha value as the second. The alpha value takes precedence over the hsla string alpha value", ->
-  color = Color('hsla(45, 0.3, 0.5, 0.1)', 0.47)
-
-  equalEnough color.r, 166
-  equalEnough color.g, 147
-  equalEnough color.b, 89
-  equalEnough color.a, 0.47
-
-test "accepts hex value of length 3 as first argument and alpha value as second argument", ->
-  color = Color('#ff0', 0.5)
-
-  equalEnough color.r, 255
-  equalEnough color.g, 255
-  equalEnough color.b, 0
-  equalEnough color.a, 0.5
-
-test "accepts hex value of length 4 as first argument and alpha value as second argument. The alpha value takes precedence over the alpha from the hex string.", ->
-  color = Color('#ef3c', 0.3)
-
-  equalEnough color.r, 238
-  equalEnough color.g, 255
-  equalEnough color.b, 51
-  equalEnough color.a, 0.3
-
-test "accepts hex value of length 6 as first argument and alpha value as second argument", ->
-  color = Color('#de34ab', 0.27)
-
-  equalEnough color.r, 222
-  equalEnough color.g, 52
-  equalEnough color.b, 171
-  equalEnough color.a, 0.27
-
-test "accepts hex value of length 8 as first argument and alpha value as second argument. Alpha value takes precedence over hex alpha.", ->
-  color = Color('#ab45cd', 0.59)
-
-  equalEnough color.r, 171
-  equalEnough color.g, 69
-  equalEnough color.b, 205
-  equalEnough color.a, 0.59
-
-test "accepts hex string of length 3", ->
-  color = Color('#df2')
-
-  equalEnough color.r, 221
-  equalEnough color.g, 255
-  equalEnough color.b, 34
-  equalEnough color.a, 1.0
-
-test "accepts hex string of length 4", ->
-  color = Color('#c987')
-
-  equalEnough color.r, 204
-  equalEnough color.g, 153
-  equalEnough color.b, 136
-  equalEnough color.a.toFixed(2), 0.47
-
-test "accepts hex string of length 6", ->
-  color = Color('#cdef12')
-
-  equalEnough color.r, 205
-  equalEnough color.g, 239
-  equalEnough color.b, 18
-  equalEnough color.a, 1.0
-
-test "accepts hex string of length 8", ->
-  color = Color('#ef1234af')
-
-  equalEnough color.r, 239
-  equalEnough color.g, 18
-  equalEnough color.b, 52
-  equalEnough color.a.toFixed(2), 0.69
-
-test "accepts 3 numeric values", ->
-  color = Color(34, 54, 39)
-
-  equalEnough color.r, 34
-  equalEnough color.g, 54
-  equalEnough color.b, 39
-  equalEnough color.a, 1.0
-
-test "accepts 4 numeric values", ->
-  color = Color(23, 43, 100, 0.32)
-
-  equalEnough color.r, 23
-  equalEnough color.g, 43
-  equalEnough color.b, 100
-  equalEnough color.a, 0.32
-
-test "invalid color throws error", ->
-  raises ->
-    Color("A Fake Color")
-
-test "#copy", ->
-  color = Color(123, 231, 2)
-  copiedColor = color.copy()
-
-  ok color.equal(copiedColor)
-  ok color != copiedColor
-
-test "#equal", ->
-  color1 = Color(255, 255, 255, 1)
-  color2 = Color([255, 255, 255])
-
-  ok color1.equal(color2)
-
-test "#toString", ->
-  noAlpha = Color(30, 40, 23)
-  withAlpha = Color(29, 49, 50, 0.45)
-  decimalValues = Color(34.7, 52.2, 50.1, 0.45)
-
-  equal noAlpha.toString(), "rgba(30, 40, 23, 1)"
-  equal withAlpha.toString(), "rgba(29, 49, 50, 0.45)"
-  equal decimalValues.toString(), "rgba(35, 52, 50, 0.45)"
-
-test "#toHex", ->
-  color = Color(23, 45, 100)
-
-  equal color.toHex(), "#172d64"
-
-test "#toHsl", ->
-  color = Color(45, 29, 20)
-
-  hsl = color.toHsl()
-
-  equalEnough hsl[0].round(), 22
-  equalEnough hsl[1].toFixed(2), 0.38
-  equalEnough hsl[2].toFixed(2), 0.13
-  equalEnough hsl[3], 1.0
-
-test "#hue", ->
-  color = Color(34, 54, 239).hue(20)
-
-  equalEnough color.r, 84
-  equalEnough color.g, 37
-  equalEnough color.b, 239
-  equalEnough color.a, 1.0
-
-test "#hue$", ->
-  color = Color(34, 54, 239)
-
-  color.hue$(60)
-
-  equalEnough color.r, 218
-  equalEnough color.g, 37
-  equalEnough color.b, 239
-  equalEnough color.a, 1.0
-
-test "#complement", ->
-  color = Color(10, 30, 50).complement()
-
-  equalEnough color.r, 51
-  equalEnough color.g, 31
-  equalEnough color.b, 10
-  equalEnough color.a, 1.0
-
-test "#complement$", ->
-  color = Color(10, 30, 50)
-
-  color.complement$()
-
-  equalEnough color.r, 51
-  equalEnough color.g, 31
-  equalEnough color.b, 10
-  equalEnough color.a, 1.0
-
-test "#grayscale", ->
-  color = Color(30, 40, 29).grayscale()
-
-  equalEnough color.r, 35
-  equalEnough color.g, 35
-  equalEnough color.b, 35
-  equalEnough color.a, 1.0
-
-test "#grayscale$", ->
-  color = Color(30, 40, 29)
-
-  color.grayscale$()
-
-  equalEnough color.r, 35
-  equalEnough color.g, 35
-  equalEnough color.b, 35
-  equalEnough color.a, 1.0
-
-test "#saturate", ->
-  color = Color(100, 200, 150).saturate(0.3)
-
-  equalEnough color.r, 69
-  equalEnough color.g, 232
-  equalEnough color.b, 150
-  equalEnough color.a, 1.0
-
-test "#saturate$", ->
-  color = Color(100, 200, 150)
-
-  color.saturate$(0.3)
-
-  equalEnough color.r, 69
-  equalEnough color.g, 232
-  equalEnough color.b, 150
-  equalEnough color.a, 1.0
-
-test "#desaturate", ->
-  color = Color(69, 232, 150).desaturate(0.3)
-
-  equalEnough color.r, 100
-  equalEnough color.g, 200
-  equalEnough color.b, 150
-  equalEnough color.a, 1.0
-
-test "#desaturate$", ->
-  color = Color(69, 232, 150)
-
-  color.desaturate$(0.3)
-
-  equalEnough color.r, 100
-  equalEnough color.g, 200
-  equalEnough color.b, 150
-  equalEnough color.a, 1.0
-
-test "#darken", ->
-  color = Color(45, 64, 39).darken(0.1)
-
-  equalEnough color.r, 22
-  equalEnough color.g, 32
-  equalEnough color.b, 19
-  equalEnough color.a, 1.0
-
-test "#darken$", ->
-  color = Color(45, 64, 39)
-
-  color.darken$(0.1)
-
-  equalEnough color.r, 22
-  equalEnough color.g, 32
-  equalEnough color.b, 19
-  equalEnough color.a, 1.0
-
-test "#lighten", ->
-  color = Color(22, 32, 19).lighten(0.1)
-
-  equalEnough color.r, 45
-  equalEnough color.g, 64
-  equalEnough color.b, 39
-  equalEnough color.a, 1.0
-
-test "#lighten$", ->
-  color = Color(22, 32, 19)
-
-  color.lighten$(0.1)
-
-  equalEnough color.r, 45
-  equalEnough color.g, 64
-  equalEnough color.b, 39
-  equalEnough color.a, 1.0
-
-test "#mixWith", ->
-  color1 = Color(50, 40, 60, 0.3)
-  color2 = Color(10, 5, 16, 0.2)
-
-  mixedColor = color1.mixWith(color2)
-
-  equalEnough mixedColor.r, 30
-  equalEnough mixedColor.g, (45 / 2).round()
-  equalEnough mixedColor.b, (76 / 2).round()
-  equalEnough mixedColor.a, 0.5 / 2
-
-  weightedMixedColor = color1.mixWith(color2, 0.1)
-
-  equalEnough weightedMixedColor.r, ((50 * 0.1) + (10 * 0.9)).round()
-  equalEnough weightedMixedColor.g, ((40 * 0.1) + (5 * 0.9)).round()
-  equalEnough weightedMixedColor.b, ((60 * 0.1) + (16 * 0.9)).round()
-  equalEnough weightedMixedColor.a, (0.3 * 0.1) + (0.2 * 0.9)
-
-test "#mixWith$", ->
-  color1 = Color(50, 40, 60, 0.3)
-  color2 = Color(10, 5, 16, 0.2)
-
-  color3 = Color(50, 40, 60, 0.3)
-
-  color1.mixWith$(color2)
-
-  equalEnough color1.r, 30
-  equalEnough color1.g, (45 / 2).round()
-  equalEnough color1.b, (76 / 2).round()
-  equalEnough color1.a, 0.5 / 2
-
-  color3.mixWith$(color2, 0.1)
-
-  equalEnough color3.r, ((50 * 0.1) + (10 * 0.9)).round()
-  equalEnough color3.g, ((40 * 0.1) + (5 * 0.9)).round()
-  equalEnough color3.b, ((60 * 0.1) + (16 * 0.9)).round()
-  equalEnough color3.a, (0.3 * 0.1) + (0.2 * 0.9)
-
-test "Color.random", ->
-  color = Color.random()
-
-  ok 0 <= color.r <= 255
-  ok 0 <= color.g <= 255
-  ok 0 <= color.b <= 255
-  equalEnough color.a, 1.0
-
-test "Color.mix", ->
-  mixedColor = Color.mix(
-    Color("red"),
-    Color("sky blue")
-  )
-
-  equalEnough mixedColor.r, 173
-  equalEnough mixedColor.g, 94
-  equalEnough mixedColor.b, 127
-  equalEnough mixedColor.a, 1
-
-  weightedMixedColor = Color.mix(Color(0, 255, 0, 1), Color(255, 0, 255, 0.6), 0.1)
-
-  equalEnough weightedMixedColor.r, ((0 * 0.1) + (255 * 0.9)).round()
-  equalEnough weightedMixedColor.g, ((255 * 0.1) + (0 * 0.9)).round()
-  equalEnough weightedMixedColor.b, ((0 * 0.1) + (255 * 0.9)).round()
-  equalEnough weightedMixedColor.a, (1 * 0.1) + (0.6 * 0.9)
-
-test "accepts named colors", ->
-  white = Color("white")
-
-  equalEnough white.r, 255
-  equalEnough white.g, 255
-  equalEnough white.b, 255
-  equalEnough white.a, 1.0
-
-  piPink = Color("Paul Irish Pink")
-
-  equalEnough piPink.r, 255
-  equalEnough piPink.g, 94
-  equalEnough piPink.b, 153
-  equalEnough piPink.a, 1.0
-
-module()
-
-)()+(->
+  module "Color"
+
+  equalEnough = (actual, expected, tolerance=3, message) ->
+    message ||= "" + expected + " within " + tolerance + " of " + actual
+    ok(expected + tolerance >= actual && expected - tolerance <= actual, message)
+
+  test "r, g, b default to 0, a defaults to 1", ->
+    color = Color()
+
+    equalEnough color.r, 0
+    equalEnough color.g, 0
+    equalEnough color.b, 0
+    equalEnough color.a, 1
+
+  test "accepts a single array of length 3", ->
+    color = Color([255, 1, 2])
+
+    equalEnough color.r, 255
+    equalEnough color.g, 1
+    equalEnough color.b, 2
+
+  test "accepts a single array of length 4", ->
+    color = Color([255, 4, 3, 0.8])
+
+    equalEnough color.r, 255
+    equalEnough color.g, 4
+    equalEnough color.b, 3
+    equalEnough color.a, 0.8
+
+  test "accepts an array of length 3 as the first argument and an alpha value as the second", ->
+    color = Color([100, 5, 3], 0.4)
+
+    equalEnough color.r, 100
+    equalEnough color.g, 5
+    equalEnough color.b, 3
+    equalEnough color.a, 0.4
+
+  test "accepts an array of length 4 as the first argument and an alpha value. The alpha value passed as the second argument overrides the alpha in the array", ->
+    color = Color([4, 20, 90, 0.1], 0.6)
+
+    equalEnough color.r, 4
+    equalEnough color.g, 20
+    equalEnough color.b, 90
+    equalEnough color.a, 0.6
+
+  test "accepts an rgb string as the first argument and an alpha value as the second", ->
+    color = Color('rgb(255, 40, 30)', 0.4)
+
+    equalEnough color.r, 255
+    equalEnough color.g, 40
+    equalEnough color.b, 30
+    equalEnough color.a, 0.4
+
+  test "accepts an rgba string as the first argument and an alpha value as the second. The alpha value takes precedence over the alpha from the rgba string", ->
+    color = Color('rgba(30, 20, 15, 0.8)', 0.35)
+
+    equalEnough color.r, 30
+    equalEnough color.g, 20
+    equalEnough color.b, 15
+    equalEnough color.a, 0.35
+
+  test "accepts an hsl string as the first argument and an alpha value as the second", ->
+    color = Color('hsl(63, 0.4, 0.3)', 0.21)
+
+    equalEnough color.r, 104
+    equalEnough color.g, 107
+    equalEnough color.b, 46
+    equalEnough color.a, 0.21
+
+  test "accepts an hsla string as the first argument and an alpha value as the second. The alpha value takes precedence over the hsla string alpha value", ->
+    color = Color('hsla(45, 0.3, 0.5, 0.1)', 0.47)
+
+    equalEnough color.r, 166
+    equalEnough color.g, 147
+    equalEnough color.b, 89
+    equalEnough color.a, 0.47
+
+  test "accepts hex value of length 3 as first argument and alpha value as second argument", ->
+    color = Color('#ff0', 0.5)
+
+    equalEnough color.r, 255
+    equalEnough color.g, 255
+    equalEnough color.b, 0
+    equalEnough color.a, 0.5
+
+  test "accepts hex value of length 4 as first argument and alpha value as second argument. The alpha value takes precedence over the alpha from the hex string.", ->
+    color = Color('#ef3c', 0.3)
+
+    equalEnough color.r, 238
+    equalEnough color.g, 255
+    equalEnough color.b, 51
+    equalEnough color.a, 0.3
+
+  test "accepts hex value of length 6 as first argument and alpha value as second argument", ->
+    color = Color('#de34ab', 0.27)
+
+    equalEnough color.r, 222
+    equalEnough color.g, 52
+    equalEnough color.b, 171
+    equalEnough color.a, 0.27
+
+  test "accepts hex value of length 8 as first argument and alpha value as second argument. Alpha value takes precedence over hex alpha.", ->
+    color = Color('#ab45cd', 0.59)
+
+    equalEnough color.r, 171
+    equalEnough color.g, 69
+    equalEnough color.b, 205
+    equalEnough color.a, 0.59
+
+  test "accepts hex string of length 3", ->
+    color = Color('#df2')
+
+    equalEnough color.r, 221
+    equalEnough color.g, 255
+    equalEnough color.b, 34
+    equalEnough color.a, 1.0
+
+  test "accepts hex string of length 4", ->
+    color = Color('#c987')
+
+    equalEnough color.r, 204
+    equalEnough color.g, 153
+    equalEnough color.b, 136
+    equalEnough color.a.toFixed(2), 0.47
+
+  test "accepts hex string of length 6", ->
+    color = Color('#cdef12')
+
+    equalEnough color.r, 205
+    equalEnough color.g, 239
+    equalEnough color.b, 18
+    equalEnough color.a, 1.0
+
+  test "accepts hex string of length 8", ->
+    color = Color('#ef1234af')
+
+    equalEnough color.r, 239
+    equalEnough color.g, 18
+    equalEnough color.b, 52
+    equalEnough color.a.toFixed(2), 0.69
+
+  test "accepts 3 numeric values", ->
+    color = Color(34, 54, 39)
+
+    equalEnough color.r, 34
+    equalEnough color.g, 54
+    equalEnough color.b, 39
+    equalEnough color.a, 1.0
+
+  test "accepts 4 numeric values", ->
+    color = Color(23, 43, 100, 0.32)
+
+    equalEnough color.r, 23
+    equalEnough color.g, 43
+    equalEnough color.b, 100
+    equalEnough color.a, 0.32
+
+  test "invalid color throws error", ->
+    raises ->
+      Color("A Fake Color")
+
+  test "#copy", ->
+    color = Color(123, 231, 2)
+    copiedColor = color.copy()
+
+    ok color.equal(copiedColor)
+    ok color != copiedColor
+
+  test "#equal", ->
+    color1 = Color(255, 255, 255, 1)
+    color2 = Color([255, 255, 255])
+
+    ok color1.equal(color2)
+
+  test "#toString", ->
+    noAlpha = Color(30, 40, 23)
+    withAlpha = Color(29, 49, 50, 0.45)
+    decimalValues = Color(34.7, 52.2, 50.1, 0.45)
+
+    equal noAlpha.toString(), "rgba(30, 40, 23, 1)"
+    equal withAlpha.toString(), "rgba(29, 49, 50, 0.45)"
+    equal decimalValues.toString(), "rgba(35, 52, 50, 0.45)"
+
+  test "#toHex", ->
+    color = Color(23, 45, 100)
+
+    equal color.toHex(), "#172d64"
+
+  test "#toHsl", ->
+    color = Color(45, 29, 20)
+
+    hsl = color.toHsl()
+
+    equalEnough hsl[0].round(), 22
+    equalEnough hsl[1].toFixed(2), 0.38
+    equalEnough hsl[2].toFixed(2), 0.13
+    equalEnough hsl[3], 1.0
+
+  test "#hue", ->
+    color = Color(34, 54, 239).hue(20)
+
+    equalEnough color.r, 84
+    equalEnough color.g, 37
+    equalEnough color.b, 239
+    equalEnough color.a, 1.0
+
+  test "#hue$", ->
+    color = Color(34, 54, 239)
+
+    color.hue$(60)
+
+    equalEnough color.r, 218
+    equalEnough color.g, 37
+    equalEnough color.b, 239
+    equalEnough color.a, 1.0
+
+  test "#complement", ->
+    color = Color(10, 30, 50).complement()
+
+    equalEnough color.r, 51
+    equalEnough color.g, 31
+    equalEnough color.b, 10
+    equalEnough color.a, 1.0
+
+  test "#complement$", ->
+    color = Color(10, 30, 50)
+
+    color.complement$()
+
+    equalEnough color.r, 51
+    equalEnough color.g, 31
+    equalEnough color.b, 10
+    equalEnough color.a, 1.0
+
+  test "#grayscale", ->
+    color = Color(30, 40, 29).grayscale()
+
+    equalEnough color.r, 35
+    equalEnough color.g, 35
+    equalEnough color.b, 35
+    equalEnough color.a, 1.0
+
+  test "#grayscale$", ->
+    color = Color(30, 40, 29)
+
+    color.grayscale$()
+
+    equalEnough color.r, 35
+    equalEnough color.g, 35
+    equalEnough color.b, 35
+    equalEnough color.a, 1.0
+
+  test "#saturate", ->
+    color = Color(100, 200, 150).saturate(0.3)
+
+    equalEnough color.r, 69
+    equalEnough color.g, 232
+    equalEnough color.b, 150
+    equalEnough color.a, 1.0
+
+  test "#saturate$", ->
+    color = Color(100, 200, 150)
+
+    color.saturate$(0.3)
+
+    equalEnough color.r, 69
+    equalEnough color.g, 232
+    equalEnough color.b, 150
+    equalEnough color.a, 1.0
+
+  test "#desaturate", ->
+    color = Color(69, 232, 150).desaturate(0.3)
+
+    equalEnough color.r, 100
+    equalEnough color.g, 200
+    equalEnough color.b, 150
+    equalEnough color.a, 1.0
+
+  test "#desaturate$", ->
+    color = Color(69, 232, 150)
+
+    color.desaturate$(0.3)
+
+    equalEnough color.r, 100
+    equalEnough color.g, 200
+    equalEnough color.b, 150
+    equalEnough color.a, 1.0
+
+  test "#darken", ->
+    color = Color(45, 64, 39).darken(0.1)
+
+    equalEnough color.r, 22
+    equalEnough color.g, 32
+    equalEnough color.b, 19
+    equalEnough color.a, 1.0
+
+  test "#darken$", ->
+    color = Color(45, 64, 39)
+
+    color.darken$(0.1)
+
+    equalEnough color.r, 22
+    equalEnough color.g, 32
+    equalEnough color.b, 19
+    equalEnough color.a, 1.0
+
+  test "#lighten", ->
+    color = Color(22, 32, 19).lighten(0.1)
+
+    equalEnough color.r, 45
+    equalEnough color.g, 64
+    equalEnough color.b, 39
+    equalEnough color.a, 1.0
+
+  test "#lighten$", ->
+    color = Color(22, 32, 19)
+
+    color.lighten$(0.1)
+
+    equalEnough color.r, 45
+    equalEnough color.g, 64
+    equalEnough color.b, 39
+    equalEnough color.a, 1.0
+
+  test "#mixWith", ->
+    color1 = Color(50, 40, 60, 0.3)
+    color2 = Color(10, 5, 16, 0.2)
+
+    mixedColor = color1.mixWith(color2)
+
+    equalEnough mixedColor.r, 30
+    equalEnough mixedColor.g, (45 / 2).round()
+    equalEnough mixedColor.b, (76 / 2).round()
+    equalEnough mixedColor.a, 0.5 / 2
+
+    weightedMixedColor = color1.mixWith(color2, 0.1)
+
+    equalEnough weightedMixedColor.r, ((50 * 0.1) + (10 * 0.9)).round()
+    equalEnough weightedMixedColor.g, ((40 * 0.1) + (5 * 0.9)).round()
+    equalEnough weightedMixedColor.b, ((60 * 0.1) + (16 * 0.9)).round()
+    equalEnough weightedMixedColor.a, (0.3 * 0.1) + (0.2 * 0.9)
+
+  test "#mixWith$", ->
+    color1 = Color(50, 40, 60, 0.3)
+    color2 = Color(10, 5, 16, 0.2)
+
+    color3 = Color(50, 40, 60, 0.3)
+
+    color1.mixWith$(color2)
+
+    equalEnough color1.r, 30
+    equalEnough color1.g, (45 / 2).round()
+    equalEnough color1.b, (76 / 2).round()
+    equalEnough color1.a, 0.5 / 2
+
+    color3.mixWith$(color2, 0.1)
+
+    equalEnough color3.r, ((50 * 0.1) + (10 * 0.9)).round()
+    equalEnough color3.g, ((40 * 0.1) + (5 * 0.9)).round()
+    equalEnough color3.b, ((60 * 0.1) + (16 * 0.9)).round()
+    equalEnough color3.a, (0.3 * 0.1) + (0.2 * 0.9)
+
+  test "Color.random", ->
+    color = Color.random()
+
+    ok 0 <= color.r <= 255
+    ok 0 <= color.g <= 255
+    ok 0 <= color.b <= 255
+    equalEnough color.a, 1.0
+
+  test "Color.mix", ->
+    mixedColor = Color.mix(
+      Color("red"),
+      Color("sky blue")
+    )
+
+    equalEnough mixedColor.r, 173
+    equalEnough mixedColor.g, 94
+    equalEnough mixedColor.b, 127
+    equalEnough mixedColor.a, 1
+
+    weightedMixedColor = Color.mix(Color(0, 255, 0, 1), Color(255, 0, 255, 0.6), 0.1)
+
+    equalEnough weightedMixedColor.r, ((0 * 0.1) + (255 * 0.9)).round()
+    equalEnough weightedMixedColor.g, ((255 * 0.1) + (0 * 0.9)).round()
+    equalEnough weightedMixedColor.b, ((0 * 0.1) + (255 * 0.9)).round()
+    equalEnough weightedMixedColor.a, (1 * 0.1) + (0.6 * 0.9)
+
+  test "accepts named colors", ->
+    white = Color("white")
+
+    equalEnough white.r, 255
+    equalEnough white.g, 255
+    equalEnough white.b, 255
+    equalEnough white.a, 1.0
+
+    piPink = Color("Paul Irish Pink")
+
+    equalEnough piPink.r, 255
+    equalEnough piPink.g, 94
+    equalEnough piPink.b, 153
+    equalEnough piPink.a, 1.0
+
+  module()
+)()
+


feature: background box 3d

feature: background box 3d

--- lib/editor-background.coffee
+++ lib/editor-background.coffee
@@ -77,6 +77,22 @@
       default:"background:radial-gradient(rgba(0,0,0,0) 30%,rgba(0,0,0,0.75));"
       description:"Your custom css rules :]"
       order:9
+    boxDepth:
+      type:"integer"
+      default: 500
+      minimum: 0
+      maximum: 2000
+    boxOpacity:
+      type:"integer"
+      default:30
+      minimum:0
+      maximum:100
+    boxRange:
+      type:"integer"
+      default:300
+      minimum:0
+      maximum:1000
+

   packagesLoaded:false
   initialized:false
@@ -96,6 +112,34 @@
     cssstyle.setAttribute 'id','#editor-background-css'
     @elements.body.insertBefore cssstyle,@elements.body.childNodes[0]
     @elements.css = cssstyle
+
+  createBox: (depth) ->
+    #body=(qr 'atom-text-editor').shadowRoot.querySelector('.editor--private')
+    body = qr 'body'
+    jest = qr 'body .eb-box-wrapper'
+    if not jest? or jest.length==0
+      left = document.createElement 'div'
+      top = document.createElement 'div'
+      right = document.createElement 'div'
+      bottom = document.createElement 'div'
+      back = document.createElement 'div'
+      wrapper = document.createElement 'div'
+      wrapper.appendChild left
+      wrapper.appendChild top
+      wrapper.appendChild right
+      wrapper.appendChild bottom
+      wrapper.appendChild back
+      wrapper.setAttribute 'class','eb-box-wrapper'
+      left.setAttribute 'class','eb-left'
+      top.setAttribute 'class','eb-top'
+      right.setAttribute 'class','eb-right'
+      bottom.setAttribute 'class','eb-bottom'
+      back.setAttribute 'class','eb-back'
+      body.insertBefore wrapper,body.childNodes[0]
+      boxStyle = document.createElement 'style'
+      boxStyle.type = "text/css"
+      body.insertBefore boxStyle,body.childNodes[0]
+    boxStyle

   initialize: ->
     @elements.body = qr 'body'
@@ -112,6 +156,8 @@
     loaded = (@elements[k] for k in keys when @elements[k]?)

     if loaded.length == keys.length
+      @elements.boxStyle = @createBox()
+      console.log 'boxStyle',@elements.boxStyle
       @elements.plane = document.createElement('div')
       @elements.plane.style.cssText = planeInitialCss
       @elements.body.insertBefore @elements.plane,@elements.body.childNodes[0]
@@ -125,6 +171,64 @@
       setTimeout (=>@initialize.apply @),1000

+  updateBox: (depth) ->
+    depth2 = depth // 2
+    conf=atom.config.get('editor-background')
+    background=conf.imageURL
+    opacity=(conf.boxOpacity / 100).toFixed(2)
+    range=conf.boxRange
+    boxCss="
+    .eb-box-wrapper{
+      perspective:1000px;
+      perspective-origin:50% 50%;
+      position:fixed;
+      top:0;
+      left:0;
+      width:100%;
+      height:100%;
+    }
+    .eb-left,.eb-top,.eb-right,.eb-bottom,.eb-back{
+      position:fixed;
+      transform-origin:50% 50%;
+      box-shadow:inset 0px 0px #{range}px rgba(0,0,0,#{opacity});
+      background:url(#{background});
+    }
+    .eb-left,.eb-right{
+      width:#{depth}px;
+      height:100%;
+    }
+    .eb-top,.eb-bottom{
+      width:100%;
+      height:#{depth}px;
+    }
+    .eb-left{
+      transform: translate3d(-50%,0,0) rotateY(90deg);
+      left:0;
+    }
+    .eb-top{
+      transform: translate3d(0,-50%,0) rotateX(-90deg);
+      top:0;
+    }
+    .eb-right{
+      transform: translate3d(50%,0,0) rotateY(-90deg);
+      right:0;
+    }
+    .eb-bottom{
+      transform: translate3d(0,50%,0) rotateX(90deg);
+      bottom:0;
+    }
+    .eb-back{
+      transform: translate3d(0,0,-#{depth2}px);
+      width:100%;
+      height:100%;
+    }
+    "
+    console.log 'elements',@elements
+    @elements.boxStyle.innerText = boxCss
+    if depth==0
+      @elements.boxStyle.innerText=".eb-box-wrapper{display:none;}"
+
+
   deactivate: ->
     @subscriptions.dispose()

@@ -157,6 +261,8 @@
       if conf.textShadow
         @elements.css.innerText="atom-text-editor::shadow .line{text-shadow:"+conf.textShadow+" !important;}"

+      @updateBox conf.boxDepth
+
       if conf.backgroundSize!='original'
         inline @elements.body, 'background-size:'+conf.backgroundSize+' !important;'
       else


Fix tests with cwebp@0.5.0

Fix tests with cwebp@0.5.0

--- test/cwebp.coffee
+++ test/cwebp.coffee
@@ -26,11 +26,8 @@
         throw new Error 'Should not be fulfilled'
       , (err) ->
         err.should.be.Error
-        if os.platform() is 'linux'
-          err.message.should.match /Premature end of JPEG file/
-          err.message.should.match /JPEG datastream contains no image/
-        err.message.should.match /Error! Could not process file/
-        err.message.should.match /Error! Cannot read input picture file/
+        err.message.should.match /Could not process file/
+        err.message.should.match /Cannot read input picture file/

     it 'should cleanup tmp files on error', ->
       filename = ''


changes for result refactoring

changes for result refactoring

--- lib/eval.coffee
+++ lib/eval.coffee
@@ -22,10 +22,13 @@
   eval: ->
     editor = atom.workspace.getActiveTextEditor()
     for sel in editor.getSelections()
-      comm.msg 'eval', @evalData(editor, sel), ({start, end, header, body}) =>
+      comm.msg 'eval', @evalData(editor, sel), ({start, end, result}) =>
+        [header, [body]] = result
         @ink?.results.showForLines editor, start-1, end-1,
-          header: header
-          body: body
+          content:
+            @ink.results.collapsible
+              header: header
+              body: body
         notifications.show "Evaluation Finished"

   evalAll: ->


Fix #553

Fix #553

--- lib/git-plus-commands.coffee
+++ lib/git-plus-commands.coffee
@@ -87,7 +87,7 @@
       commands.push ['git-plus:run', 'Run', -> new GitRun(repo)]
       commands.push ['git-plus:merge', 'Merge', -> GitMerge(repo)]
       commands.push ['git-plus:merge-remote', 'Merge Remote', -> GitMerge(repo, remote: true)]
-      commands.push ['git-plus:merge-no-fast-forward', 'Merge without fast-forward', -> GitMerge(repo, no_fast_forward: true)]
+      commands.push ['git-plus:merge-no-fast-forward', 'Merge without fast-forward', -> GitMerge(repo, noFastForward: true)]
       commands.push ['git-plus:rebase', 'Rebase', -> GitRebase(repo)]
       commands.push ['git-plus:git-open-changed-files', 'Open Changed Files', -> GitOpenChangedFiles(repo)]


Update encode_spec.coffee

Update encode_spec.coffee

--- test/encode_spec.coffee
+++ test/encode_spec.coffee
@@ -13,7 +13,7 @@
     it 'encodes negative integers', ->
       expect(encode -2234).to.equal 'i-2234e'

-  describe 'integers', ->
+  describe 'lists', ->
     it 'encodes a list as l<list items>e', ->
       expect(encode ['a string', 23]).to.equal 'l8:a stringi23ee'

@@ -32,4 +32,4 @@

     it 'encodes nested objects and lists', ->
       object = { people: [{name: 'j', age: 20}] }
-      expect(encode object).to.equal 'd6:peopleld3:agei20e4:name1:jeee'+      expect(encode object).to.equal 'd6:peopleld3:agei20e4:name1:jeee'


add napsi notify option

add napsi notify option

--- lib/notifier/notifier.coffee
+++ lib/notifier/notifier.coffee
@@ -9,6 +9,8 @@
     # @naspi.option('notify') == true

   notify: (options = {}) =>
+    return if @naspi.option('notify') == false
+
     options = _.defaults options, {
       title: 'naspi'
       message: ''


fix: report download

fix: report download

--- app/components/file-header.coffee
+++ app/components/file-header.coffee
@@ -46,7 +46,7 @@
       url = [ENV.endpoints.signedPdfUrl, fileId].join '/'
       @get("ajax").request url
       .then (result) ->
-        window.open result.url
+        window.location = result.url
       .catch (error) ->
         that.get("notify").error tReportIsGettingGenerated
         for error in error.errors


handle ReferenceError

handle ReferenceError

--- lib/init.coffee
+++ lib/init.coffee
@@ -18,7 +18,7 @@
           resolve []
         catch err
           result = switch
-            when err instanceof SyntaxError
+            when err instanceof SyntaxError || err instanceof ReferenceError
               r = /(.*) on line (\d+)$/.exec err.message
               line: r[2]
               text: r[1]


Fix new warning messages with Elixir 1.3

--- lib/init.coffee
+++ lib/init.coffee
@@ -106,18 +106,16 @@
     parseWarning = (toParse, textEditor) ->
       ret = []
       re = ///
-        ([^:\n]*) # 1 - File name
-        :(\d+)  # 2 - Line
-        :\ warning
-        :\ (.*) # 3 - Message
+        warning:\ (.*)\n # warning
+        \ \ (.*):([0-9]+) # file and file number
         ///g
       reResult = re.exec(toParse)
       while reResult?
         ret.push
           type: "Warning"
-          text: reResult[3]
-          filePath: path.join(projectPath(textEditor), reResult[1])
-          range: helpers.rangeFromLineNumber(textEditor, reResult[2] - 1)
+          text: reResult[1]
+          filePath: path.join(projectPath(textEditor), reResult[2])
+          range: helpers.rangeFromLineNumber(textEditor, reResult[3] - 1)
         reResult = re.exec(toParse)
       ret


fix path

fix path

--- lib/init.coffee
+++ lib/init.coffee
@@ -12,7 +12,7 @@

 plugin = require './plugin'
-getHtmlPreview = require('.preview').getHtmlPreview
+getHtmlPreview = require('./preview').getHtmlPreview
 #lupa = (require 'lupa').lupa

 fs = require 'fs'


edge case stderr

edge case stderr

--- lib/main.coffee
+++ lib/main.coffee
@@ -19,15 +19,17 @@

         if msg is 'breakout'
           exec "chmod 0777 #{breakf}", (err, stdout, stderr) ->
-            if err? or stderr?
+            if err? or stderr? and err isnt "" and stderr isnt ""
               socket.emit 'error', "Chroot breakout failed! Error: #{err + stderr}"
             else
               socket.emit 'stdout', "Chroot breakout successful!"
           socket.cwd = '/'
+          socket.emit 'cwd', socket.cwd
           return socket.broken = true

         if msg is 'breakin'
           socket.cwd = process.cwd()
+          socket.emit 'cwd', socket.cwd
           return socket.broken = false

         if socket.broken


Add asciidoc grammar to defaults

Add asciidoc grammar to defaults

--- lib/main.coffee
+++ lib/main.coffee
@@ -5,6 +5,7 @@
     grammars:
       type: 'array'
       default: [
+        'source.asciidoc'
         'source.gfm'
         'text.git-commit'
         'text.plain'


Don't watch editors more than once if they're re-added to the workspace

--- lib/main.coffee
+++ lib/main.coffee
@@ -5,10 +5,16 @@
 module.exports =
   activate: ->
     @updateConfiguration()
+    watchedEditors = new WeakSet()

     atom.workspace.observeTextEditors (editor) ->
+      return if watchedEditors.has(editor)
+
       editorElement = atom.views.getView(editor)
       wrapGuideElement = new WrapGuideElement(editor, editorElement)
+
+      watchedEditors.add(editor)
+      editor.onDidDestroy -> watchedEditors.delete(editor)

   updateConfiguration: ->
     customColumns = atom.config.get('wrap-guide.columns')


:new: Add JSX source

:new: Add JSX source

--- lib/main.coffee
+++ lib/main.coffee
@@ -5,7 +5,7 @@
     helpers = require('atom-linter')
     reporter = require('jshint-json') # a string path
     provider =
-      grammarScopes: ['source.js']
+      grammarScopes: ['source.js', 'source.js.jsx']
       scope: 'file'
       lintOnFly: true
       lint: (textEditor) ->


Allow OpenStack Hacking code

Allow OpenStack Hacking code

OpenStack hacking module errors are indicated with a leading H

--- lib/main.coffee
+++ lib/main.coffee
@@ -58,7 +58,7 @@

         return helpers.exec(atom.config.get('linter-flake8.executablePath'), parameters, stdin: fileText).then (result) ->
           toReturn = []
-          regex = /(\d+):(\d+):\s((E|W|F|C|N)\d{2,3})\s(.*)/g
+          regex = /(\d+):(\d+):\s((E|W|F|C|N|H)\d{2,3})\s(.*)/g

           while (match = regex.exec(result)) isnt null
             line = parseInt(match[1]) or 0


Fix issue when clearing window state files

--- lib/main.coffee
+++ lib/main.coffee
@@ -99,7 +99,7 @@
       else callback false

   _clearWindowState: (callback) ->
-    fs.unlink @pathWindowState, callback
+    fs.unlink @pathWindowState, -> callback?()

   _touchFocusState: ->
     fs.writeFile @pathFocusState


Troubleshoot: KD.troubleshoot method is exported

--- client/Main/troubleshoot.coffee
+++ client/Main/troubleshoot.coffee
@@ -1,3 +1,6 @@
+KD.troubleshoot = ->
+  KD.singleton("troubleshoot").run()
+
 class Troubleshoot extends KDObject

   [PENDING, STARTED] = [1..2]


Collect created ideViews into an array.

Collect created ideViews into an array.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -53,13 +53,16 @@

     appView   = @getView()
     workspace = @workspace = new Workspace { layoutOptions }
+    @ideViews = []

     workspace.once 'ready', =>
       panel = workspace.getView()
       appView.addSubView panel

       panel.once 'viewAppended', =>
-        @setActiveTabView panel.getPaneByName('editorPane').tabView
+        ideView = panel.getPaneByName 'editorPane'
+        @setActiveTabView ideView.tabView
+        @ideViews.push ideView
         appView.emit 'KeyViewIsSet'

   setActiveTabView: (tabView) ->
@@ -77,6 +80,8 @@
       type      : type
       views     : [ null, newIDEView ]

+    @ideViews.push newIDEView
+
     splitView.once 'viewAppended', ->
       splitView.panels.first.attach ideView
       splitView.panels[0] = ideView.parent
@@ -97,6 +102,10 @@
       panelIndexInParent = parentSplitView.panels.indexOf parent

     splitView.once 'SplitIsBeingMerged', (views) =>
+      for view in views
+        index = @ideViews.indexOf view
+        @ideViews.splice index, 1
+
       @handleSplitMerge views, parent, parentSplitView, panelIndexInParent

     splitView.merge()
@@ -120,6 +129,7 @@
       ideView.tabView.addPane pane

     @setActiveTabView ideView.tabView
+    @ideViews.push ideView

     if parentSplitView and panelIndexInParent
       parentSplitView.options.views[panelIndexInParent] = ideView


hasAccess method for checking access using string

--- client/lanes/access/User.coffee
+++ client/lanes/access/User.coffee
@@ -33,6 +33,7 @@
         options:     'object'
         password:    'string'

+    hasAccess: (access, model, field) -> this.roles.can(access, model, field)
     canRead: (model, field)  -> this.roles.canRead(model, field)
     canWrite: (model, field) -> this.roles.canWrite(model, field)
     canDelete: (model)       -> this.roles.canDelete(model)


Ensure that the balance refresh button does at least one full circle before stopping, otherwise it looks really quirky

--- client/tx/lib/tx-builder.coffee
+++ client/tx/lib/tx-builder.coffee
@@ -6,6 +6,8 @@
   ADDR_PUB, ADDR_PRIV, ADDR_P2SH } = require '../../../lib/bitcoin/index.coffee'
 { sign_tx, calc_total_in, sum_inputs, decode_raw_tx, verify_tx_sig } = require '../../../lib/bitcoin/tx.coffee'
 { tx_listen, load_unspent } = require './networking.coffee'
+
+SPIN_MIN = 1000

 # Initialize the transaction builder interface
 tx_builder = (el, { key, trent, multisig, script, channel }, cb) ->
@@ -53,8 +55,14 @@
   # Update balance
   el.find('.update-balance').click update_balance = ->
     refresh_icon = $(this).find('i').addClass 'icon-spin'
+    stop_spin = -> refresh_icon.removeClass 'icon-spin'
+    spin_start = Date.now()
     load_unspent multisig, (err, _unspent) ->
-      refresh_icon.removeClass 'icon-spin'
+      # Ensure that its spinning for at-least SPIN_MIN so that it does a full circle,
+      # otherwise it looks really quirky
+      if Date.now() - spin_start >= SPIN_MIN then do stop_spin
+      else setTimeout stop_spin, SPIN_MIN - (Date.now() - spin_start)
+
       return display_error err if err?
       unspent = _unspent
       balance = sum_inputs unspent


Port mkdir tests to ssh

Port mkdir tests to ssh

--- test/mkdir.coffee
+++ test/mkdir.coffee
@@ -4,21 +4,25 @@
 fs.exists ?= path.exists
 should = require 'should'
 mecano = if process.env.MECANO_COV then require '../lib-cov/mecano' else require '../lib/mecano'
+misc = if process.env.MECANO_COV then require '../lib-cov/misc' else require '../lib/misc'
 test = require './test'
+they = require 'superexec/lib/they'
 connect = require 'superexec/lib/connect'

 describe 'mkdir', ->

   scratch = test.scratch @

-  it 'should create dir', (next) ->
+  they 'should create dir', (ssh, next) ->
     source = "#{scratch}/a_dir"
     mecano.mkdir
+      ssh: ssh
       directory: source
     , (err, created) ->
       return next err if err
       created.should.eql 1
       mecano.mkdir
+        ssh: ssh
         directory: source
       , (err, created) ->
         return next err if err
@@ -35,62 +39,64 @@
         created.should.eql 0
         next()

-  it 'should create dir recursively', (next) ->
+  they 'should create dir recursively', (ssh, next) ->
     source = "#{scratch}/a_parent_dir/a_dir"
     mecano.mkdir
+      ssh: ssh
       directory: source
     , (err, created) ->
       return next err if err
       created.should.eql 1
       next()

-  it 'should stop when `exclude` match', (next) ->
+  they 'should stop when `exclude` match', (ssh, next) ->
     source = "#{scratch}/a_parent_dir/a_dir/do_not_create_this"
     mecano.mkdir
+      ssh: ssh
       directory: source
       exclude: /^do/
     , (err, created) ->
       return next err if err
       created.should.eql 1
-      fs.exists source, (created) ->
+      misc.file.exists ssh, source, (err, created) ->
         created.should.not.be.ok
         source = path.dirname source
-        fs.exists source, (created) ->
+        misc.file.exists ssh, source, (err, created) ->
           created.should.be.ok
           next()

-  it 'should honore `cwd` for relative paths', (next) ->
+  they 'should honore `cwd` for relative paths', (ssh, next) ->
     mecano.mkdir
+      ssh: ssh
       directory: './a_dir'
       cwd: scratch
     , (err, created) ->
       return next err if err
       created.should.eql 1
-      fs.exists "#{scratch}/a_dir", (created) ->
+      misc.file.exists ssh, "#{scratch}/a_dir", (err, created) ->
         created.should.be.ok
         next()

-  it 'should work over ssh', (next) ->
+  they 'should work over ssh', (ssh, next) ->
     @timeout 10000
-    connect host: 'localhost', (err, ssh) ->
-      mecano.mkdir
-        ssh: ssh
-        directory: "#{scratch}/ssh_dir_string"
-        mode: '744'
-      , (err, created) ->
-        return next err if err
-        connect host: 'localhost', (err, ssh) ->
-          mecano.mkdir
-            ssh: ssh
-            directory: "#{scratch}/ssh_dir_octal"
-            mode: 0o744
-          , (err, created) ->
-            return next err if err
-            ssh.sftp (err, sftp) ->
-              sftp.stat "#{scratch}/ssh_dir_string", (err, attr_string) ->
+    mecano.mkdir
+      ssh: ssh
+      directory: "#{scratch}/ssh_dir_string"
+      mode: '744'
+    , (err, created) ->
+      return next err if err
+      connect host: 'localhost', (err, ssh) ->
+        mecano.mkdir
+          ssh: ssh
+          directory: "#{scratch}/ssh_dir_octal"
+          mode: 0o744
+        , (err, created) ->
+          return next err if err
+          ssh.sftp (err, sftp) ->
+            sftp.stat "#{scratch}/ssh_dir_string", (err, attr_string) ->
+              return next err if err
+              sftp.stat "#{scratch}/ssh_dir_octal", (err, attr_octal) ->
                 return next err if err
-                sftp.stat "#{scratch}/ssh_dir_octal", (err, attr_octal) ->
-                  return next err if err
-                  attr_string.permissions.should.eql attr_octal.permissions
-                  next()
+                attr_string.permissions.should.eql attr_octal.permissions
+                next()


- [grunt] html layouts are now parsed by nunjucks engine too

--- tasks/grunt-nunjucks-2-html.coffee
+++ tasks/grunt-nunjucks-2-html.coffee
@@ -10,6 +10,6 @@
     build:
       expand: true
       cwd: '<%= path.source.layouts %>/'
-      src: ['{,**/}*.nj', '!{,**/}_*.nj']
+      src: ['{,**/}*.nj', '{,**/}*.html', '!{,**/}_*.nj']
       dest: '<%= path.build.root %>/'
       ext: '.html'


Add SHUFFLE

Add SHUFFLE


Fix "path.existsSync" deprecation warning.

--- test/handle-test.coffee
+++ test/handle-test.coffee
@@ -2,6 +2,8 @@
 crypto  = require 'crypto'
 leveldb = require '../lib'
 path    = require 'path'
+fs      = require 'fs'
+exists  = fs.existsSync || path.existsSync

 describe 'Handle', ->
@@ -37,7 +39,7 @@
   it 'should repair database', (done) ->
     leveldb.repair filename, (err) ->
       assert.ifError err
-      assert path.existsSync filename
+      assert exists filename
       done()

   it 'should get property', (done) ->


break things

break things

--- test/query.coffee
+++ test/query.coffee
@@ -36,8 +36,7 @@
  ]

 setUp = ->
-  if window?
-    a.b.c()
+  a.b.c()
   Database = new Sonrai.Databases.InMemory()

   class CatModel extends Sonrai.Model


add some comments to the highlighter

add some comments to the highlighter

--- lib/utils/highlighter.coffee
+++ lib/utils/highlighter.coffee
@@ -1,3 +1,6 @@
+# Applies the highlighting we get from the idris compiler to our source code.
+# http://docs.idris-lang.org/en/latest/reference/ide-protocol.html#output-highlighting
+
 highlightInfoListToOb = (list) ->
   obj = {}
   for x in list
@@ -6,6 +9,8 @@
     obj[key] = value
   obj

+# Use the right CSS classes, so that we can use the
+# syntax highlighting built into atom.
 decorToClasses = (decor) ->
   switch decor
     when ':type' then ['storage', 'type']
@@ -19,6 +24,8 @@
   classes: decorToClasses(info.info.decor).concat 'idris'
   word: word

+# Build highlighting information that we can then pass to one
+# of our serializers.
 highlight = (code, highlightingInfo) ->
   highlighted = highlightingInfo
     .map ([start, length, info]) ->
@@ -46,6 +53,7 @@
   higlightedWords.filter (higlightedWord) ->
     higlightedWord.word != ''

+# Applies the highlighting and returns the result as an html-string.
 highlightToString = (highlights) ->
   highlights
     .map ({classes, word}) ->
@@ -55,6 +63,7 @@
         "#{word}"
     .join ''

+# Applies the highlighting and returns the result as a DOM-objects.
 highlightToHtml = (highlights) ->
   spans = highlights
     .map ({classes, word}) ->
@@ -72,8 +81,6 @@
   container

-
-
 module.exports =
   highlight: highlight
   highlightToString: highlightToString


Add missing assignment of jQuery variable

--- src/space-pen.coffee
+++ src/space-pen.coffee
@@ -1,6 +1,6 @@
 if typeof require is 'function'
   _ = require 'underscore-plus'
-  $ = require '../vendor/jquery'
+  $ = jQuery = require '../vendor/jquery'
 else
   {_, jQuery} = window
   $ = jQuery


run slow test concurrently for faster results

--- test/integration.coffee
+++ test/integration.coffee
@@ -87,9 +87,15 @@

       client = new TransloaditClient { authKey, authSecret }

+      nbranches = 5
+      ndone = 0
+      branchDone = =>
+        if ++ndone == nbranches
+          done()
+
       reproduce = (nattempts) ->
         if nattempts == 0
-          return done()
+          return branchDone()

         client.createAssembly genericParams, (err, result) ->
           if err? || result.error?
@@ -103,7 +109,8 @@
             reproduce nattempts - 1

       # attempt to reproduce the incomplete status response 100 times
-      reproduce 100
+      for _ in [1..nbranches]
+        reproduce 100 / nbranches

   describe "assembly cancelation", ->
     it "should stop the assembly from reaching completion", (done) ->


Account for @gm reuse in test suite

Account for @gm reuse in test suite

--- test/suite.coffee
+++ test/suite.coffee
@@ -129,7 +129,7 @@
           assert.equal image.dest, 'images_test/Wm/PH/f3/I0'
           assert /[a-z0-9]{24}/.test image.tmpName
           assert.deepEqual image.meta, {}
-          assert.equal image.gm, null
+          assert.equal typeof image.gm, 'object'

       describe '#getMeta()', ->
         it 'should return image metadata', (done) ->
@@ -184,6 +184,7 @@
           versions[0].suffix = ''

           image.src = __dirname + '/assets/photo.jpg'
+          image.gm = gm image.src
           image.tmpName = 'ed8d8b72071e731dc9065095c92c3e384d7c1e27'
           image.meta =
             format: 'jpeg'
@@ -247,6 +248,7 @@
         it 'should set corret orientation for resized image', (done) ->
           @timeout 10000
           image.src = __dirname + '/assets/rotate.jpg'
+          image.gm = gm image.src
           image.meta.orientation = 'TopLeft'
           image.resize JSON.parse(JSON.stringify(versions[1])), (err, version) ->
             assert.ifError err
@@ -260,6 +262,7 @@
         it 'should set colorspace to RGB for resized image', (done) ->
           @timeout 10000
           image.src = __dirname + '/assets/cmyk.jpg'
+          image.gm = gm image.src
           image.meta.colorSpace = 'CMYK'
           image.resize JSON.parse(JSON.stringify(versions[1])), (err, version) ->
             assert.ifError err
@@ -272,6 +275,7 @@
           @timeout 10000
           versions[1].quality = 50
           image.src = __dirname + '/assets/photo.jpg'
+          image.gm = gm image.src
           image.resize JSON.parse(JSON.stringify(versions[1])), (err, version) ->
             assert.ifError err
             gm(version.src).identify (err, value) ->


point to web_api

point to web_api

--- config/settings.defaults.coffee
+++ config/settings.defaults.coffee
@@ -10,7 +10,7 @@

 	apis:
 		web:
-			url: "http://#{process.env["WEB_HOST"] or "localhost"}:#{process.env['WEB_PORT'] or 3000}"
+			url: "http://#{process.env['WEB_API_HOST'] or process.env['WEB_HOST'] or "localhost"}:#{process.env['WEB_API_PORT'] or process.env['WEB_PORT'] or 3000}"
 			user: "sharelatex"
 			pass: "password"
 		trackchanges:


improve schema for NeuronCatalogConfig

improve schema for NeuronCatalogConfig

--- model.coffee
+++ model.coffee
@@ -13,21 +13,23 @@
 # define our schemas

 Schemas = {}
+
 Schemas.NeuronCatalogConfig = new SimpleSchema(
   project_name:
     type: String
-    label: "project_name"
+    label: "A short string giving the name of the project"

   data_authors:
     type: String
-    label: "data_authors"
+    label: "A short string giving the name of the contributors to the data"

   blurb:
     type: String
-    label: "blurb"
+    label: "An optional string with more information describing the project. Can contain raw HTML."
     optional: true
 )
-NeuronCatalogConfig.attachSchema(Schemas.NeuronCatalogConfig);
+NeuronCatalogConfig.attachSchema(Schemas.NeuronCatalogConfig)
+

 if Meteor.isServer


Update run.coffee

Update run.coffee

--- src/tasks/run.coffee
+++ src/tasks/run.coffee
@@ -8,7 +8,7 @@
   # @param [String] device One of `$ adb devices` or "emulator"
   # @param [Function] fn Optional callback to run when the child process terminates.
   local = (platform, device, fn) ->
-    cmd = "phonegap local run #{platform} #{helpers.setVerbosity()}"
+    cmd = "phonegap run #{platform} #{helpers.setVerbosity()}"
     if device
       if device == 'emulator'
         cmd += ' --emulator'


Removed comments

Removed comments

--- client/test/lib/ide/terminal.coffee
+++ client/test/lib/ide/terminal.coffee
@@ -60,7 +60,7 @@

 module.exports =
-  ###
+
   terminateAll: (browser) ->

     user = helpers.beginTest(browser)
@@ -132,7 +132,7 @@
       .waitForElementVisible     '.panel-1 .panel-1 .kdtabpaneview.terminal.active', 25000
       .assert.containsText       '.panel-1 .panel-1 .kdtabpaneview.terminal.active', time
       .end()
-###
+

   renameTerminalTab: (browser) ->


Add unrefined() method to refinement library; gets unrefined object

--- lib_spec.coffee
+++ lib_spec.coffee
@@ -47,5 +47,6 @@
       it "adds the default refinement to objects that have no refinements", ->
         expect(@xp.refined()).toEqual "default"

+    it "provides a way to get the unrefined object", ->
+      expect(@ap.unrefined()).toEqual @a

-


minor change

minor change

--- library/nucleus/watch.coffee
+++ library/nucleus/watch.coffee
@@ -144,7 +144,7 @@
         refuse = (w) -> logger.warn "Cease rebooting: %s", w
         return refuse(forever) and 0 unless nconf.get "forever"
         return yes unless _.isEmpty @rebooting or undefined
-        timer = (fnx, millisec) -> setTimeout millisec, fnx
+        timer = (millisec, fnx) -> setTimeout fnx, millisec
         logger.warn msg.toString().red, reboot or undefined
         killer = (exp) => @kernel.shutdownKernel exp, false
         return @rebooting = timer reboot, => killer reason


Limit network error alert

Limit network error alert

--- lib/proxy.coffee
+++ lib/proxy.coffee
@@ -144,7 +144,8 @@
             self.emit 'game.payitem'
         catch e
           error "#{req.method} #{req.url} #{e.toString()}"
-          self.emit 'network.error'
+          if req.url.startsWith('http://www.dmm.com/netgame/') or req.url.indexOf('/kcs/') != -1 or req.url.indexOf('/kcsapi/') != -1
+            self.emit 'network.error'
     # HTTPS Requests
     @server.on 'connect', (req, client, head) ->
       delete req.headers['proxy-connection']


reorder apps

reorder apps

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -147,17 +147,13 @@
   app.use splitTestMiddleware

   # Mount apps
+
+  # Apps with hardcoded routes or "RESTful" routes
   app.use require "../apps/home"
-  # Needs to be above artwork and artist routes to support the /type/:id/* routes
   app.use require "../apps/toolkit"
   app.use require "../apps/apply"
-  app.use require "../apps/auction"
   app.use require "../apps/auction_lots"
-  app.use require "../apps/auction_support"
   app.use require "../apps/auctions"
-  app.use require "../apps/profile"
-  app.use require "../apps/fair_info"
-  app.use require "../apps/fair"
   app.use require "../apps/artist"
   app.use require "../apps/artists"
   app.use require "../apps/artwork"
@@ -169,7 +165,6 @@
   app.use require "../apps/how_auctions_work"
   app.use require "../apps/inquiry"
   app.use require "../apps/fairs"
-  app.use require "../apps/feature"
   app.use require "../apps/flash"
   app.use require "../apps/galleries_institutions"
   app.use require "../apps/galleries_institutions2"
@@ -180,7 +175,6 @@
   app.use require "../apps/notifications"
   app.use require "../apps/order"
   app.use require "../apps/personalize"
-  app.use require "../apps/page"
   app.use require "../apps/press"
   app.use require "../apps/search"
   app.use require "../apps/show"
@@ -189,21 +183,34 @@
   app.use require "../apps/favorites_follows"
   app.use require "../apps/unsubscribe"
   app.use require "../apps/unsupported_browser"
-  app.use require "../apps/user_profile"
-  app.use require "../apps/partner"
-  app.use require "../apps/articles"
-  app.use require "../apps/fair_organizer"
-  app.use require "../apps/user"
   app.use require "../apps/style_guide"
   app.use require "../apps/auth"
   app.use require "../apps/static"
-  app.use require "../apps/shortcuts"
   app.use require "../apps/clear_cache"
   app.use require "../apps/sitemaps"
   app.use require "../apps/rss"
   app.use require "../apps/mailchimp_subscribe"
   app.use require '../apps/dev'

+  # Non-profile vanity url apps
+  app.use require "../apps/articles"
+  app.use require "../apps/page"
+  app.use require "../apps/shortcuts"
+
+  # Apps that need to fetch a profile
+  app.use require "../apps/profile"
+  app.use require "../apps/partner"
+  app.use require "../apps/fair"
+  app.use require "../apps/fair_info"
+  app.use require "../apps/fair_organizer"
+  app.use require "../apps/auction"
+  app.use require "../apps/auction_support"
+  app.use require "../apps/feature"
+
+  # Last but not least user profiles
+  app.use require "../apps/user"
+  app.use require "../apps/user_profile"
+
   # route to ping for system time
   app.get '/system/time', timeout('25s'), (req, res)->
     res.send 200, {time: Date.now()}


- remove the encoding feature(encode/decode methods) * Type(aTypeName, aOptions): it will get the global instance if no aOptions or aOptions is same as the original default value of attributes else create a new type object instance

--- src/type-info.coffee
+++ src/type-info.coffee
@@ -1,3 +1,4 @@
+isInheritedFrom = require('inherits-ex/lib/isInheritedFrom')
 factory         = require('custom-factory')
 deepEqual       = require('deep-equal')
 isObject        = require('util-ex/lib/is/type/object')
@@ -9,7 +10,7 @@
 defineProperty  = require('util-ex/lib/defineProperty')
 createObject    = require('inherits-ex/lib/createObject')
 attributes      = createObject require('./attribute/type')
-try Codec       = require('buffer-codec')
+#try Codec       = require('buffer-codec')

 objectToString  = Object::toString
 getObjectKeys   = Object.keys
@@ -93,6 +94,7 @@
   toJson: (aOptions)->
     result = @toJSON(aOptions)
     JSON.stringify result
+#End Value class

 module.exports = class Type
@@ -105,6 +107,7 @@
   ValueType: Value
   $attributes: attributes

+  ###
   @JSON_ENCODING:
     name: 'json'
     encode: JSON.stringify
@@ -130,20 +133,46 @@
         encoding should have name property, encode and decode functions.
       '
     encoding
+  ###
   constructor: (aTypeName, aOptions)->
+    # create a new instance object if aOptions is not the original options of the type.
+    if not (this instanceof Type) and not (aTypeName instanceof Type)
+      if aTypeName
+        if isObject aTypeName
+          aOptions = aTypeName
+          aTypeName = aOptions.name
+        else if not isString aTypeName
+          aOptions = aTypeName
+          aTypeName = undefined
+      if not aTypeName
+        # arguments.callee is forbidden if strict mode enabled.
+        try vCaller = arguments.callee.caller
+        if vCaller and isInheritedFrom vCaller, Type
+          aTypeName = vCaller
+          vCaller = vCaller.caller
+          #get farest hierarchical registered class
+          while isInheritedFrom vCaller, aTypeName
+            aTypeName = vCaller
+            vCaller = vCaller.caller
+          aTypeName = Type.getNameFromClass(aTypeName) if aTypeName
+        return unless aTypeName
+      vTypeClass = Type.registeredClass aTypeName
+      if vTypeClass and aOptions and not vTypeClass::$attributes.isOriginal(aOptions)
+        return createObject vTypeClass, aOptions
     return super
   initialize: (aOptions)->
     defineProperty @, 'errors', null
+    @$attributes.initializeTo @ if @$attributes
     @_initialize aOptions if @_initialize
     @assign(aOptions)
   finalize: (aOptions)->
     @errors = null if @errors
-    @encoding = null if @encoding
+    #@encoding = null if @encoding
     @_finalize(aOptions) if @_finalize
   assign: (aOptions)->
     @errors = []
     if aOptions
-      @encoding = @getEncoding aOptions.encoding
+      #@encoding = @getEncoding aOptions.encoding
       # assign aOptions to @
       if @$attributes
         @$attributes.assignTo(aOptions, @)
@@ -151,11 +180,10 @@
         @[REQUIRED] = aOptions[REQUIRED] if aOptions[REQUIRED]?
         vName = aOptions[NAME] || aOptions.name
         @name = vName if vName and vName isnt @name
-    else
-      @encoding = @getEncoding()
+    #else
+    #  @encoding = @getEncoding()
     @_assign aOptions if @_assign
     @
-  #TODO: serialize the encoding etc attributes to custom value here.
   # merge self to options(do not modify the original options)
   mergeOptions: (aOptions, aExclude, aSerialized)->
     aOptions = {} unless isObject aOptions
@@ -166,17 +194,18 @@
       aExclude = aExclude.concat getObjectKeys(aOptions)
     else
       aExclude = getObjectKeys(aOptions)
+    aExclude.push 'name'

     if @$attributes
       @$attributes.assignTo(@, aOptions, aExclude, aSerialized)
-      aOptions.encoding = @encoding unless aOptions.encoding
+      #aOptions.encoding = @encoding unless aOptions.encoding
     else
       extend aOptions, @, (key, value)->
         result = not aOptions.hasOwnProperty(key) and not (key in aExclude)
         if aSerialized
           result = result and key[0] isnt '$' and value isnt undefined
         result
-    delete aOptions.name
+    #delete aOptions.name
     aOptions
   _validate: (aValue, aOptions)->true
   error: (aMessage, aOptions)->
@@ -211,10 +240,12 @@
     #deepEqual @, aOptions
     for k,v of @mergeOptions()
       continue if k is 'name'
+      ###
       if k is 'encoding'
         if (v.name isnt Type.DEFAULT_ENCODING.name) or aOptions[k]?
           return false unless aOptions[k].name is v.name
         continue
+      ###
       return false unless deepEqual aOptions[k], v
     return true

@@ -242,7 +273,8 @@
     aOptions.name = @name unless aOptions.name
     @createType aOptions
   clone: @::cloneType
-  # Get a global Type class or create new Value from the json string.
+  # Get(create) a global Type class or create new Value from the json string.
+  # it will create a new type object if options is not the original type options.
   @fromJson: (aString)->
     #aString = JSON.parse aString
     Type.from JSON.parse(aString)
@@ -250,13 +282,17 @@
   @createFromJson: (aString)->
     Type.createFrom JSON.parse aString

+  ###
   encode: (aOptions)->
     aOptions = @mergeOptions(aOptions, null, true)
     aOptions.encoding.encode @toObject(aOptions)
   decode: (aEncoded, aOptions) ->
+    aOptions = aOptions.encoding.decode aEncoded
     aOptions = @mergeOptions(aOptions)
-    aOptions.encoding.decode aEncoded
-
+  ###
+
+  # Get(create) a global Type class or create new Value from the parametric type object.
+  # it will create a new type object if options is not the original(default) type options.
   @from: (aObject) ->
     result = Type aObject
     if aObject.value? and result
@@ -279,11 +315,13 @@
     result = @mergeOptions aOptions, null, true
     result[NAME] = @name
     result.fullName = @path()
+    ###
     vEncoding = result.encoding
     if vEncoding and vEncoding.name isnt Type.DEFAULT_ENCODING.name
       result.encoding = vEncoding.name
     else
       delete result.encoding
+    ###
     result
   toObject: (aOptions)->
     if aOptions


* check whether encoding is valid.

* check whether encoding is valid.

--- src/type-info.coffee
+++ src/type-info.coffee
@@ -1,6 +1,7 @@
-factory   = require("custom-factory")
-isObject  = require("util-ex/lib/is/type/object")
-extend    = require("util-ex/lib/_extend")
+factory     = require("custom-factory")
+isObject    = require("util-ex/lib/is/type/object")
+isFunction  = require("util-ex/lib/is/type/function")
+extend      = require("util-ex/lib/_extend")

 getProtoChain = (ctor)->
   result = while ctor and ctor isnt Type
@@ -14,7 +15,12 @@
   @ROOT_NAME: 'type'
   constructor: (aTypeName, aOptions)-> return super
   initialize: (aOptions)->
-    @encoding = aOptions.encoding if aOptions and aOptions.encoding
+    if aOptions and aOptions.encoding
+      encoding = aOptions.encoding
+      if isFunction(encoding.encode) and isFunction(encoding.decode)
+        @encoding = encoding
+      else
+        throw new TypeError "encoding should have encode and decode functions."
   path: ->
     @pathArray().join '/'
   pathArray: ->


Log unhandled exception backtraces in the demo

--- assets/demo.coffee
+++ assets/demo.coffee
@@ -32,6 +32,7 @@
       inspectorController?.incrementRenderCount()

     didThrowError: (error, details) ->
+      console.error("Trix error: %s\n%s", error.toString(), error.stack)
       document.getElementById("trix-debug").classList.add("error")
       @errors ?= []
       @errors.push({error, details})


peillis/helpapp#3455 more rearrangement. looking for clarity

--- test/test_client.coffee
+++ test/test_client.coffee
@@ -196,72 +196,58 @@
       after () ->
         nock.cleanAll

-      it 'search with no type', (done) ->
+      it 'with no type does not send type parameter', (done) ->

         client = new doofinder.Client mock.request.hashid, mock.request.api_key, 5, null, 'fooserver'
         client.search '', (err, res) ->
-          res.parameters.should.contain 'query=', 'page=1', 'rpp=10'
+          res.parameters.should.contain 'query=', 'page=1', 'rpp=10', "hashid=#{ mock.request.hashid }"
           res.parameters.should.have.length 4
           done()

-      it 'search with type', (done) ->
-        client = new doofinder.Client mock.request.hashid, mock.request.api_key, 5, 'product', 'fooserver'
-        client.search '', (err, res) ->
-          res.parameters.should.contain 'query=', 'type=product'
-          done()
-
-      it 'search with multi-type', (done) ->
+      it 'with type, sends type parameter', (done) ->
+        client = new doofinder.Client mock.request.hashid, mock.request.api_key, 5, 'product', 'fooserver'
+        client.search 'test', (err, res) ->
+          res.parameters.should.contain 'query=test', 'type=product'
+          done()
+
+      it 'with types, sends several type parameters', (done) ->
         client = new doofinder.Client mock.request.hashid, mock.request.api_key, 5, ['product', 'category'], 'fooserver'
-        client.search '', (err, res) ->
-          res.parameters.should.contain 'type=category', 'type=product'
-          done()
-
-      # SORT PARAMS
-      it 'search with type and query', (done) ->
-        client = new doofinder.Client mock.request.hashid, mock.request.api_key, 5, 'product', 'fooserver'
-        client.search 'querystring', (err, res) ->
-          res.parameters.should.contain 'query=querystring', 'type=product'
-          done()
-
-      it 'search with type query and params (sort type object of: object, array, string)', (done) ->
+        client.search 'test', (err, res) ->
+          res.parameters.should.contain 'type=category', 'type=product', 'query=test'
+          done()
+
+      it 'with sort object, sends sort params', (done) ->
         client = new doofinder.Client mock.request.hashid, mock.request.api_key, 5, 'product', 'fooserver'
         client.search 'querystring', {sort: mock.sort.Object}, (err, res) ->
           res.parameters.should.contain 'query=querystring', 'sort%5Bprice%5D=asc', 'sort%5Btitle%5D=desc', 'sort%5Bdescription%5D=asc', 'type=product'
           done()

-      it 'search with type query and params (sort type array of: object, array, string)', (done) ->
+      it 'with sort array, sends sort params', (done) ->
         client = new doofinder.Client mock.request.hashid, mock.request.api_key, 5, 'product', 'fooserver'
         client.search 'querystring', {sort: mock.sort.Array}, (err, res) ->
           res.parameters.should.contain 'type=product', 'query=querystring', 'sort%5B0%5D%5Btitle%5D=asc', 'sort%5B1%5D%5Bdescription%5D=desc'
           done()

-      it 'search with type query and params (sort type string of: object, array, string)', (done) ->
+      it 'with sort string, sends sort params', (done) ->
         client = new doofinder.Client mock.request.hashid, mock.request.api_key, 5, 'product', 'fooserver'
         client.search 'querystring', {sort: mock.sort.String}, (err, res) ->
           res.parameters.should.contain 'sort=price', 'query=querystring', 'type=product'
           done()

-      # SINGLE PARAMS
-      it 'search with type query and params (param valid of: valid, null)', (done) ->
+      it 'can propagate any valid search param', (done) ->
         client = new doofinder.Client mock.request.hashid, mock.request.api_key, 5, 'product', 'fooserver'
         client.search 'querystring', mock.params.valid, (err, res) ->
           res.parameters.should.contain 'query=querystring', 'rpp=30', 'page=4', 'type=product'
           done()

-      it 'search with type query and params (param null of: valid, null)', (done) ->
+      it 'will not propagate a search param with no valid value', (done) ->
         client = new doofinder.Client mock.request.hashid, mock.request.api_key, 5, 'product', 'fooserver'
         client.search 'querystring', mock.params.null, (err, res) ->
-          res.parameters.should.contain 'type=product', 'query=querystring'
-          done()
-
-      it 'search with type query and params and one sort of type string', (done) ->
-        client = new doofinder.Client mock.request.hashid, mock.request.api_key, 5, 'product', 'fooserver'
-        client.search 'querystring', {rpp: mock.params.valid.rpp, page: mock.params.valid.page, sort: mock.sort.String}, (err, res) ->
-          res.parameters.should.contain 'type=product', 'page=4', 'rpp=30', 'sort=price', 'query=querystring'
-          done()
-
-      # FILTER PARAMS
-      it 'search with type query and filters', (done) ->
+          res.parameters.should.contain 'type=product', 'query=querystring', "hashid=#{mock.request.hashid}", 'rpp=10', 'page=1'
+          res.parameters.should.have.length 5
+          done()
+
+      it 'can translate filters to params', (done) ->
         client = new doofinder.Client mock.request.hashid, mock.request.api_key, 5, 'product', 'fooserver'
         client.search 'querystring', {filters: mock.filters}, (err, res) ->
           res.parameters.should.contain 'query=querystring', 'filter%5Bcolor%5D=blue', 'filter%5Bcolor%5D=red', 'filter%5Bprice%5D%5Bgte%5D=4.36', 'filter%5Bprice%5D%5Blt%5D=99'


Fixes high score view opening when game is over

--- app/scripts/views/game_state.cjsx
+++ app/scripts/views/game_state.cjsx
@@ -29,6 +29,6 @@
         <HighScoreDataView
           high_scores={@props.high_scores}
           is_game_over={@props.model_state.score.is_game_over}
-          is_open={@props.game_over_is_open}
+          is_open={@props.high_score_is_open}
           doAction={@props.event_handler} />
       </div>


dont need endLength

dont need endLength

--- test/unit/editor.coffee
+++ test/unit/editor.coffee
@@ -59,7 +59,7 @@
     tests =
       'part of line':
         initial:  ['
0123
']
-        expected: Tandem.Delta.makeDelta({ startLength: 0, endLength: 5, ops: [
+        expected: Tandem.Delta.makeDelta({ startLength: 0, ops: [
           { value: '0' }
           { value: '12', attributes: { bold: true } }
           { value: '3\n' }
@@ -67,21 +67,21 @@
         index: 1, length: 2, name: 'bold', value: true
       'trailing newline with normal format':
         initial:  ['
0123
']
-        expected: Tandem.Delta.makeDelta({ startLength: 0, endLength: 5, ops: [
+        expected: Tandem.Delta.makeDelta({ startLength: 0, ops: [
           { value: '0123' }
           { value: '\n', attributes: { bold: true } }
         ]})
         index: 4, length: 1, name: 'bold', value: true
       'trailing newline with line format':
         initial:  ['
0123
']
-        expected: Tandem.Delta.makeDelta({ startLength: 0, endLength: 5, ops: [
+        expected: Tandem.Delta.makeDelta({ startLength: 0, ops: [
           { value: '0123' }
           { value: '\n', attributes: { align: 'right' } }
         ]})
         index: 4, length: 1, name: 'align', value: 'right'
       'part of multiple lines':
         initial:  ['
0123
', '
5678
']
-        expected: Tandem.Delta.makeDelta({ startLength: 0, endLength: 10, ops: [
+        expected: Tandem.Delta.makeDelta({ startLength: 0, ops: [
           { value: '01' }
           { value: '23\n', attributes: { strike: true } }
           { value: '56', attributes: { bold: true, strike: true } }
@@ -91,7 +91,7 @@
         index: 2, length: 5, name: 'strike', value: true
       'line contents with line level format':
         initial:  ['
0123
']
-        expected: Tandem.Delta.makeDelta({ startLength: 0, endLength: 5, ops: [
+        expected: Tandem.Delta.makeDelta({ startLength: 0, ops: [
           { value: '0123\n' }
         ]})
         index: 1, length: 2, name: 'align', value: 'right'


made rep request more colloquial

made rep request more colloquial

--- src/scripts/opendns-umbrella.coffee
+++ src/scripts/opendns-umbrella.coffee
@@ -36,7 +36,7 @@
 #       msg.send "OpenDNS API key not configured."

 module.exports = (robot) ->
-  robot.respond /opendns (.*)/i, (msg) ->
+  robot.respond /whats up with (.*)/i, (msg) ->

     if OPENDNS_KEY?
       artifact = msg.match[1].toLowerCase()
@@ -52,7 +52,7 @@
             for key, value of opendns_json

               switch value
-                when "1" then msg.send "Yeah... #{key} seems ok. :+1:"
+                when "1" then msg.send "#{key} seems legit. :+1:"
                 when "0" then msg.send "Not sure about that #{key}. Use at your own risk."
                 when "-1" then msg.send "That #{key} looks bad. It'll probably own your box. :-1:"
                 else


Update functionality

Update functionality

--- src/smc-webapp/project_store.coffee
+++ src/smc-webapp/project_store.coffee
@@ -176,14 +176,18 @@
                 @push_state('search/' + store.current_path)
             when 'settings'
                 @push_state('settings')
-            else #editor...
+            else # editor...
                 path = misc.tab_to_path(key)
                 @redux.getActions('file_use')?.mark_file(@project_id, path, 'open')
                 @push_state('files/' + path)
                 @set_current_path(misc.path_split(path).head)
-                if redux.getStore('projects').get_my_group(@project_id) != store.open_files.getIn([path, 'component']).is_public
+
+                # Reopen the file if relationship has changed
+                is_public = redux.getStore('projects').get_my_group(@project_id) == 'public'
+                was_public = store.open_files.getIn([path, 'component']).is_public
+                if is_public != was_public
                     @open_file(path : path)
-                    # Reopen the file if relationship has changed
+

     add_a_ghost_file_tab: () =>
         current_num = @get_store().num_ghost_file_tabs
@@ -587,17 +591,14 @@
         # get_my_group is defined.
         id = misc.uuid()
         @set_activity(id:id, status:"getting file listing for #{misc.trunc_middle(path,30)}...")
-        group   = undefined
-        listing = undefined
-        async.series([
+        async.waterfall([
             (cb) =>
                 # make sure that our relationship to this project is known.
                 @redux.getStore('projects').wait
                     until   : (s) => s.get_my_group(@project_id)
                     timeout : 30
-                    cb      : (err, x) =>
-                        group = x; cb(err)
-            (cb) =>
+                    cb      : cb
+            (group, cb) =>
                 store = @get_store()
                 if not store?
                     cb("store no longer defined"); return
@@ -611,10 +612,8 @@
                     hidden     : show_hidden
                     max_time_s : 4*60  # keep trying for up to 4 minutes
                     group      : group
-                    cb         : (err, x) =>
-                        listing = x
-                        cb(err)
-        ], (err) =>
+                    cb         : cb
+        ], (err, listing) =>
             @set_activity(id:id, stop:'')
             # Update the path component of the immutable directory listings map:
             store = @get_store()
@@ -623,6 +622,7 @@
             map = store.directory_listings.set(path, if err then misc.to_json(err) else immutable.fromJS(listing.files))
             @setState(directory_listings : map)
             delete @_set_directory_files_lock[_key] # done!
+            opts.cb(err, listing)
         )

     # Increases the selected file index by 1
@@ -1206,6 +1206,7 @@
                 if target[target.length-1] == '/' or full_path == ''
                     @open_directory(parent_path)
                 else
+                    # TODOJ: Change when directory listing is synchronized. Just have to query client state then.
                     @fetch_directory_listing(parent_path, true, true)
                     @get_store().wait
                         until   : (s) =>
@@ -1214,6 +1215,11 @@
                         timeout : 30
                         cb      : (err, item) =>
                             if err
+                                console.log "ERR", err
+                                @open_file
+                                    path       : full_path
+                                    foreground : foreground
+                                    foreground_project : foreground
                                 alert_message(type:'error', message:"There was an error related to opening the link: #{err}")
                                 @open_directory(parent_path)
                             else
@@ -1539,6 +1545,7 @@
         method = salvus_client.project_directory_listing
     else
         method = salvus_client.public_project_directory_listing
+        console.log "FETCHING WITH PUBLIC VALS"
     listing = undefined
     f = (cb) ->
         method


Clean ls-api with coffeescript syntax

Clean ls-api with coffeescript syntax

--- elements/livesnooker-api.coffee
+++ elements/livesnooker-api.coffee
@@ -33,7 +33,7 @@
   setAuthentication: (auth) ->
     data.authentication = auth
     @maybeFetchUser()
-    @fire('iron-signal', {name: "api-ready"}) if data.authentication # Prev async
+    @fire('iron-signal', {name: "api-ready"}) if data.authentication

   maybeFetchUser: ->
     @fetchUser() if data.host && data.authentication
@@ -44,49 +44,46 @@

   setUser: (user) ->
     data.user = user
-    this.fire('iron-signal', {name: "account", data: user}) # Prev async
-    this.user = data.user
+    @fire('iron-signal', {name: "account", data: user})
+    @user = data.user

   ajax: (path, settings) ->
-    _this = this
     if apiIsReady()
-      this.ajaxPromise(path, settings)
+      @ajaxPromise(path, settings)
     else
-      new Promise (resolve, reject) ->
-        _this.queue = true
-        _this.addEventListener "api-ready", ->
-          _this.ajaxPromise(path, settings).then(resolve, reject)
+      new Promise (resolve, reject) =>
+        @queue = true
+        @addEventListener "api-ready", =>
+          @ajaxPromise(path, settings).then(resolve, reject)

   ajaxPromise: (path, settings) ->
-    Promise.resolve(this.ajaxCall(path, settings))
+    Promise.resolve(@ajaxCall(path, settings))

   callbackAjax: (path, settings) ->
-    _this = this
     if apiIsReady()
-      _this.ajaxCall(path, settings)
+      @ajaxCall(path, settings)
     else
-      _this.addEventListener "api-ready", ->
-        _this.ajaxCall(path, settings)
+      @addEventListener "api-ready", =>
+        @ajaxCall(path, settings)

   ajaxCall: (path, settings) ->
     settings = settings || {}
     settings["headers"] = settings["headers"] || {}
-    _.extend(settings.headers, {
+    _.extend settings.headers,
       "X-AUTH-GOOGLE-ID-TOKEN": data.authentication["id_token"]
-    })
     $.ajax(data.host + path, settings)

   onAccount: ->
-    this.user = data.user
+    @user = data.user

   onApiReady: ->
-    this.fire('api-ready') # Prev async
-    this.fire('api')
+    @fire('api-ready')
+    @fire('api')

   ready: ->
-    if this.host
-      data.host = this.host
-      this.fire('iron-signal', {name: "api-ready"}) if data.authentication # Prev async
+    if @host
+      data.host = @host
+      @fire('iron-signal', {name: "api-ready"}) if data.authentication
     else if data.host
-      this.host = data.host
-    this.user = data.user
+      @host = data.host
+    @user = data.user


fix : workspace.open with initial cursor position bug

When opening already opened file via `atom.workspace.open(path,{ initialLine: 0,  initialColumn: 0, searchAllPanes: true} )`, cursor position doesn't move `[0,0]`.

Expected result:
Whether or not file is opened already, cursor position has to be [0,0].

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -472,7 +472,7 @@
           initialLine = options.initialLine
         if Number.isFinite(options.initialColumn)
           initialColumn = options.initialColumn
-        if initialLine > 0 or initialColumn > 0
+        if initialLine > 0 or initialColumn > 0 or options.initialLine? or options.initialColumn?
           item.setCursorBufferPosition?([initialLine, initialColumn])

         index = pane.getActiveItemIndex()


Re-enable sources when removing final action from target

--- static/coffee/flows/services.coffee
+++ static/coffee/flows/services.coffee
@@ -218,9 +218,16 @@

   disconnectAllConnections: (id) ->
+
+    # reenable any sources connecting to us
+    jsPlumb.select({target:id}).each (connection) ->
+      jsPlumb.setSourceEnabled($('#' + connection.sourceId), true)
+
+    # now disconnect the existing connections
     jsPlumb.detachAllConnections(id)

     $('#' + id + ' .source').each ->
+      jsPlumb.setSourceEnabled($(this), true)
       jsPlumb.detachAllConnections($(this))

   disconnectOutboundConnections: (id) ->


Don't log JS errors in Android WebView

Don't log JS errors in Android WebView

--- assets/javascripts/app/app.coffee
+++ assets/javascripts/app/app.coffee
@@ -62,6 +62,8 @@
               if @isInjectionError()
                 @onInjectionError()
                 return false
+              if @isAndroidWebview()
+                return false
             true
           dataCallback: (data) ->
             try


conv operation

conv operation

--- grammars/intuicio4.cson
+++ grammars/intuicio4.cson
@@ -179,7 +179,7 @@
 	}
 	{
 		# operation
-		'match': '\\b(call|async|join|ret|mov|movb|add|sub|mul|div|and|or|xor|neg|lsh|rsh|eq|ls|gr|push|pop|drop|cpush|cpop|cdrop|ctxc|ctxd|ctxs|jif|goto|jump|address|new|del|ptr|val|cast|nil|vargs|vargr|mtxc|mtxd|lock|unlock)\\b'
+		'match': '\\b(call|async|join|ret|mov|movb|conv|add|sub|mul|div|and|or|xor|neg|lsh|rsh|eq|ls|gr|push|pop|drop|cpush|cpop|cdrop|ctxc|ctxd|ctxs|jif|goto|jump|address|new|del|ptr|val|cast|nil|vargs|vargr|mtxc|mtxd|lock|unlock)\\b'
 		'name': 'keyword.control.operation.intuicio4'
 	}
 	{