Fix missing highlighting for negative ditroff values

--- grammars/output.cson
+++ grammars/output.cson
@@ -153,7 +153,7 @@
 			},{
 				# Set character height or slant
 				name: "meta.set-character-property.ditroff"
-				match: "\\G([HS]\\S*)(?:\\s+(\\d+))?\\s*?(?=$|#)"
+				match: "\\G([HS]\\S*)(?:\\s+(-?\\d+))?\\s*?(?=$|#)"
 				captures:
 					1: name: "keyword.device.control.ditroff"
 					2: name: "constant.numeric.ditroff"
@@ -229,7 +229,7 @@
 	# Move X units vertically or horizontally
 	move:
 		name: "meta.move.ditroff"
-		match: "([HhVv])\\s*(\\d+)"
+		match: "([HhVv])\\s*(-?\\d+)"
 		captures:
 			1: name: "entity.name.function.ditroff"
 			2: name: "constant.numeric.integer.ditroff"
@@ -259,7 +259,7 @@

 			# Print character by index in current font
 			name: "meta.print-character.indexed.ditroff"
-			match: "(N)\\s*(\\d+)"
+			match: "(N)\\s*(-?\\d+)"
 			captures:
 				1: name: "entity.name.function.ditroff"
 				2: name: "constant.numeric.integer.ditroff"
@@ -286,7 +286,7 @@
 		},{
 			# Print text with track-kerning (GNU)
 			name: "meta.print-text.track-kerned.gnu.ditroff"
-			begin: "^\\s*(u)\\s*(\\d+)\\s*"
+			begin: "^\\s*(u)\\s*(-?\\d+)\\s*"
 			end:   "(?=\\s|$)"
 			contentName: "string.quoted.double.ditroff"
 			beginCaptures:


example(controller): Fixed interpolation bug

--- src/example_controller/example_controller.coffee
+++ src/example_controller/example_controller.coffee
@@ -71,7 +71,7 @@
   $scope.uploadRoute         = "/test_upload"
   $scope.fileUploaderEnabled = true
   $scope.uploadPreviews      = []
-  $scope.previewImage        = (preview) ->
+  $scope.previewImage        = (preview = {}) ->
     if /image./.test(preview.type)  then preview.fileData else "/img/file_icon.png"
   $scope.previewProgress = (preview) ->
     return width: "#{preview.progress}%"


Fix typo

Fix typo

--- src/javascripts/includes/stations_request.coffee
+++ src/javascripts/includes/stations_request.coffee
@@ -17,6 +17,6 @@
   extractStationNames: (data=[]) ->
     i.value for i in data

-# The loaded script sets values on this object and call showSuggestion.
+# The loaded script sets values on this object and calls showSuggestion.
 SLs = showSuggestion: ->


stacktrace in prod in some old code.... ?'d it.

--- src/smc-webapp/r_misc/old-coffee-code-index.cjsx
+++ src/smc-webapp/r_misc/old-coffee-code-index.cjsx
@@ -1081,12 +1081,14 @@

     render_time: ->
-        time = @props.state?.get('time')
+        time = @props.state?.get?('time')
         if time
             return <Space/> (<exports.TimeAgo date={time} />)

     render: ->
-        s = COMPUTE_STATES[@props.state?.get('state')]
+        # In production I hit a traceback in which @props.state was defined
+        # but did not have a get attribute.  No clue why.
+        s = COMPUTE_STATES[@props.state?.get?('state')]
         if not s?
             return <Loading />
         {display, desc, icon, stable} = s


fixed x axis tick positioning with negative values

--- src/viz/types/helpers/graph/includes/plot.coffee
+++ src/viz/types/helpers/graph/includes/plot.coffee
@@ -186,7 +186,8 @@
   if xValues.length is 1
     xMaxWidth = vars.axes.width
   else
-    xMaxWidth   = vars.x.scale.viz(xValues[1]) - vars.x.scale.viz(xValues[0])
+    xMaxWidth  = vars.x.scale.viz(xValues[1]) - vars.x.scale.viz(xValues[0])
+    xMaxWidth  = Math.abs xMaxWidth
     xMaxWidth -= vars.labels.padding * 2

   if xAxisWidth > xMaxWidth and xText.join("").indexOf(" ") > 0
@@ -203,6 +204,7 @@

   vars.x.ticks.hidden   = false
   vars.x.ticks.baseline = "auto"
+
   if xAxisWidth <= xMaxWidth
     vars.x.ticks.rotate = 0
   else if xAxisWidth < vars.axes.height/2


Left line out of comment

Left line out of comment

--- static/src/scripts/app/views/TeamsMapView.coffee
+++ static/src/scripts/app/views/TeamsMapView.coffee
@@ -91,9 +91,8 @@
         @infowindow.open @map, @startMarker

       @markerBounds.extend @startMarker.position
-
+      ###

-###
       # only add marker after the compeition has started
       endMarkerContext =
         title: 'Location X'
@@ -112,11 +111,9 @@
       google.maps.event.addListener @endMarker, 'click', (endMarker) =>
         @infowindow.setContent @endMarker.html
         @infowindow.open @map, @endMarker
-###
-

       @markerBounds.extend @endMarker.position
-
+      ###
       # used to force minimum zoom if all competitors at the start position
       @markerBounds.extend (new google.maps.LatLng(53.3471, -6.28789))


Corrections

Corrections

--- public/js/pandacloudwatch.coffee
+++ public/js/pandacloudwatch.coffee
@@ -1,8 +1,8 @@
 #===============================================================================
-# PandaPandaCloudWatch is a data visiualization tool for Amazon Web Services.
-# The idea is to keep things simple and Web-native.   We make use of Dygraph
-# a data visualization library written in JavaScript that acts of HTML div elements.
-# Data is accessed from AWS via Amazon's javascript API.
+# Panda Panopticon is a data visiualization tool for Amazon Web Services.
+# The idea is to keep things simple and Web-native.   We make use of HighCharts,
+# a data visualization library written in JavaScript.
+# Data is accessed from AWS via Amazon's API (Node SDK).
 #
 # This tool is written in CoffeeScript.
 #
@@ -148,8 +148,8 @@

   foo_1 = document.getElementById('login1').invalid
-  foo_2 = window.document.getElementById('login2').invalid
-  foo_3 = window.document.getElementById('login3').invalid
+  foo_2 = document.getElementById('login2').invalid
+  foo_3 = document.getElementById('login3').invalid

   if foo_1 == false and foo_2 == false and foo_3 == false
     # We may call Amzaon.  Prepare an AJAX call to our proxy server.


Stub the routers getCurrentParams method

This will break if/when JS ever becomes multi-threaded.  Which will
probably never happen.

--- test/components/helpers/component-testing.coffee
+++ test/components/helpers/component-testing.coffee
@@ -9,14 +9,14 @@
 {commonActions} = require './utilities'
 sandbox = null
 ROUTER = null
-
+CURRENT_ROUTER_PARAMS = null
 # Mock a router for the context
 beforeEach ->
   sandbox = sinon.sandbox.create()
   ROUTER  = sandbox.spy()
   ROUTER.makeHref = sandbox.spy()
   ROUTER.transitionTo = sandbox.spy()
-
+  ROUTER.getCurrentParams = sandbox.spy( -> CURRENT_ROUTER_PARAMS )
 afterEach ->
   sandbox.restore()

@@ -35,6 +35,7 @@

   renderComponent: (component, options = {}) ->
     options.props ||= {}
+    CURRENT_ROUTER_PARAMS = options.routerParams or {}
     root = document.createElement('div')
     promise = new Promise( (resolve, reject) ->
       props = _.clone(options.props)
@@ -50,6 +51,7 @@
     # defer adding the then callback so it'll be called after whatever is attached after the return
     _.defer -> promise.then ->
       React.unmountComponentAtNode(root)
+      CURRENT_ROUTER_PARAMS = {}
       return arguments
     promise


prep for merge

prep for merge

--- salvus/page/project_files.cjsx
+++ salvus/page/project_files.cjsx
@@ -436,14 +436,14 @@
     displayName : "ProjectFiles-ProjectFilesActions"

     propTypes :
-        checked_files       : rtypes.object
-        listing             : rtypes.array
-        file_action         : rtypes.string
-        page                : rtypes.number
-        page_size           : rtypes.number
-        project_id          : rtypes.string
-        current_path        : rtypes.array
-        flux                : rtypes.object.isRequired
+        checked_files : rtypes.object
+        listing       : rtypes.array
+        file_action   : rtypes.string
+        page          : rtypes.number
+        page_size     : rtypes.number
+        project_id    : rtypes.string
+        current_path  : rtypes.array
+        flux          : rtypes.object.isRequired

     getInitialState : ->
         select_entire_directory : 'hidden' # hidden -> check -> clear


fix skipping creating already-existent previews

--- scripts/create_previews.coffee
+++ scripts/create_previews.coffee
@@ -1,4 +1,5 @@
 path = require 'path'
+fs = require 'fs'

 _ = require 'underscore'
 Q = require 'q'
@@ -14,6 +15,11 @@
 {Semaphore} = require '../shared/helpers/concurrency_helper'

 DEFAULT_QUALITY = 60
+
+fileExists = (filename) ->
+  deferred = Q.defer()
+  fs.exists filename, (exists) -> deferred.resolve exists
+  deferred.promise

 parseSize = (size) ->
   size = /(\d+)x(\d+)(?:@(\d+))?/.exec size
@@ -44,21 +50,26 @@
     src: mkPath root, getOriginal(picture).src
     dst: mkPath root, dstPathOnServer

-  makeDirectories(path.dirname(resizeOpts.dst)).then ->
-    resizeImage(resizeOpts).spread (resized) ->
-      albumUpdateSemaphore.push ->
-        getAlbum(path: albumPath).then (album) ->
-          picture = _.find album.pictures, (pic) -> pic.path == picture.path
-          picture.media.push
-            width: parseInt resized.width
-            height: parseInt resized.height
-            src: dstPathOnServer
-          picture.media = _.sortBy picture.media, (medium) -> medium.width
-          saveAlbum(album)
-        .then ->
-          process.stdout.write '.' unless quiet
-    .fail ->
-      console.warn '\nFailed to create thumbnail:', resizeOpts.src
+  fileExists(resizeOpts.dst).then (exists) ->
+    if exists
+      process.stdout.write '-'
+      return Q.when {}
+
+    makeDirectories(path.dirname(resizeOpts.dst)).then ->
+      resizeImage(resizeOpts).spread (resized) ->
+        albumUpdateSemaphore.push ->
+          getAlbum(path: albumPath).then (album) ->
+            picture = _.find album.pictures, (pic) -> pic.path == picture.path
+            picture.media.push
+              width: parseInt resized.width
+              height: parseInt resized.height
+              src: dstPathOnServer
+            picture.media = _.sortBy picture.media, (medium) -> medium.width
+            saveAlbum(album)
+          .then ->
+            process.stdout.write '.' unless quiet
+      .fail ->
+        console.warn '\nFailed to create thumbnail:', resizeOpts.src

 createPreviews = (opts) ->
   {albums, sizes, concurrency, root, output, quiet} = opts
@@ -68,21 +79,16 @@
   albums.forEach (album) ->
     album.pictures.forEach (picture) ->
       sizes.forEach (size) ->
-        existingPreview = _.find picture.media, (medium) -> medium.width == size.width or medium.height == size.height
-
-        if existingPreview
-          process.stdout.write '-'
-        else
-          do (album, picture, size) ->
-            sem.push ->
-              createPreview
-                albumPath: album.path
-                picture: picture
-                size: size
-                root: root
-                output: output
-                quiet: quiet
-            .done()
+        do (album, picture, size) ->
+          sem.push ->
+            createPreview
+              albumPath: album.path
+              picture: picture
+              size: size
+              root: root
+              output: output
+              quiet: quiet
+          .done()

   sem.finished


bolding search results

bolding search results

--- vendor/assets/javascripts/token-text-area.coffee
+++ vendor/assets/javascripts/token-text-area.coffee
@@ -107,17 +107,20 @@
   openAutocomplete: ->
     # Query server for autocomplete suggestions.
     if @options.onQuery
-      @options.onQuery @word[0].trim(), (results) =>
+      trimmedQuery = @word[0].trim()
+      @options.onQuery trimmedQuery, (results) =>
         # Save currently selected suggestion to re-higlight it.
         selected = @resultList.find(".selected")
         @closeAutocomplete()

         # Populate results list.
+
         for result in results
+          boldedName = result.name.split(trimmedQuery).join("#{trimmedQuery}")
           if result.tokenDisplayName
-            @resultList.append("
#{result.name}
")
+            @resultList.append("
#{boldedName}
")
           else
-            @resultList.append("
#{result.name}
")
+            @resultList.append("
#{boldedName}
")

         # Re-select previously selected suggestion.
         $("li[data-id=" + selected.attr("data-id") + "]").addClass("selected") unless selected is null


rebased against master

rebased against master

--- server/controllers/ical.coffee
+++ server/controllers/ical.coffee
@@ -7,7 +7,7 @@
 child = require 'child_process'
 rm = require 'rimraf'
 zip = require 'bauer-zip'
-# path = require 'path'
+
 localization = require '../libs/localization_manager'

 module.exports.export = (req, res) ->


manager applied to webserver

manager applied to webserver

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -1,6 +1,14 @@
 {argv} = require 'optimist'

-KONFIG = require argv.c?.trim()
+# KONFIG = require argv.c?.trim()
+#Config = require('koding-config-manager')
+
+
+
+KONFIG = require('koding-config-manager').factory("main.#{argv.c}")
+
+console.log KONFIG
+
 {webserver, mongo, mq, projectRoot, kites, uploads, basicAuth} = KONFIG

 webPort = argv.p ? webserver.port


remove node cluster

remove node cluster

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -4,207 +4,201 @@

 webPort = argv.p ? webserver.port

-cluster = require 'cluster'
-
-if cluster.isMaster
-  cluster.fork() for i in [0...webserver.clusterSize]
-  cluster.on "exit", (worker, code, signal) ->
-    cluster.fork()
-else
-  processMonitor = (require 'processes-monitor').start
-    name : "webServer on port #{webPort}"
-    stats_id: "webserver." + process.pid
-    interval : 30000
-    librato: KONFIG.librato
-    limit_hard  :
-      memory   : 300
-      callback : ->
-        console.log "[WEBSERVER #{webPort}] Using excessive memory, exiting."
-        process.exit()
-    die :
-      after: "non-overlapping, random, 3 digits prime-number of minutes"
-      middleware : (name,callback) -> koding.disconnect callback
-      middlewareTimeout : 5000
-
-  # Services (order is important here)
-  services =
-    kite_webterm:
-      pattern: 'webterm'
-    worker_auth:
-      pattern: 'auth'
-    kite_sharedhosting:
-      pattern: 'kite-sharedHosting'
-    kite_applications:
-      pattern: 'kite-application'
-    kite_databases:
-      pattern: 'kite-database'
-    webserver:
-      pattern: 'web'
-    worker_social:
-      pattern: 'social'
-
-  incService = (serviceKey, inc) ->
-    for key, value of services
-      if serviceKey.indexOf(value.pattern) > -1
-        value.count = if value.count then value.count += inc else 1
-        break
-
-  # Presences
-  koding = require './bongo'
-  koding.connect ->
-    koding.monitorPresence
-      join: (serviceKey) ->
-        incService serviceKey, 1
-      leave: (serviceKey) ->
-        incService serviceKey, -1
-
-  {extend} = require 'underscore'
-  express  = require 'express'
-  Broker   = require 'broker'
-  fs       = require 'fs'
-  hat      = require 'hat'
-  nodePath = require 'path'
-
-  app      = express()
-
-  # this is a hack so express won't write the multipart to /tmp
-  #delete express.bodyParser.parse['multipart/form-data']
-
-  app.configure ->
-    app.set 'case sensitive routing', on
-    app.use express.cookieParser()
-    app.use express.session {"secret":"foo"}
-    app.use express.bodyParser()
-    app.use express.compress()
-    app.use express.static "#{projectRoot}/website/"
-
-  app.use (req, res, next)->
-    res.removeHeader "X-Powered-By"
-    next()
-
-  if basicAuth
-    app.use express.basicAuth basicAuth.username, basicAuth.password
-
-  process.on 'uncaughtException',(err)->
-    console.log 'there was an uncaught exception'
-    console.error err
-    stack = err?.stack
-    console.log stack  if stack?
-
-  koding = require './bongo'
-
-  authenticationFailed = (res, err)->
-    res.send "forbidden! (reason: #{err?.message or "no session!"})", 403
-
-  startTime = null
-  app.get "/-/cache/latest", (req, res)->
-    {JActivityCache} = koding.models
-    startTime = Date.now()
-    JActivityCache.latest (err, cache)->
-      if err then console.warn err
-      # console.log "latest: #{Date.now() - startTime} msecs!"
-      return res.send if cache then cache.data else {}
-
-  app.get "/-/cache/before/:timestamp", (req, res)->
-    {JActivityCache} = koding.models
-    JActivityCache.before req.params.timestamp, (err, cache)->
-      if err then console.warn err
-      res.send if cache then cache.data else {}
-
-  app.get "/Logout", (req, res)->
-    res.clearCookie 'clientId'
-    res.redirect 302, '/'
-
-  if uploads?.enableStreamingUploads
-
-    s3 = require('./s3') uploads.s3
-
-    app.post '/Upload', s3..., (req, res)->
-      res.send(for own key, file of req.files
-        filename  : file.filename
-        resource  : nodePath.join uploads.distribution, file.path
-      )
-
-    app.get '/Upload/test', (req, res)->
-      res.send \
-        """
-
-        <form method="post" action="/upload" enctype="multipart/form-data" onsubmit="return submitForm(this)">
-
Title: <input type="text" name="title" />

-
Image: <input type="file" name="image" id="image" />

-
<input type="submit" value="Upload" />

-        </form>
-        """
-
-  app.get "/", (req, res)->
-    if frag = req.query._escaped_fragment_?
-      res.send 'this is crawlable content'
+processMonitor = (require 'processes-monitor').start
+  name : "webServer on port #{webPort}"
+  stats_id: "webserver." + process.pid
+  interval : 30000
+  librato: KONFIG.librato
+  limit_hard  :
+    memory   : 300
+    callback : ->
+      console.log "[WEBSERVER #{webPort}] Using excessive memory, exiting."
+      process.exit()
+  die :
+    after: "non-overlapping, random, 3 digits prime-number of minutes"
+    middleware : (name,callback) -> koding.disconnect callback
+    middlewareTimeout : 5000
+
+# Services (order is important here)
+services =
+  kite_webterm:
+    pattern: 'webterm'
+  worker_auth:
+    pattern: 'auth'
+  kite_sharedhosting:
+    pattern: 'kite-sharedHosting'
+  kite_applications:
+    pattern: 'kite-application'
+  kite_databases:
+    pattern: 'kite-database'
+  webserver:
+    pattern: 'web'
+  worker_social:
+    pattern: 'social'
+
+incService = (serviceKey, inc) ->
+  for key, value of services
+    if serviceKey.indexOf(value.pattern) > -1
+      value.count = if value.count then value.count += inc else 1
+      break
+
+# Presences
+koding = require './bongo'
+koding.connect ->
+  koding.monitorPresence
+    join: (serviceKey) ->
+      incService serviceKey, 1
+    leave: (serviceKey) ->
+      incService serviceKey, -1
+
+{extend} = require 'underscore'
+express  = require 'express'
+Broker   = require 'broker'
+fs       = require 'fs'
+hat      = require 'hat'
+nodePath = require 'path'
+
+app      = express()
+
+# this is a hack so express won't write the multipart to /tmp
+#delete express.bodyParser.parse['multipart/form-data']
+
+app.configure ->
+  app.set 'case sensitive routing', on
+  app.use express.cookieParser()
+  app.use express.session {"secret":"foo"}
+  app.use express.bodyParser()
+  app.use express.compress()
+  app.use express.static "#{projectRoot}/website/"
+
+app.use (req, res, next)->
+  res.removeHeader "X-Powered-By"
+  next()
+
+if basicAuth
+  app.use express.basicAuth basicAuth.username, basicAuth.password
+
+process.on 'uncaughtException',(err)->
+  console.log 'there was an uncaught exception'
+  console.log process.pid
+#    console.error err
+#    stack = err?.stack
+#    console.log stack  if stack?
+
+koding = require './bongo'
+
+authenticationFailed = (res, err)->
+  res.send "forbidden! (reason: #{err?.message or "no session!"})", 403
+
+startTime = null
+app.get "/-/cache/latest", (req, res)->
+  {JActivityCache} = koding.models
+  startTime = Date.now()
+  JActivityCache.latest (err, cache)->
+    if err then console.warn err
+    # console.log "latest: #{Date.now() - startTime} msecs!"
+    return res.send if cache then cache.data else {}
+
+app.get "/-/cache/before/:timestamp", (req, res)->
+  {JActivityCache} = koding.models
+  JActivityCache.before req.params.timestamp, (err, cache)->
+    if err then console.warn err
+    res.send if cache then cache.data else {}
+
+app.get "/Logout", (req, res)->
+  res.clearCookie 'clientId'
+  res.redirect 302, '/'
+
+if uploads?.enableStreamingUploads
+
+  s3 = require('./s3') uploads.s3
+
+  app.post '/Upload', s3..., (req, res)->
+    res.send(for own key, file of req.files
+      filename  : file.filename
+      resource  : nodePath.join uploads.distribution, file.path
+    )
+
+  app.get '/Upload/test', (req, res)->
+    res.send \
+      """
+
+      <form method="post" action="/upload" enctype="multipart/form-data" onsubmit="return submitForm(this)">
+
Title: <input type="text" name="title" />

+
Image: <input type="file" name="image" id="image" />

+
<input type="submit" value="Upload" />

+      </form>
+      """
+
+app.get "/", (req, res)->
+  if frag = req.query._escaped_fragment_?
+    res.send 'this is crawlable content'
+  else
+    # log.info "serving index.html"
+    res.header 'Content-type', 'text/html'
+    fs.readFile "#{projectRoot}/website/index.html", (err, data) ->
+      throw err if err
+      res.send data
+
+app.get "/-/presence/:service", (req, res) ->
+  {service} = req.params
+  if services[service] and services[service].count > 0
+    res.send 200
+  else
+    res.send 404
+
+app.get "/-/status/:event/:kiteName",(req,res)->
+  # req.params.data
+
+  obj =
+    processName : req.params.kiteName
+    # processId   : KONFIG.crypto.decrypt req.params.encryptedPid
+
+  koding.mq.emit 'public-status', req.params.event, obj
+  res.send "got it."
+
+app.get "/-/api/user/:username/flags/:flag", (req, res)->
+  {username, flag} = req.params
+  {JAccount}       = koding.models
+  JAccount.one "profile.nickname" : username, (err, account)->
+    if err or not account
+      state = false
     else
-      # log.info "serving index.html"
-      res.header 'Content-type', 'text/html'
-      fs.readFile "#{projectRoot}/website/index.html", (err, data) ->
-        throw err if err
-        res.send data
-
-  app.get "/-/presence/:service", (req, res) ->
-    {service} = req.params
-    if services[service] and services[service].count > 0
-      res.send 200
-    else
-      res.send 404
-
-  app.get "/-/status/:event/:kiteName",(req,res)->
-    # req.params.data
-
-    obj =
-      processName : req.params.kiteName
-      # processId   : KONFIG.crypto.decrypt req.params.encryptedPid
-
-    koding.mq.emit 'public-status', req.params.event, obj
-    res.send "got it."
-
-  app.get "/-/api/user/:username/flags/:flag", (req, res)->
-    {username, flag} = req.params
-    {JAccount}       = koding.models
-    JAccount.one "profile.nickname" : username, (err, account)->
-      if err or not account
-        state = false
-      else
-        state = account.checkFlag('super-admin') or account.checkFlag(flag)
-      res.end "#{state}"
-
-  getAlias = do->
-    caseSensitiveAliases = ['auth']
-    (url)->
-      rooted = '/' is url.charAt 0
-      url = url.slice 1  if rooted
-      if url in caseSensitiveAliases
-        alias = "#{url.charAt(0).toUpperCase()}#{url.slice 1}"
-      if alias and rooted then "/#{alias}" else alias
-
-  app.get '*', (req,res)->
-    {url} = req
-    queryIndex = url.indexOf '?'
-    [urlOnly, query] =\
-      if ~queryIndex then [url.slice(0, queryIndex), url.slice(queryIndex)]
-      else [url, '']
-    alias = getAlias urlOnly
-    redirectTo = if alias then "#{alias}#{query}" else "/#!#{urlOnly}#{query}"
-    res.header 'Location', redirectTo
-    res.send 302
-
-  app.listen webPort
-
-  console.log '[WEBSERVER] running', "http://localhost:#{webPort} pid:#{process.pid}"
+      state = account.checkFlag('super-admin') or account.checkFlag(flag)
+    res.end "#{state}"
+
+getAlias = do->
+  caseSensitiveAliases = ['auth']
+  (url)->
+    rooted = '/' is url.charAt 0
+    url = url.slice 1  if rooted
+    if url in caseSensitiveAliases
+      alias = "#{url.charAt(0).toUpperCase()}#{url.slice 1}"
+    if alias and rooted then "/#{alias}" else alias
+
+app.get '*', (req,res)->
+  {url} = req
+  queryIndex = url.indexOf '?'
+  [urlOnly, query] =\
+    if ~queryIndex then [url.slice(0, queryIndex), url.slice(queryIndex)]
+    else [url, '']
+  alias = getAlias urlOnly
+  redirectTo = if alias then "#{alias}#{query}" else "/#!#{urlOnly}#{query}"
+  res.header 'Location', redirectTo
+  res.send 302
+
+app.listen webPort
+
+console.log '[WEBSERVER] running', "http://localhost:#{webPort} pid:#{process.pid}"


Delete unused methods

Delete unused methods

--- server/models/indicator.coffee
+++ server/models/indicator.coffee
@@ -319,25 +319,6 @@
     )
   )

-calculateRecency = (indicator, callback) ->
-  indicator.calculateRecencyOfHeadline().then((recency)->
-    indicator.narrativeRecency = recency
-    callback()
-  ).catch((err) ->
-    callback(err)
-  )
-
-indicatorSchema.statics.calculateNarrativeRecency = (indicators) ->
-  deferred = Q.defer()
-
-  async.each indicators, calculateRecency, (err) ->
-    if err?
-      deferred.reject(err)
-    else
-      deferred.resolve()
-
-  return deferred.promise
-
 indicatorSchema.statics.convertNestedParametersToAssociationIds = (attributes) ->
   if attributes.theme? and typeof attributes.theme is 'object'
     attributes.theme = attributes.theme._id.toString()


pass "disabled" prop onto link element

pass "disabled" prop onto link element

--- shared/src/factories/link.cjsx
+++ shared/src/factories/link.cjsx
@@ -6,7 +6,7 @@
 filterPropsBase = filterProps

 LINK_PROPS = [
-  'alt', 'title', 'activeOnlyWhenExact', 'activeStyle', 'activeClassName', 'isActive', 'location',
+  'alt', 'title', 'activeOnlyWhenExact', 'activeStyle', 'activeClassName', 'isActive', 'location', 'disabled'
 ]

 filterProps = (props, options = {}) ->


Rename rendering helper method

Rename rendering helper method

--- spec/lanes/components/grid/RowEditorSpec.coffee
+++ spec/lanes/components/grid/RowEditorSpec.coffee
@@ -12,7 +12,7 @@
 )
 LAST_ROW_SELECTOR = '.fixedDataTableRowLayout_rowWrapper:last-child .public_fixedDataTableCell_cellContent'

-SetCode = (q, value) ->
+RenderEdit = (q, value) ->
     grid = LT.renderComponent(LC.Grid, props: {
         query: q, editor: true
     })
@@ -45,22 +45,21 @@
         expect(editor.props.model.id).toEqual(2)

     it 'can edit both rows and collections', ->
-        {editor} = SetCode(@query, 'BOB')
+        {editor} = RenderEdit(@query, 'BOB')
         _.dom(editor).qs('.btn.save').click()
         expect( @query.results.rowAt(1)[1] ).toEqual('BOB')

         @query.src = @collection
-        {editor} = SetCode(@query, 'BOB')
+        {editor} = RenderEdit(@query, 'BOB')
         _.dom(editor).qs('.btn.save').click()
         expect(@collection.at(1).code).toEqual('BOB')

     it 'does not update when edit is canceled', ->
-        {editor} = SetCode(@query, 'CANCKED')
+        {editor} = RenderEdit(@query, 'CANCKED')
         _.dom(editor).qs('.btn.cancel').click()
         expect( @query.results.rowAt(1)[1] ).toEqual('TEST2')
-
         @query.src = @collection
-        {editor} = SetCode(@query, 'CANCELED')
+        {editor} = RenderEdit(@query, 'CANCELED')
         _.dom(editor).qs('.btn.cancel').click()

         expect( @collection.at(1).code ).toEqual('TEST2')


Alerts users about an enabled email filter.

--- picoCTF-web/web/coffee/front-page.coffee
+++ picoCTF-web/web/coffee/front-page.coffee
@@ -6,10 +6,12 @@
 Glyphicon = ReactBootstrap.Glyphicon
 ButtonInput = ReactBootstrap.ButtonInput
 ButtonGroup = ReactBootstrap.ButtonGroup
+Alert = ReactBootstrap.Alert

 update = React.addons.update

 LoginForm = React.createClass
+
   render: ->
     userGlyph = <Glyphicon glyph="user"/>
     lockGlyph = <Glyphicon glyph="lock"/>
@@ -34,14 +36,21 @@
         </form>
       </Panel>
     else
+      showEmailFilter = (->
+        <Alert bsStyle="warning">
+          You can register provided you have an email for one of these domains: {@props.emailFilter.join ", "}.
+        </Alert>
+      ).bind this
+
       registrationForm = if @props.status == "Register" then \

           <Row>
-            <Col md={6}>
-              <Input type="text" id="first-name" valueLink={@props.firstname} label="Firstname"/>
-            </Col>
-            <Col md={6}>
-              <Input type="text" id="last-name" valueLink={@props.lastname} label="Lastname"/>
+            {if @props.emailFilter.length > 0 then showEmailFilter() else }
+            <Col md={6}>
+              <Input type="text" id="first-name" valueLink={@props.firstname} label="First Name"/>
+            </Col>
+            <Col md={6}>
+              <Input type="text" id="last-name" valueLink={@props.lastname} label="Last Name"/>
             </Col>
           </Row>
           <Row>
@@ -196,7 +205,8 @@

         <Col md={6} mdOffset={3}>
           <LoginForm setPage={@setPage} status={@state.page} onRegistration={@onRegistration}
-            onLogin={@onLogin} onPasswordReset={@onPasswordReset} {...links}/>
+            onLogin={@onLogin} onPasswordReset={@onPasswordReset} emailFilter={@state.settings.email_filter}
+            {...links}/>
         </Col>


Fixed a bug where accessing a member of a variable would cause it to get declared, masking any possible global variables. Ex: process.argv would cause a var process; to be added, which would mask the actual process variable in the global space.

--- source/coffee/generator.coffee
+++ source/coffee/generator.coffee
@@ -134,7 +134,7 @@
     rv = ''
     if @base.type is 'IDENTIFIER'
       rv += KEYWORD_TRANSLATE[@base.value] or @base.value
-      scope[@base.value] = 'closures ok' unless scope[@base.value]? or not @is_lvalue() or KEYWORD_TRANSLATE[@base.value]
+      scope[@base.value] = 'closures ok' unless scope[@base.value]? or not @is_lvalue() or KEYWORD_TRANSLATE[@base.value] or @accessors.length > 0
     else
       rv += @base.js()


Fine tune CKEditor startup options

Fine tune CKEditor startup options

--- app/assets/javascripts/ckeditor/config.js.coffee
+++ app/assets/javascripts/ckeditor/config.js.coffee
@@ -1,8 +1,12 @@

 CKEDITOR.editorConfig = (config) ->
-  config.language = 'en'
+  config.height = '500'
   config.allowedContent = true
-  config.height = '500'
+
+  config.language = 'es'
+  config.scayt_sLang = 'es_ES'
+  config.scayt_autoStartup = true
+
   config.toolbar_Pure = [
     { name: 'document',    items: [ 'Source','-','Save','Preview','-','Templates' ] },
     { name: 'clipboard',   items: [ 'Undo','Redo' ] },
@@ -17,4 +21,6 @@
     { name: 'colors',      items: [ 'TextColor','BGColor' ] },
   ]
   config.toolbar = 'Pure'
+  config.toolbarCanCollapse = true
+  config.toolbarStartupExpanded = false
   true


Changes mastercard copy

Changes mastercard copy

--- app/assets/javascripts/lib/jquery.payment.coffee
+++ app/assets/javascripts/lib/jquery.payment.coffee
@@ -61,7 +61,7 @@
       luhn: true
   }
   {
-      type: 'mastercard'
+      type: 'master'
       pattern: /^5[0-5]/
       format: defaultFormat
       length: [16]


Add data-snippet_name when rendering a snippet

--- app/assets/javascripts/mercury/snippet.js.coffee
+++ app/assets/javascripts/mercury/snippet.js.coffee
@@ -77,6 +77,7 @@
       contenteditable: "false"
       'draggable': "true"
       'data-snippet': @identity
+      'data-snippet_name': @name
       'data-snippet_options': JSON.stringify(@options)
       'data-snippet_version': @version
     }, context)


Add category system type attribute

Add category system type attribute

--- app/assets/javascripts/models/category.js.coffee
+++ app/assets/javascripts/models/category.js.coffee
@@ -1,6 +1,7 @@
 App.Category = App.Model.extend
   id: Ember.attr()
   name: Ember.attr()
+  system_type: Ember.attr()
   count: Ember.attr(Number)
   total: Ember.attr(Number)
   transactions: Ember.hasMany 'App.Transaction',


Fix spree client require call

Fix spree client require call

--- app/assets/javascripts/sapphire_cms/spree.coffee
+++ app/assets/javascripts/sapphire_cms/spree.coffee
@@ -1,5 +1,5 @@
 #= require underscore
-#= require sapphire-cms/client
+#= require sapphire_cms/client
 #= require ./sapphire-cms.spree

 angular.module 'app', ['sp.spree']


gradebook: fix focus on keyboard shortcuts modal

When opening the keyboard shortcuts modal in Gradebook (new or old),
the focus is now on the close button.

closes GRADE-692

Test Plan:
1. Disable 'New Gradebook' in course settings.
2. Go to the Gradebook.
3. Tab to the keyboard shortcuts icon and select it.
4. Verify the keyboard shortcuts modal opens and focus is on the close
   button (the "x").
5. Enable 'New Gradebook' in course settings.
6. Go to the Gradebook.
7. Tab to the keyboard shortcuts icon and select it.
8. Verify the keyboard shortcuts modal opens and focus is on the close
   button (the "x").

Change-Id: Ia688e89c797303398e26b298b88b69684f491b38
Reviewed-on: https://gerrit.instructure.com/135422
Reviewed-by: Derek Bender <d24d8a7ee520e34b1cec38800225d9f36b552df7@instructure.com>
Reviewed-by: Shahbaz Javeed <6692d7be3fa215e5483a121d6fb314def6049877@instructure.com>
Tested-by: Jenkins
QA-Review: Indira Pai <9d2d5fa73bc5f5239dec42fbfd2a2b8495480e96@instructure.com>
Product-Review: Matt Goodwin <7c47e89894930304a2cdfa1d2ccca1e1eddb893d@instructure.com>

--- app/coffeescripts/views/KeyboardNavDialog.coffee
+++ app/coffeescripts/views/KeyboardNavDialog.coffee
@@ -57,9 +57,6 @@

               height: "auto"

-              open: ->
-                $(".navigation_list:first", @).focus()
-
               close: ->
                 $("li", @).attr("tabindex", "") # prevents chrome bsod
                 if(activeElement)


:green_heart: Adjust tests to reflect the new scrolling conditions

--- spec/table-element-spec.coffee
+++ spec/table-element-spec.coffee
@@ -1774,11 +1774,13 @@
         expect(tableElement.getRowsContainer().scrollTop).toBeLessThan(300)

     it 'scrolls the view when the selection reach the last column', ->
-      tableElement.setColumnsWidths([500, 500, 500])
+      tableEditor.addColumn('rab')
+      tableEditor.addColumn('bar')
+      tableElement.setColumnsWidths([500, 500, 500, 500, 500])
       nextAnimationFrame()

       startCell = tableShadowRoot.querySelector('atom-table-cell[data-row="6"][data-column="0"]')
-      endCell = tableShadowRoot.querySelector('atom-table-cell[data-row="6"][data-column="1"]')
+      endCell = tableShadowRoot.querySelector('atom-table-cell[data-row="6"][data-column="2"]')

       mousedown(startCell)
       mousemove(endCell)


💚 Fix one more failing assertion on AppVeyor

Similar to the approach used in
1fdffe1924482562039905bce6dc4c5663f7f66e, this commit aims to resolve
the failure seen in
https://ci.appveyor.com/project/Atom/settings-view/build/498/job/m3pgr6df0xt9youy.

--- spec/updates-panel-spec.coffee
+++ spec/updates-panel-spec.coffee
@@ -91,13 +91,12 @@

         resolveC()

-      waits 0
-      runs ->
-        notifications = atom.notifications.getNotifications()
-        expect(notifications.length).toBe 1
-
+      waitsFor ->
+        atom.notifications.getNotifications().length is 1
+
+      runs ->
         spyOn(atom, 'restartApplication')
-        notifications[0].options.buttons[0].onDidClick()
+        atom.notifications.getNotifications()[0].options.buttons[0].onDidClick()
         expect(atom.restartApplication).toHaveBeenCalled()

     it 'works with queue enabled', ->


test(mixins): add tests for EventDispatcher mixin

--- specs/units/mixins.spec.coffee
+++ specs/units/mixins.spec.coffee
@@ -47,3 +47,29 @@
     singular: 'child'
     plural: 'children'
   }
+
+describe spectacular.EventDispatcher, ->
+  given 'listener', ->
+    dummy = {foo: ->}
+    spyOn(dummy, 'foo')
+    dummy.foo
+
+  subject 'dispatcher', -> new spectacular.EventDispatcher
+
+  context 'when adding a listener', ->
+    before -> @dispatcher.on 'event', @listener
+
+    specify 'calling hasListener("event")', ->
+      @dispatcher.hasListener('event').should be true
+
+    context 'and then removing it', ->
+      before -> @dispatcher.off 'event', @listener
+
+      specify 'calling hasListener("event")', ->
+        @dispatcher.hasListener('event').should be false
+
+    context 'and then dispatching a message', ->
+      before -> @dispatcher.dispatch name: 'event', message: 'message'
+      subject -> @listener
+
+      specify 'the listener', -> should haveBeenCalled


naming some sektors in add boulder form

naming some sektors in add boulder form

--- views/addboulder.coffee
+++ views/addboulder.coffee
@@ -60,12 +60,12 @@
         sectors[0]  = "11 Block"
         sectors[1]  = "10"
         sectors[2]  = "9"
-        sectors[3]  = "8"
+        sectors[3]  = "8 Platte"
         sectors[4]  = "7"
-        sectors[5]  = "6"
+        sectors[5]  = "6 Bug"
         sectors[6]  = "5"
         sectors[7]  = "4"
-        sectors[8]  = "3"
+        sectors[8]  = "3 Dach"
         sectors[9]  = "2"
         sectors[10] = "1"
         return sectors


search - implement mobile-specific excerpt

--- _assets/javascripts/sections/search.coffee
+++ _assets/javascripts/sections/search.coffee
@@ -59,7 +59,8 @@
       $result.removeClass('example')
       $result.find('h2 a').text(post.title)
       $result.find('a').attr('title',post.title).attr('href',post.url)
-      $result.find('.excerpt').text(post.excerpt)
+      $result.find('.desktop_excerpt .excerpt').text(post.excerpt)
+      $result.find('.mobile_excerpt .excerpt').text(post.excerpt_mobile)
       $result.show()
       # console.log $result


Registration: handle redirects wisely

Registration: handle redirects wisely

--- client/Main/maincontroller/maincontroller.coffee
+++ client/Main/maincontroller/maincontroller.coffee
@@ -140,12 +140,12 @@
         @utils.defer ->
           firstRoute = KD.getSingleton("router").visitedRoutes.first

-         if firstRoute and /^\/Verify/.test firstRoute
-           firstRoute = "/"
-         if firstRoute and /^\/Reset/.test firstRoute
-           firstRoute = "/"
-
-          window.location.pathname = firstRoute or "/"
+          firstRoute = KD.getSingleton("router").visitedRoutes.first
+          if firstRoute and /^\/(?:Reset|Register|Verify|Confirm)\//.test firstRoute
+            firstRoute = "/Activity"
+
+          {entryPoint} = KD.config
+          KD.getSingleton('router').handleRoute firstRoute or '/Activity', {replaceState: yes, entryPoint}

   setVisitor:(visitor)-> @visitor = visitor
   getVisitor: -> @visitor


If groupEntryPoint is koding just open the page requested

--- client/app/MainApp/lazy/lazydomcontroller.coffee
+++ client/app/MainApp/lazy/lazydomcontroller.coffee
@@ -14,7 +14,11 @@
       else if @userEnteredFromProfile()
         @addProfileViews()

-      landingPageSideBar = new LandingPageSideBar
+      if KD.isLoggedIn() and KD.config.groupEntryPoint is 'koding'
+        $('#group-landing').css 'opacity', 0
+        @utils.wait 600, -> $('#group-landing').hide()
+      else
+        landingPageSideBar = new LandingPageSideBar

   userEnteredFromGroup:-> KD.config.groupEntryPoint?


added historical dom cache

added historical dom cache

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -1,4 +1,4 @@
-historyCache = []
+window.historyCache = []
 visit = (url) ->
   if browserSupportsPushState?
     reflectNewUrl url
@@ -16,8 +16,8 @@
   xhr.send()

 fetchHistory = (url) ->
-  while(historyCache.length > 0)
-    cache = historyCache.pop()
+  while(window.historyCache.length > 0)
+    cache = window.historyCache.pop()
     if cache.url == url
       replaceHTML cache.html
       triggerPageChange
@@ -62,7 +62,7 @@
   document.documentElement.appendChild doc.body, originalBody
   document.documentElement.removeChild originalBody
   document.title = title.textContent if title = doc.querySelector "title"
-  historyCache.push({url: document.location.href, html:html}) if cache
+  window.historyCache.push({url: document.location.href, html:html}) if cache

 extractLink = (event) ->


Reorder variable declarations by relation and proper order

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -1,10 +1,12 @@
+pageCache      = {}
 cacheSize      = 10
 currentState   = null
+loadedAssets   = null
+
 referer        = document.location.href
-loadedAssets   = null
-pageCache      = {}
+requestMethod  = document.cookie.match(/request_method=(\w+)/)?[1].toUpperCase() or ''
+
 createDocument = null
-requestMethod  = document.cookie.match(/request_method=(\w+)/)?[1].toUpperCase() or ''
 xhr            = null


Use correct file name for s3

Use correct file name for s3

Signed-off-by: Yuki Nishijima <a6aa6c594f3f904825833313c0bab3e292e95764@artsymail.com>

--- desktop/apps/gallery_partnerships2/routes.coffee
+++ desktop/apps/gallery_partnerships2/routes.coffee
@@ -3,7 +3,7 @@
 resizer = require '../../components/resizer'
 markdown = require '../../components/util/markdown'

-page = new JSONPage name: 'gallery-partnerships'
+page = new JSONPage name: 'gallery-partnerships2'

 @index = (req, res, next) ->
   page.get()


#15562: Added TestAdapter

#15562: Added TestAdapter

--- app/assets/javascripts/ga_events.js.coffee
+++ app/assets/javascripts/ga_events.js.coffee
@@ -77,3 +77,8 @@

 class GaEvents.NullAdapter
   push: (obj) -> console.log obj if console?
+
+class GaEvents.TestAdapter
+  push: (obj) ->
+    window.events = [] unless window.events?
+    window.events.push obj


autozoom layer when activated instead of initialize

--- app/assets/javascripts/map_layer.js.coffee
+++ app/assets/javascripts/map_layer.js.coffee
@@ -24,19 +24,21 @@

   option_auto_zoom: =>
     @layer.on 'ready', =>
-      if @config.options? and @config.options.auto_zoom == 'yes'
+
+  addTo: (map, zIndex = 100) =>
+    map.addLayer(@layer)
+    @layer.setZIndex(zIndex)
+
+    if @config.options?
+      if @config.options.auto_zoom == 'yes'
         @map.whenReady =>
           setTimeout =>
             bounds = @layer.getBounds()
             @map.fitBounds(bounds) if bounds.isValid()
           , 100

-  addTo: (map, zIndex = 100) =>
-    map.addLayer(@layer)
-    @layer.setZIndex(zIndex)
-
-    if @config.options? and @config.options.baselayer != 'yes'
-      @layer.bringToFront()
+      if @config.options.baselayer != 'yes'
+        @layer.bringToFront()

   removeFrom: (map, control) =>
     map.removeLayer(@layer)


#233 Refactor

#233 Refactor

--- app/assets/javascripts/oxalis/model.coffee
+++ app/assets/javascripts/oxalis/model.coffee
@@ -81,95 +81,97 @@
           url : "/user/configuration"
           dataType : "json"
         ).pipe(
-          (user) =>
-
-            $.assertExtendContext({
-              task: tracingId
-              dataSet: tracing.content.dataSet.name
-            })
-
-            console.log "tracing", tracing
-            console.log "user", user
-
-            dataSet = tracing.content.dataSet
-            @user = new User(user)
-            @scaleInfo = new ScaleInfo(dataSet.scale)
-
-            if (bb = tracing.content.boundingBox)?
-                @boundingBox = {
-                  min : bb.topLeft
-                  max : [
-                    bb.topLeft[0] + bb.width
-                    bb.topLeft[1] + bb.height
-                    bb.topLeft[2] + bb.depth
-                  ]
-                }
-
-            layerOptions = {
-              color : { allowManipulation : true },
-              volume : { allowManipulation : false },
-              segmentation : { allowManipulation : false }
-            }
-
-            @dataSetName = dataSet.name
-            zoomStepCount = Infinity
-            @binary = {}
-            @lowerBoundary = [ Infinity,  Infinity,  Infinity]
-            @upperBoundary = [-Infinity, -Infinity, -Infinity]
-
-            for layer in dataSet.dataLayers
-
-              _.extend layer, layerOptions[layer.name]
-              layer.bitDepth = parseInt( layer.elementClass.substring(4) )
-              @binary[layer.name] = new Binary(this, tracing, layer, tracingId)
-              zoomStepCount = Math.min(zoomStepCount, @binary[layer.name].cube.ZOOM_STEP_COUNT - 1)
-
-              for i in [0..2]
-                @lowerBoundary[i] = Math.min @lowerBoundary[i], @binary[layer.name].lowerBoundary[i]
-                @upperBoundary[i] = Math.max @upperBoundary[i], @binary[layer.name].upperBoundary[i]
-
-            if @getColorBinaries().length == 0
-              Toast.error("No data available! Something seems to be wrong with the dataset.")
-            @setDefaultBinaryColors()
-
-            # if "volume" layer still used, change name to segmentation
-            if @binary["volume"]?
-              @binary["segmentation"] = @binary["volume"]
-              delete @binary["volume"]
-
-            @flycam = new Flycam2d(constants.PLANE_WIDTH, @scaleInfo, zoomStepCount, @user)
-            @flycam3d = new Flycam3d(constants.DISTANCE_3D, dataSet.scale)
-
-            @flycam3d.on
-              "changed" : (matrix, zoomStep) =>
-                @flycam.setPosition( matrix[12..14] )
-
-            @flycam.on
-              "positionChanged" : (position) =>
-                @flycam3d.setPositionSilent(position)
-
-            # init state
-            @flycam.setPosition( state.position || tracing.content.editPosition )
-            if state.zoomStep?
-              @flycam.setZoomStep( state.zoomStep )
-              @flycam3d.setZoomStep( state.zoomStep )
-            if state.rotation?
-              @flycam3d.setRotation( state.rotation )
-
-            if controlMode == constants.CONTROL_MODE_TRACE
-
-              if "volume" in tracing.content.settings.allowedModes
-                $.assert( @binary["segmentation"]?,
-                  "Volume is allowed, but segmentation does not exist" )
-                @volumeTracing = new VolumeTracing(tracing, @flycam, @binary["segmentation"].cube)
-
-              else
-                @skeletonTracing = new SkeletonTracing(tracing, @scaleInfo, @flycam, @flycam3d, @user)
-
-            {"restrictions": tracing.restrictions, "settings": tracing.content.settings}
-
+          (user) => @initializeWithData(controlMode, state, tracingId, tracingType, tracing, user)
           -> Toast.error("Ooops. We couldn't communicate with our mother ship. Please try to reload this page.")
         )
+
+  initializeWithData : (controlMode, state, tracingId, tracingType, tracing, user) ->
+
+    $.assertExtendContext({
+      task: tracingId
+      dataSet: tracing.content.dataSet.name
+    })
+
+    console.log "tracing", tracing
+    console.log "user", user
+
+    dataSet = tracing.content.dataSet
+    @user = new User(user)
+    @scaleInfo = new ScaleInfo(dataSet.scale)
+
+    if (bb = tracing.content.boundingBox)?
+        @boundingBox = {
+          min : bb.topLeft
+          max : [
+            bb.topLeft[0] + bb.width
+            bb.topLeft[1] + bb.height
+            bb.topLeft[2] + bb.depth
+          ]
+        }
+
+    layerOptions = {
+      color : { allowManipulation : true },
+      volume : { allowManipulation : false },
+      segmentation : { allowManipulation : false }
+    }
+
+    @dataSetName = dataSet.name
+    zoomStepCount = Infinity
+    @binary = {}
+    @lowerBoundary = [ Infinity,  Infinity,  Infinity]
+    @upperBoundary = [-Infinity, -Infinity, -Infinity]
+
+    for layer in dataSet.dataLayers
+
+      _.extend layer, layerOptions[layer.name]
+      layer.bitDepth = parseInt( layer.elementClass.substring(4) )
+      @binary[layer.name] = new Binary(this, tracing, layer, tracingId)
+      zoomStepCount = Math.min(zoomStepCount, @binary[layer.name].cube.ZOOM_STEP_COUNT - 1)
+
+      for i in [0..2]
+        @lowerBoundary[i] = Math.min @lowerBoundary[i], @binary[layer.name].lowerBoundary[i]
+        @upperBoundary[i] = Math.max @upperBoundary[i], @binary[layer.name].upperBoundary[i]
+
+    if @getColorBinaries().length == 0
+      Toast.error("No data available! Something seems to be wrong with the dataset.")
+    @setDefaultBinaryColors()
+
+    # if "volume" layer still used, change name to segmentation
+    if @binary["volume"]?
+      @binary["segmentation"] = @binary["volume"]
+      delete @binary["volume"]
+
+    @flycam = new Flycam2d(constants.PLANE_WIDTH, @scaleInfo, zoomStepCount, @user)
+    @flycam3d = new Flycam3d(constants.DISTANCE_3D, dataSet.scale)
+
+    @flycam3d.on
+      "changed" : (matrix, zoomStep) =>
+        @flycam.setPosition( matrix[12..14] )
+
+    @flycam.on
+      "positionChanged" : (position) =>
+        @flycam3d.setPositionSilent(position)
+
+    # init state
+    @flycam.setPosition( state.position || tracing.content.editPosition )
+    if state.zoomStep?
+      @flycam.setZoomStep( state.zoomStep )
+      @flycam3d.setZoomStep( state.zoomStep )
+    if state.rotation?
+      @flycam3d.setRotation( state.rotation )
+
+    if controlMode == constants.CONTROL_MODE_TRACE
+
+      if "volume" in tracing.content.settings.allowedModes
+        $.assert( @binary["segmentation"]?,
+          "Volume is allowed, but segmentation does not exist" )
+        @volumeTracing = new VolumeTracing(tracing, @flycam, @binary["segmentation"].cube)
+
+      else
+        @skeletonTracing = new SkeletonTracing(tracing, @scaleInfo, @flycam, @flycam3d, @user)
+
+    {"restrictions": tracing.restrictions, "settings": tracing.content.settings}
+

   getColorBinaries : ->


fixed errors on ecommerce tracking events

--- app/coffee/modules/common/analytics.coffee
+++ app/coffee/modules/common/analytics.coffee
@@ -129,6 +129,6 @@
         @win.ga('ec:setAction', action, {
             'list': page
         })
-        @.trackEvent("ecommerce", "set-action", action, page)
+        @.trackEvent("ecommerce", "set-action", (action+page), 0)

 module.service("$tgAnalytics", AnalyticsService)


update translations

update translations

--- src/coffee/translations.coffee
+++ src/coffee/translations.coffee
@@ -45,74 +45,99 @@

   _translations:
     de:
-      button: 'Abonnieren'
+      button: "Abonnieren"
       panels:
-        title: 'Abonnieren'
+        title: "Abonnieren"
       podcast_panel:
-        choose_client: 'Client auswählen'
+        choose_client: "Client auswählen"
       help_panel:
-        title: 'Abonnieren?'
-        paragraph1: 'Du bist dabei einen Podcast zu abonnieren. Das erlaubt deinem Podcast Client neue Folgen automatisch herunterzuladen oder das Podcast Archiv mit alten Episoden zu durchsuchen.'
-        paragraph2: 'Der Podlove Subscribe Button hilft dir dabei. Wähle deinen favorisierten Podcast Client aus der Liste der verfügbaren Apps oder wähle einen Podcast Cloud Service den du benutzt.'
-        paragraph3: 'Beim Start sollte der Podcast Client den Podcast zur Liste der Abonnements hinzufügen. Benutze den Download Link um die App zu installieren, falls sie es noch nicht ist.'
+        title: "Abonnieren?"
+        paragraph1: "Du bist dabei einen Podcast zu abonnieren. Das erlaubt deinem Podcast Client neue Folgen automatisch herunterzuladen oder das Podcast Archiv mit alten Episoden zu durchsuchen."
+        paragraph2: "Der Podlove Subscribe Button hilft dir dabei. Wähle deinen favorisierten Podcast Client aus der Liste der verfügbaren Apps oder wähle einen Podcast Cloud Service den du benutzt."
+        paragraph3: "Beim Start sollte der Podcast Client den Podcast zur Liste der Abonnements hinzufügen. Benutze den Download Link um die App zu installieren, falls sie es noch nicht ist."
       clients_panel:
-        app: 'App'
-        cloud: 'Cloud'
-        other_client: 'Anderer Client'
+        app: "App"
+        cloud: "Cloud"
+        other_client: "Anderer Client"
       finish_panel:
-        handing_over_to: 'Übergebe an
 %{client}'
-        something_went_wrong: 'Funktioniert etwas nicht wie erwartet?'
-        try_again: 'Nochmal versuchen'
-        install: '%{client} Webseite besuchen'
-        register_an_account: 'Einen Account registrieren bei '
-        please_copy_url: 'Bitte die URL kopieren und in deinen Podcast- oder RSS-Client einfügen.'
-        or_install: 'oder Client installieren'
+        handing_over_to: "Übergebe an
 %{client}"
+        something_went_wrong: "Funktioniert etwas nicht wie erwartet?"
+        try_again: "Nochmal versuchen"
+        install: "%{client} Webseite besuchen"
+        register_an_account: "Einen Account registrieren bei "
+        please_copy_url: "Bitte die URL kopieren und in deinen Podcast- oder RSS-Client einfügen."
+        or_install: "oder Client installieren"

     en:
-      button: 'Subscribe'
+      button: "Subscribe"
       panels:
-        title: 'Subscribe'
+        title: "Subscribe"
       podcast_panel:
-        choose_client: 'Choose client'
+        choose_client: "Choose App"
       help_panel:
-        title: 'Subscribe?'
-        paragraph1: 'You are about to subscribe to a podcast. This will allow your podcast client program to  automatically download new episodes or access the archive of previously released stuff.'
-        paragraph2: 'The Podlove Subscription Tool helps you to do this. Select your favorite podcast client from a list of potential apps on your device or pick a podcast cloud service on the web that you use.'
-        paragraph3: 'Upon launch, the podcast client should offer you to add the podcast to your list of subscriptions. Use the download link to get the app if not yet available.'
+        title: "Subscribe?"
+        paragraph1: "You are about to subscribe to a podcast. This will allow your podcast app to automatically download new episodes or access the archive of previously released episodes."
+        paragraph2: "The Podlove Subscribe Button helps you to do this. Select your favorite podcast app from a list of potential apps on your device or pick a podcast cloud service on the web that you use."
+        paragraph3: "Upon launch, the podcast client should offer you to add the podcast to your list of subscriptions. Use the download link to get the app if not yet available."
       clients_panel:
-        app: 'App'
-        cloud: 'Cloud'
-        other_client: 'Other Client'
+        app: "App"
+        cloud: "Cloud"
+        other_client: "Other App"
       finish_panel:
-        handing_over_to: 'Handing over to %{client}'
-        something_went_wrong: 'Did something go wrong?'
-        try_again: 'Try again'
+        handing_over_to: "Handing over to %{client}"
+        something_went_wrong: "Did something go wrong?"
+        try_again: "Try again"
         install: "Visit %{client} website"
-        register_an_account: 'Register an account with '
-        please_copy_url: 'Please copy the URL below and add it to your Podcast- or RSS-Client.'
-        or_install: 'or install client'
+        register_an_account: "Register an account with "
+        please_copy_url: "Please copy the URL below and add it to your podcast or RSS app."
+        or_install: "or install app"
+
+    fi:
+      button: "Tilaa"
+      panels:
+        title: "Tilaa"
+      podcast_panel:
+        choose_client: "Valitse ohjelma"
+      help_panel:
+        title: "Haluatko tilata?"
+        paragraph1: "You are about to subscribe to a podcast. This will allow your podcast app to automatically download new episodes or access the archive of previously released episodes."
+        paragraph2: "The Podlove Subscribe Button helps you to do this. Select your favorite podcast app from a list of potential apps on your device or pick a podcast cloud service on the web that you use."
+        paragraph3: "Upon launch, the podcast client should offer you to add the podcast to your list of subscriptions. Use the download link to get the app if not yet available."
+      clients_panel:
+        app: "App"
+        cloud: "Cloud"
+        other_client: "Eri ohjelma"
+      finish_panel:
+        handing_over_to: "Annetaan %{client}:lle"
+        something_went_wrong: "Menikö jotain väärin?"
+        try_again: "Kokeile uudestaan"
+        install: "Mene %{client}:n sivustolle"
+        register_an_account: "Rekisteröidy"
+        please_copy_url: "Ole hyvä ja kopioi alla olevan linkin ja syötä se sinuun podcast tai RSS ohjelmaan."
+        or_install: "tai installoi ohjelma"

     ja:
-      button: '登録する'
+      button: "登録する"
       panels:
-        title: '登録する'
+        title: "登録する"
       podcast_panel:
-        choose_client: 'クライアントを選ぶ'
+        choose_client: "クライアントを選ぶ"
       help_panel:
-        title: '登録がよろしいですか？'
-        paragraph1: '今新しいポットキャストを登録しています。それでポットキャストクライアントアプリケーションで新しいエピソードを自動でダウンロードできる、またはポットキャストアーカイブで過去のエピソードを探せます。'
-        paragraph2: 'ポットラブ登録ボタンは登録を支援します。気に入り、使っているポットキャストクライアントがポットキャストクラウドサービスを使用可能なもののリストを選んで下さい。'
-        paragraph3: 'スタートアップでポットキャストクライアントがポットキャストを登録はずです。アプリがまだインストールしなかったら、ダウンロードリンクをインストールために使って下さい。'
+        title: "登録がよろしいですか？"
+        paragraph1: "今新しいポットキャストを登録しています。それでポットキャストクライアントアプリケーションで新しいエピソードを自動でダウンロードできる、またはポットキャストアーカイブで過去のエピソードを探せます。"
+        paragraph2: "ポットラブ登録ボタンは登録を支援します。気に入り、使っているポットキャストクライアントがポットキャストクラウドサービスを使用可能なもののリストを選んで下さい。"
+        paragraph3: "スタートアップでポットキャストクライアントがポットキャストを登録はずです。アプリがまだインストールしなかったら、ダウンロードリンクをインストールために使って下さい。"
       clients_panel:
-        app: 'アプリ'
-        cloud: 'クラウド'
-        other_client: '他のクライアント'
+        app: "アプリ"
+        cloud: "クラウド"
+        other_client: "他のクライアント"
       finish_panel:
-        handing_over_to: '%{client}に渡す'
-        something_went_wrong: '何が失敗しましたか？'
-        try_again: 'もう一度試してください'
-        install: 'App Storeからインストールする'
-        register_an_account: '%{client}にアカウントを登録する'
-        please_copy_url: 'URLをコピーして、ポットキャストがRSSクライアントに貼り付けて下さい。'
+        handing_over_to: "%{client}に渡す"
+        something_went_wrong: "何が失敗しましたか？"
+        try_again: "もう一度試してください"
+        install: "Visit %{client} website"
+        register_an_account: "%{client}にアカウントを登録する"
+        please_copy_url: "URLをコピーして、ポットキャストがRSSクライアントに貼り付けて下さい。"
+        or_install: "or install app"

 module.exports = Translations


fat arrow isn't needed

fat arrow isn't needed

--- test-integration/helpers/window-position.coffee
+++ test-integration/helpers/window-position.coffee
@@ -5,7 +5,7 @@
     test.driver.executeScript("window.scrollTo(0,0);")
     test.sleep(200)

-  scrollTo: (el) =>
+  scrollTo: (el) ->
     test.driver.executeScript("arguments[0].scrollIntoView(true);", el)
     test.sleep(200)


more efficient histocache storage

more efficient histocache storage

Former-commit-id: 2c5efbb2117a3aa3de0efa1004f566e9d50d7771 [formerly b594ea7b80af7e0d738733ce395bdf1df85e9a98] [formerly 47b7f5e1da7f8369a2323c0ff076fbda5250627d [formerly eabe84d4ff8e3c3d7ce0a0bedb25d6f5aedca25d]] [formerly 093e5eb4f86e467d17e052191f388b8059428832 [formerly e80d109d3a88f3fac2646d48ccd8773c29a7e143] [formerly b41c28b36729bb8b9b11b60849d801dca45f27bd [formerly 7de129c33b5415ad051a357626ed10455927a396]]]
Former-commit-id: b9c9c92a09c63f31bbc9ff5d93d140224f06ce01 [formerly 4c764b9f273ef9b5ebd01e0d03fbecf5a35a7bb0] [formerly e31ed4192cadb9dfec89df425a84319601e2243c [formerly fe37d06a8ffff4b2b547017c44b26387539bad6a]]
Former-commit-id: f3c88dd52cda6977c52f2123b12b4cb1690a4c1c [formerly f72ccf6d178a56f1fba71f918ac490f5206e3fa9]
Former-commit-id: 9fca8398e8d8fb79e926cf6ccf36a670bca8b29b
Former-commit-id: 4eebb2e98b6e66f3c54e113ebd8624f03a003b64

--- @client/histogram.coffee
+++ @client/histogram.coffee
@@ -815,7 +815,7 @@
     positions = {}
     for o, i in nodes
       positions[parseInt(opinions[i].user.split('/user/')[1])] = \
-        [o.x - o.radius, o.y - o.radius]
+        [Math.round((o.x - o.radius) * 10) / 10, Math.round((o.y - o.radius) * 10) / 10]

     opts.done?(positions)


Added style name specification to PageAnimation

--- src/core/page_animation.coffee
+++ src/core/page_animation.coffee
@@ -3,8 +3,9 @@
   #
   #
   #
-  constructor: (@prevPage, @nextPage) ->
-    @pageAnimeCss = new Scena.Css('page_anime')
+  constructor: (@prevPage, @nextPage, styleName) ->
+    styleName = 'PageAnimation' if styleName is undefined
+    @pageAnimeCss = new Scena.Css(styleName)

   #
   #


fixed null handling

fixed null handling

--- src/driver/simpleDriver.coffee
+++ src/driver/simpleDriver.coffee
@@ -187,7 +187,7 @@
           buckets = {}
           for d in segment._raws[dataset]
             key = parallelSplitFn(d)
-            throw new Error("bucket returned undefined") unless key? # ToDo: handle nulls
+            #throw new Error("bucket returned undefined") unless key? # ToDo: handle nulls
             keyString = String(key)

             if not bucketValue.hasOwnProperty(keyString)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/function_extensions.coffee
+++ src/function_extensions.coffee
@@ -6,7 +6,7 @@
     return memo if ran
     ran = true

-
+    return mom

   _.once = function(func) {
     var ran = false, memo;


Fix typo from merge conflict resolution

Fix typo from merge conflict resolution

--- bokehjs/src/coffee/models/axes/axis.coffee
+++ bokehjs/src/coffee/models/axes/axis.coffee
@@ -126,7 +126,7 @@
   below  : RIGHT
   left   : RIGHT
   right  : LEFT
-
+}

 _apply_location_heuristics = (ctx, side, orient) ->
   if _.isString(orient)


avoid double newline addition

avoid double newline addition

--- src/modules/attribution.coffee
+++ src/modules/attribution.coffee
@@ -9,8 +9,10 @@
         _.each(delta.ops, (op) =>
           op.attributes['author'] = @authorId if Tandem.InsertOp.isInsert(op)
         )
-        delta.apply((index, text) =>
-          @editor.formatAt(index, text.length, 'author', @authorId, false)
+        _.defer( =>
+          delta.apply((index, text) =>
+            @editor.formatAt(index, text.length, 'author', @authorId, false)
+          )
         )
     )
     this.addAuthor(@authorId, color)


Added HUBOT_JIRA_IGNORE_USERS parameter

Added HUBOT_JIRA_IGNORE_USERS parameter

--- src/scripts/jira-issues.coffee
+++ src/scripts/jira-issues.coffee
@@ -1,5 +1,7 @@
 # Description:
 #   Looks up jira issues when they're mentioned in chat
+#
+#   Will ignore users set in HUBOT_JIRA_IGNORE_USERS (by default, JIRA and GitHub).
 #
 # Dependencies:
 #   None
@@ -9,6 +11,7 @@
 #   HUBOT_JIRA_IGNORECASE (optional; default is "true")
 #   HUBOT_JIRA_USERNAME (optional)
 #   HUBOT_JIRA_PASSWORD (optional)
+#   HUBOT_JIRA_IGNORE_USERS (optional, format: "user1|user2", default is "jira|github")
 #
 # Commands:
 #
@@ -26,6 +29,10 @@
   if jiraUsername != undefined && jiraUsername.length > 0
     auth = "#{jiraUsername}:#{jiraPassword}"

+  jiraIgnoreUsers = process.env.HUBOT_JIRA_ISSUES_IGNORE_USERS
+  if jiraIgnoreUsers == undefined
+    jiraIgnoreUsers = "jira|github"
+
   http.get {host: jiraDomain, auth: auth, path: "/rest/api/2/project"}, (res) ->
     data = ''
     res.on 'data', (chunk) ->
@@ -40,39 +47,40 @@
         jiraPattern += "i"

       robot.hear eval(jiraPattern), (msg) ->
-        for i in msg.match
-          issue = i.toUpperCase()
-          now = new Date().getTime()
-          if cache.length > 0
-            cache.shift() until cache.length is 0 or cache[0].expires >= now
-          if cache.length == 0 or (item for item in cache when item.issue is issue).length == 0
-            cache.push({issue: issue, expires: now + 120000})
-            msg.http(jiraUrl + "/rest/api/2/issue/" + issue)
-              .auth(auth)
-              .get() (err, res, body) ->
-                try
-                  json = JSON.parse(body)
-                  key = json.key
+        if not msg.message.user.name.match(new RegExp(jiraIgnoreUsers, "gi"))
+          for i in msg.match
+            issue = i.toUpperCase()
+            now = new Date().getTime()
+            if cache.length > 0
+              cache.shift() until cache.length is 0 or cache[0].expires >= now
+            if cache.length == 0 or (item for item in cache when item.issue is issue).length == 0
+              cache.push({issue: issue, expires: now + 120000})
+              msg.http(jiraUrl + "/rest/api/2/issue/" + issue)
+                .auth(auth)
+                .get() (err, res, body) ->
+                  try
+                    json = JSON.parse(body)
+                    key = json.key

-                  message = "[" + key + "] " + json.fields.summary
-                  message += '\nStatus: '+json.fields.status.name
-                  if (json.fields.assignee and json.fields.assignee.displayName)
-                    message += ', assigned to ' + json.fields.assignee.displayName
-                  else
-                    message += ', unassigned'
-                  message += ", rep. by "+json.fields.reporter.displayName
-                  if json.fields.fixVersions and json.fields.fixVersions.length > 0
-                    message += ', fixVersion: '+json.fields.fixVersions[0].name
-                  else
-                    message += ', fixVersion: NONE'
+                    message = "[" + key + "] " + json.fields.summary
+                    message += '\nStatus: '+json.fields.status.name
+                    if (json.fields.assignee and json.fields.assignee.displayName)
+                      message += ', assigned to ' + json.fields.assignee.displayName
+                    else
+                      message += ', unassigned'
+                    message += ", rep. by "+json.fields.reporter.displayName
+                    if json.fields.fixVersions and json.fields.fixVersions.length > 0
+                      message += ', fixVersion: '+json.fields.fixVersions[0].name
+                    else
+                      message += ', fixVersion: NONE'

-                  msg.send message
+                    msg.send message

-                  urlRegex = new RegExp(jiraUrl + "[^\\s]*" + key)
-                  if not msg.message.text.match(urlRegex)
-                    msg.send jiraUrl + "/browse/" + key
-                catch error
-                  try
-                    msg.send "[*ERROR*] " + json.errorMessages[0]
-                  catch reallyError
-                    msg.send "[*ERROR*] " + reallyError
+                    urlRegex = new RegExp(jiraUrl + "[^\\s]*" + key)
+                    if not msg.message.text.match(urlRegex)
+                      msg.send jiraUrl + "/browse/" + key
+                  catch error
+                    try
+                      msg.send "[*ERROR*] " + json.errorMessages[0]
+                    catch reallyError
+                      msg.send "[*ERROR*] " + reallyError


better English

better English

--- src/scripts/redis-brain.coffee
+++ src/scripts/redis-brain.coffee
@@ -35,7 +35,7 @@
         robot.logger.info "Data for #{prefix} brain retrieved from Redis"
         robot.brain.mergeData JSON.parse(reply.toString())
       else
-        robot.logger.info "Initializing new #{prefix} data for brain"
+        robot.logger.info "Initializing new data for #{prefix} brain"
         robot.brain.mergeData {}

       robot.brain.setAutoSave true


util/sync: taking care of @get_doc returning undefined -- issue #1855

--- src/smc-util/syncstring.coffee
+++ src/smc-util/syncstring.coffee
@@ -1519,7 +1519,7 @@
                         if err
                             cb(err)
                         else if not exists
-                            dbg("write '#{path}' to disk from syncstring in-memory database version -- '#{@get_doc().slice(0,80)}...'")
+                            dbg("write '#{path}' to disk from syncstring in-memory database version -- '#{@get_doc()?.slice(0,80)}...'")
                             @_client.write_file
                                 path : path
                                 data : @to_str()


fix typing info getting stuck

fix typing info getting stuck

--- src/ui/views/typinginfo.coffee
+++ src/ui/views/typinginfo.coffee
@@ -8,7 +8,8 @@
     c = conv[conv_id]
     return unless c

-    if c.typing?.length
+    typing = (t for t in (c.typing or []) when t.status != 'STOPPED')
+    if typing.length
         div class:'typing', ->
             for t, i in c.typing
                 name = nameof entity[t.user_id.chat_id]


Update validation methods for correct time-out style erring behavior.

--- src/validator/MaxLength.coffee
+++ src/validator/MaxLength.coffee
@@ -26,9 +26,21 @@
     @messages.error = 'The maximum length of :max characters has been reached.'

+  ###*
+  # The maximum number of characters allowed for this validator.
+  #
+  # @member {number} max
+  # @memberof stout-ui/validator/MaxLength
+  ###
   @property 'max'

+  ###*
+  # The length when length-warnings are issues.
+  #
+  # @member {number} max
+  # @memberof stout-ui/validator/MaxLength
+  ###
   @property 'warn'

@@ -49,14 +61,13 @@
   # @memberof stout-ui/validator/MaxLength#
   ###
   softValidate: (v) ->
-    if v.length >= @max
+    if @_errorTimeout then clearTimeout @_errorTimeout
+    if v.length > @max
       @_error()
-      if @_errorTimeout then clearTimeout @_errorTimeout
       @_errorTimeout = setTimeout =>
-        console.log 'okaying...', @
         @_ok()
       , @removeTime
-    else if v.length >= @warn
+    else if v.length > @warn
       @_warning()
     else
       @_ok()
@@ -72,3 +83,5 @@
   # @memberof stout-ui/validator/MaxLength#
   ###
   validate: ->
+    if @_errorTimeout then clearTimeout @_errorTimeout
+    @_ok()


[Email Confirmation] Add a helper method for looking up email confirmation status

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -93,7 +93,7 @@
         'fetchRelatedUsersFromGraph', 'fetchDomains', 'fetchDomains',
         'unlinkOauth', 'changeUsername',
         'markUserAsExempt', 'checkFlag', 'userIsExempt', 'checkGroupMembership',
-        'getOdeskAuthorizeUrl', 'fetchStorage', 'fetchStorages', 'store', 'unstore'
+        'getOdeskAuthorizeUrl', 'fetchStorage', 'fetchStorages', 'store', 'unstore', 'isEmailVerified'
       ]
     schema                  :
       skillTags             : [String]
@@ -619,6 +619,11 @@
           return callback err, null
         callback null, (i._id for i in data)

+  isEmailVerified: (callback)->
+    @fetchUser (err, user)->
+      return callback err if err
+      callback null, (user.status is "confirmed")
+
   markUserAsExempt: secure (client, exempt, callback)->
     {delegate} = client.connection
     if delegate.can 'flag', this


payment: use JEarnedReward to fetch referral space

--- workers/social/lib/social/models/payment.coffee
+++ workers/social/lib/social/models/payment.coffee
@@ -195,9 +195,10 @@
         return callback err  if err

         plan.storage += space
-
         callback null, plan

   fetchReferrerSpace = (client, callback)->
-    JReferral = require "./referral/index"
-    JReferral.fetchEarnedSpace client, callback
+    originId = client.connection.delegate.getId()
+
+    JEarnedReward = require './rewards/earnedreward'
+    JEarnedReward.fetchEarnedAmount {originId}, callback


workers: add fetch group invoices handler

--- workers/social/lib/social/models/payment.coffee
+++ workers/social/lib/social/models/payment.coffee
@@ -22,6 +22,8 @@
           (signature Object, Function)
         invoices          :
           (signature Object, Function)
+        fetchGroupInvoices:
+          (signature Object, Function)
         creditCard        :
           (signature Object, Function)
         fetchGroupCreditCard:
@@ -146,6 +148,33 @@
     url = "#{socialProxyUrl}/payments/subscriptions?account_id=#{data.accountId}"

     get url, data, callback
+
+  @fetchGroupInvoices = (group, callback) ->
+
+    return callback new KodingError 'No such group'  unless group
+
+    if group.slug is 'koding' or KONFIG.environment is 'default'
+      return callback null, { planTitle: 'unlimited' }
+
+    url = "#{socialProxyUrl}/payments/group/invoices?group_id=#{group._id}"
+    get url, {}, (err, subscription) ->
+      return callback err  if err
+
+      # unless isSubscriptionOk group, subscription
+      #   return callback new KodingError 'Trial period exceeded'
+
+      callback null, sanitizeSubscription subscription
+
+  @fetchGroupInvoices$ = secure (client, callback) ->
+
+    slug = client?.context?.group
+
+    return callback new KodingError 'No such group'  unless slug
+
+    JGroup = require './group'
+    JGroup.one { slug }, (err, group) ->
+      return callback err  if err
+      Payment.fetchGroupInvoices group, callback

   @invoices = secure (client, data, callback) ->
     data.accountId = getAccountId client


auto render in constructor

auto render in constructor

--- source/app/organism/modal.coffee
+++ source/app/organism/modal.coffee
@@ -33,6 +33,8 @@
     @constructor.type = "Modal"
     @attributes.parent = document.body
     super @attributes
+    @render()
+
     @article = @el.children("article")
     # @TODO: Test in QuoJS
     @article.bind "webkitAnimationEnd mozAnimationEnd", @onAnimationEnd


Fixes regex in quickVows.

Fixes regex in quickVows.

--- test/helpers/quick-vows.coffee
+++ test/helpers/quick-vows.coffee
@@ -7,9 +7,10 @@

       'My context description': quickVows
         topic: {name: 'myTopic', size: 15, colors: ['blue', 'green']}
+        'it should have property': 'name'
         'the name should equal': 'myTopic'
-        'the size should equal': 15
-        'the colors should be an instaceOf': Array
+        'its size should equal': 15
+        'colors should be an instaceOf': Array
         'the colors should eql': ['blue', 'green']

 In the vows property keys, 'the', 'it' and 'its' (at the beginning of the vow)
@@ -42,19 +43,19 @@
         context.topic = arg
       else
         argString = "#{arg}"
-        argString = argString.substr(0, 20) + '...' if argString.length > 30
         argString = argString.replace(/\n*/g, '')
+        argString = argString.substr(0, 30) + '...' if argString.length > 30
         context["#{vow} '#{argString}'"] = (topic) ->
           obj = topic
           scope = {}
           for part in vow.split(' ')
-            if not /^(?:the|it|its)$/.test part
+            if not /^(the|it|its)$/.test part
               obj = obj[part]
             if part is 'should'
               scope = obj
           if typeof obj is 'function'
             obj.call scope, arg
-          else if /^(?:empty|arguments|ok|true|false)$/.test part
+          else if /^(empty|arguments|ok|true|false)$/.test part
             # The assertion has already been made.
           else
             throw new Error 'quickVow did not resolve into a function'


button changes yo

button changes yo

--- Framework/Views/Controls/Button_Framework.coffee
+++ Framework/Views/Controls/Button_Framework.coffee
@@ -31,6 +31,10 @@
       left: @startStyle("left")
       right: @startStyle("right")
     })
+
+    @view.addEventListener("singletap", () => @settings.onClick() if @view.enabled)
+    @view.addEventListener("touchstart", () => @onTouchStart() if @view.enabled)
+    @view.addEventListener("touchend", () => @onTouchEnd() if @view.enabled)

     @button = Ti.UI.createView({
       height: Ti.UI.FILL
@@ -138,10 +142,6 @@

   setEnabled: (enabled) =>
     if enabled
-      @view.addEventListener("singletap", @settings.onClick)
-      @view.addEventListener("touchstart", @onTouchStart)
-      @view.addEventListener("touchend", @onTouchEnd)
+      @view.enabled = true
     else
-      @view.removeEventListener("singletap", @settings.onClick)
-      @view.removeEventListener("touchstart", @onTouchStart)
-      @view.removeEventListener("touchend", @onTouchEnd)
+      @view.enabled = false


:memo: Fix typo in spec description

:memo: Fix typo in spec description

--- spec/command-palette-spec.coffee
+++ spec/command-palette-spec.coffee
@@ -113,7 +113,7 @@
       expect(matches.length).toBe 1
       expect(matches.last().text()).toBe 'Application'

-    it "highlights multiple matches in the file name", ->
+    it "highlights multiple matches in the command name", ->
       palette.filterEditorView.getModel().setText('ApplicationAbout')
       palette.populateList()
       resultView = palette.getSelectedItemView()


Make tests run with promise and non-promise package deactivate

--- spec/deprecation-cop-spec.coffee
+++ spec/deprecation-cop-spec.coffee
@@ -29,6 +29,8 @@
       waitsForPromise ->
         activationPromise

+      waitsForPromise ->
+        Promise.resolve(atom.packages.deactivatePackage('deprecation-cop')) # Wrapped for Promise & non-Promise deactivate
+
       runs ->
-        atom.packages.deactivatePackage('deprecation-cop')
         expect(atom.workspace.getActivePane().getActiveItem()).not.toExist()


client: remove duplicate payment success event

--- client/app/lib/payment/paymentmodal.coffee
+++ client/app/lib/payment/paymentmodal.coffee
@@ -141,5 +141,3 @@

     @form.showSuccess operation

-    @once 'KDModalViewDestroyed', =>
-      @emit 'PaymentWorkflowFinished', @state


Update spec to use new config based providers

--- spec/symbol-provider-spec.coffee
+++ spec/symbol-provider-spec.coffee
@@ -12,7 +12,7 @@
     return suggestion if suggestion.word is word
   null

-describe 'Autocomplete', ->
+fdescribe 'SymbolProvider', ->
   [completionDelay, editorView, editor, mainModule, autocompleteManager] = []

   beforeEach ->
@@ -39,13 +39,19 @@
       waitsForPromise ->
         atom.packages.activatePackage("language-coffee-script").then ->
           atom.packages.activatePackage("autocomplete-plus").then (a) ->
-            autocompleteManager = a.mainModule.autocompleteManager
+            mainModule = a.mainModule
+
+      waitsFor ->
+        mainModule.autocompleteManager?.ready
+
+      runs ->
+        autocompleteManager = mainModule.autocompleteManager

       runs ->
         advanceClock 1
         editorView = atom.views.getView(editor)

-    fit "properly swaps a lower priority type for a higher priority type", ->
+    it "properly swaps a lower priority type for a higher priority type", ->
       # SomeModule is parsed as a variable in the
       # `SomeModule = require 'some-module'` line and as a class in the
       # `extends SomeModule` line
@@ -81,7 +87,7 @@
       provider = autocompleteManager.providerManager.fuzzyProvider
       expect(indexOfWord(provider.wordList, 'quicksort')).not.toEqual(-1)

-    it "adds words to the wordlist after they have been written", ->
+    xit "adds words to the wordlist after they have been written", ->
       provider = autocompleteManager.providerManager.fuzzyProvider

       expect(provider.wordList.indexOf('somethingNew')).toEqual(-1)


Cleanup SymbolProvider specs

Cleanup SymbolProvider specs

--- spec/symbol-provider-spec.coffee
+++ spec/symbol-provider-spec.coffee
@@ -13,39 +13,153 @@
   [completionDelay, editorView, editor, mainModule, autocompleteManager, provider] = []

   beforeEach ->
+    # Set to live completion
+    atom.config.set('autocomplete-plus.enableAutoActivation', true)
+    atom.config.set('autocomplete-plus.defaultProvider', 'Symbol')
+
+    # Set the completion delay
+    completionDelay = 100
+    atom.config.set('autocomplete-plus.autoActivationDelay', completionDelay)
+    completionDelay += 100 # Rendering delaya\
+
+    workspaceElement = atom.views.getView(atom.workspace)
+    jasmine.attachToDOM(workspaceElement)
+
+    waitsForPromise ->
+      Promise.all [
+        atom.workspace.open("sample.js").then (e) -> editor = e
+        atom.packages.activatePackage("language-javascript")
+        atom.packages.activatePackage("autocomplete-plus").then (a) -> mainModule = a.mainModule
+      ]
+
     runs ->
-      # Set to live completion
-      atom.config.set('autocomplete-plus.enableAutoActivation', true)
-      atom.config.set('autocomplete-plus.defaultProvider', 'Symbol')
+      autocompleteManager = mainModule.autocompleteManager
+      advanceClock 1
+      editorView = atom.views.getView(editor)
+      provider = autocompleteManager.providerManager.fuzzyProvider

-      # Set the completion delay
-      completionDelay = 100
-      atom.config.set('autocomplete-plus.autoActivationDelay', completionDelay)
-      completionDelay += 100 # Rendering delaya\
+  it "does not output suggestions from the other buffer", ->
+    [results, coffeeEditor] = []

-      workspaceElement = atom.views.getView(atom.workspace)
-      jasmine.attachToDOM(workspaceElement)
+    waitsForPromise ->
+      Promise.all [
+        atom.packages.activatePackage("language-coffee-script")
+        atom.workspace.open("sample.coffee").then (e) -> coffeeEditor = e
+      ]

-  describe "when completing with the default configuration", ->
+    runs ->
+      advanceClock 1 # build the new wordlist
+      expect(suggestionsForPrefix(provider, coffeeEditor, 'item')).toHaveLength 0
+
+  it "runs a completion ", ->
+    expect(suggestionForWord(provider.symbolStore, 'quicksort')).toBeTruthy()
+
+  it "adds words to the symbol list after they have been written", ->
+    expect(suggestionsForPrefix(provider, editor, 'anew')).not.toContain 'aNewFunction'
+
+    editor.insertText('function aNewFunction(){};')
+    editor.insertText(' ')
+    advanceClock provider.changeUpdateDelay
+
+    expect(suggestionsForPrefix(provider, editor, 'anew')).toContain 'aNewFunction'
+
+  it "adds words after they have been added to a ", ->
+    expect(suggestionsForPrefix(provider, editor, 'some')).not.toContain 'somestring'
+
+    editor.insertText('abc = "somestring"')
+    editor.insertText(' ')
+    advanceClock provider.changeUpdateDelay
+
+    expect(suggestionsForPrefix(provider, editor, 'some')).toContain 'somestring'
+
+  it "removes words from the symbol list when they do not exist in the buffer", ->
+    editor.moveToBottom()
+    editor.moveToBeginningOfLine()
+
+    expect(suggestionsForPrefix(provider, editor, 'anew')).not.toContain 'aNewFunction'
+
+    editor.insertText('function aNewFunction(){};')
+    advanceClock provider.changeUpdateDelay
+    expect(suggestionsForPrefix(provider, editor, 'anew')).toContain 'aNewFunction'
+
+    editor.setCursorBufferPosition([13, 21])
+    editor.backspace()
+    advanceClock provider.changeUpdateDelay
+
+    expect(suggestionsForPrefix(provider, editor, 'anew')).toContain 'aNewFunctio'
+    expect(suggestionsForPrefix(provider, editor, 'anew')).not.toContain 'aNewFunction'
+
+  it "correctly tracks the buffer row associated with symbols as they change", ->
+    editor.setText('')
+    advanceClock(provider.changeUpdateDelay)
+
+    editor.setText('function abc(){}\nfunction abc(){}')
+    advanceClock(provider.changeUpdateDelay)
+    suggestion = suggestionForWord(provider.symbolStore, 'abc')
+    expect(suggestion.bufferRowsForEditorPath(editor.getPath())).toEqual [0, 1]
+
+    editor.setCursorBufferPosition([2, 100])
+    editor.insertText('\n\nfunction omg(){}; function omg(){}')
+    advanceClock(provider.changeUpdateDelay)
+    suggestion = suggestionForWord(provider.symbolStore, 'omg')
+    expect(suggestion.bufferRowsForEditorPath(editor.getPath())).toEqual [3, 3]
+
+    editor.selectLeft(16)
+    editor.backspace()
+    advanceClock(provider.changeUpdateDelay)
+    suggestion = suggestionForWord(provider.symbolStore, 'omg')
+    expect(suggestion.bufferRowsForEditorPath(editor.getPath())).toEqual [3]
+
+    editor.insertText('\nfunction omg(){}')
+    advanceClock(provider.changeUpdateDelay)
+    suggestion = suggestionForWord(provider.symbolStore, 'omg')
+    expect(suggestion.bufferRowsForEditorPath(editor.getPath())).toEqual [3, 4]
+
+    editor.setText('')
+    advanceClock(provider.changeUpdateDelay)
+
+    expect(suggestionForWord(provider.symbolStore, 'abc')).toBeUndefined()
+    expect(suggestionForWord(provider.symbolStore, 'omg')).toBeUndefined()
+
+    editor.setText('function abc(){}\nfunction abc(){}')
+    editor.setCursorBufferPosition([0, 0])
+    editor.insertText('\n')
+    editor.setCursorBufferPosition([2, 100])
+    editor.insertText('\nfunction abc(){}')
+    advanceClock(provider.changeUpdateDelay)
+
+    # This is kind of a mess right now. it does not correctly track buffer
+    # rows when there are several changes before the change delay is
+    # triggered. So we're just making sure the row is in there.
+    suggestion = suggestionForWord(provider.symbolStore, 'abc')
+    expect(suggestion.bufferRowsForEditorPath(editor.getPath())).toContain 3
+
+  describe "when includeCompletionsFromAllBuffers is enabled", ->
     beforeEach ->
-      runs -> atom.config.set "autocomplete-plus.enableAutoActivation", true
+      atom.config.set('autocomplete-plus.includeCompletionsFromAllBuffers', true)

-      waitsForPromise -> atom.workspace.open("sample.coffee").then (e) ->
-        editor = e
-
-      # Activate the package
       waitsForPromise ->
         atom.packages.activatePackage("language-coffee-script").then ->
-          atom.packages.activatePackage("autocomplete-plus").then (a) ->
-            mainModule = a.mainModule
+          atom.workspace.open("sample.coffee").then (e) ->
+            editor = e

       runs ->
-        autocompleteManager = mainModule.autocompleteManager
-        advanceClock 1
-        editorView = atom.views.getView(editor)
         provider = autocompleteManager.providerManager.fuzzyProvider

-    it "does not output suggestions from the other buffer", ->
+    afterEach ->
+      atom.config.set('autocomplete-plus.includeCompletionsFromAllBuffers', false)
+
+    it "outputs unique suggestions", ->
+      results = null
+      waitsForPromise ->
+        promise = provider.getSuggestions({editor, prefix: 'qu', bufferPosition: new Point(7, 0)})
+        advanceClock 1
+        promise.then (r) -> results = r
+
+      runs ->
+        expect(results).toHaveLength 1
+
+    it "outputs suggestions from the other buffer", ->
       results = null
       waitsForPromise ->
         promise = provider.getSuggestions({editor, prefix: 'item', bufferPosition: new Point(7, 0)})
@@ -53,149 +167,11 @@
         promise.then (r) -> results = r

       runs ->
-        expect(results).toHaveLength 0
+        expect(results[0].text).toBe 'items'

-  describe "when auto-activation is enabled", ->
-    beforeEach ->
-      runs ->
-        atom.config.set('autocomplete-plus.enableAutoActivation', true)
-
-      waitsForPromise -> atom.workspace.open('sample.js').then (e) ->
-        editor = e
-
-      # Activate the package
-      waitsForPromise ->
-        atom.packages.activatePackage("language-javascript").then ->
-          atom.packages.activatePackage("autocomplete-plus").then (a) ->
-            mainModule = a.mainModule
-
-      runs ->
-        autocompleteManager = mainModule.autocompleteManager
-        advanceClock 1
-        editorView = atom.views.getView(editor)
-        provider = autocompleteManager.providerManager.fuzzyProvider
-
-    it "runs a completion ", ->
-      expect(suggestionForWord(provider.symbolStore, 'quicksort')).toBeTruthy()
-
-    it "adds words to the symbol list after they have been written", ->
-      expect(suggestionsForPrefix(provider, editor, 'anew')).not.toContain 'aNewFunction'
-
-      editor.insertText('function aNewFunction(){};')
-      editor.insertText(' ')
-      advanceClock provider.changeUpdateDelay
-
-      expect(suggestionsForPrefix(provider, editor, 'anew')).toContain 'aNewFunction'
-
-    it "adds words after they have been added to a ", ->
-      expect(suggestionsForPrefix(provider, editor, 'some')).not.toContain 'somestring'
-
-      editor.insertText('abc = "somestring"')
-      editor.insertText(' ')
-      advanceClock provider.changeUpdateDelay
-
-      expect(suggestionsForPrefix(provider, editor, 'some')).toContain 'somestring'
-
-    it "removes words from the symbol list when they do not exist in the buffer", ->
-      editor.moveToBottom()
-      editor.moveToBeginningOfLine()
-
-      expect(suggestionsForPrefix(provider, editor, 'anew')).not.toContain 'aNewFunction'
-
-      editor.insertText('function aNewFunction(){};')
-      advanceClock provider.changeUpdateDelay
-      expect(suggestionsForPrefix(provider, editor, 'anew')).toContain 'aNewFunction'
-
-      editor.setCursorBufferPosition([13, 21])
-      editor.backspace()
-      advanceClock provider.changeUpdateDelay
-
-      expect(suggestionsForPrefix(provider, editor, 'anew')).toContain 'aNewFunctio'
-      expect(suggestionsForPrefix(provider, editor, 'anew')).not.toContain 'aNewFunction'
-
-    it "correctly tracks the buffer row associated with symbols as they change", ->
-      editor.setText('')
-      advanceClock(provider.changeUpdateDelay)
-
-      editor.setText('function abc(){}\nfunction abc(){}')
-      advanceClock(provider.changeUpdateDelay)
-      suggestion = suggestionForWord(provider.symbolStore, 'abc')
-      expect(suggestion.bufferRowsForEditorPath(editor.getPath())).toEqual [0, 1]
-
-      editor.setCursorBufferPosition([2, 100])
-      editor.insertText('\n\nfunction omg(){}; function omg(){}')
-      advanceClock(provider.changeUpdateDelay)
-      suggestion = suggestionForWord(provider.symbolStore, 'omg')
-      expect(suggestion.bufferRowsForEditorPath(editor.getPath())).toEqual [3, 3]
-
-      editor.selectLeft(16)
-      editor.backspace()
-      advanceClock(provider.changeUpdateDelay)
-      suggestion = suggestionForWord(provider.symbolStore, 'omg')
-      expect(suggestion.bufferRowsForEditorPath(editor.getPath())).toEqual [3]
-
-      editor.insertText('\nfunction omg(){}')
-      advanceClock(provider.changeUpdateDelay)
-      suggestion = suggestionForWord(provider.symbolStore, 'omg')
-      expect(suggestion.bufferRowsForEditorPath(editor.getPath())).toEqual [3, 4]
-
-      editor.setText('')
-      advanceClock(provider.changeUpdateDelay)
-
-      expect(suggestionForWord(provider.symbolStore, 'abc')).toBeUndefined()
-      expect(suggestionForWord(provider.symbolStore, 'omg')).toBeUndefined()
-
-      editor.setText('function abc(){}\nfunction abc(){}')
-      editor.setCursorBufferPosition([0, 0])
-      editor.insertText('\n')
-      editor.setCursorBufferPosition([2, 100])
-      editor.insertText('\nfunction abc(){}')
-      advanceClock(provider.changeUpdateDelay)
-
-      # This is kind of a mess right now. it does not correctly track buffer
-      # rows when there are several changes before the change delay is
-      # triggered. So we're just making sure the row is in there.
-      suggestion = suggestionForWord(provider.symbolStore, 'abc')
-      expect(suggestion.bufferRowsForEditorPath(editor.getPath())).toContain 3
-
-    describe "when includeCompletionsFromAllBuffers is enabled", ->
-      beforeEach ->
-        atom.config.set('autocomplete-plus.includeCompletionsFromAllBuffers', true)
-
-        waitsForPromise ->
-          atom.packages.activatePackage("language-coffee-script").then ->
-            atom.workspace.open("sample.coffee").then (e) ->
-              editor = e
-
-        runs ->
-          provider = autocompleteManager.providerManager.fuzzyProvider
-
-      afterEach ->
-        atom.config.set('autocomplete-plus.includeCompletionsFromAllBuffers', false)
-
-      it "outputs unique suggestions", ->
-        results = null
-        waitsForPromise ->
-          promise = provider.getSuggestions({editor, prefix: 'qu', bufferPosition: new Point(7, 0)})
-          advanceClock 1
-          promise.then (r) -> results = r
-
-        runs ->
-          expect(results).toHaveLength 1
-
-      it "outputs suggestions from the other buffer", ->
-        results = null
-        waitsForPromise ->
-          promise = provider.getSuggestions({editor, prefix: 'item', bufferPosition: new Point(7, 0)})
-          advanceClock 1
-          promise.then (r) -> results = r
-
-        runs ->
-          expect(results[0].text).toBe 'items'
-
-    # Fixing This Fixes #76
-    xit 'adds words to the wordlist with unicode characters', ->
-      expect(provider.symbolStore.indexOf('somēthingNew')).toBeFalsy()
-      editor.insertText('somēthingNew')
-      editor.insertText(' ')
-      expect(provider.symbolStore.indexOf('somēthingNew')).toBeTruthy()
+  # Fixing This Fixes #76
+  xit 'adds words to the wordlist with unicode characters', ->
+    expect(provider.symbolStore.indexOf('somēthingNew')).toBeFalsy()
+    editor.insertText('somēthingNew')
+    editor.insertText(' ')
+    expect(provider.symbolStore.indexOf('somēthingNew')).toBeTruthy()


Refactor inquiry test using spies to be slightly more readable.

--- desktop/test/models/artwork_inquiry.coffee
+++ desktop/test/models/artwork_inquiry.coffee
@@ -16,9 +16,9 @@
       Backbone.sync.restore()

     it 'sends the inquiry immediately', ->
-      @inquiry.set(id: 'foo').send(null, success: ->
-        true.should.be.true()
-      ).then ->
+      success = sinon.spy()
+      @inquiry.set(id: 'foo').send(null, success: success).then ->
+        success.called.should.be.ok()
         Backbone.sync.args[0][2].url
           .should.containEql '/api/v1/me/artwork_inquiry_request/foo/send'


remove log

remove log

--- packages/tower-application/server/watcher.coffee
+++ packages/tower-application/server/watcher.coffee
@@ -40,7 +40,6 @@
     chokidar    = require('chokidar')
     directories = ['app', 'config']
     clientPath  = new RegExp(_.regexpEscape(_path.join('app', 'client')))
-    console.log clientPath

     watcher     = chokidar.watch directories,
       # ignore anything NOT matching js|coffee|iced


Better require.

Better require.

--- src/coffee/stockxmlimport.coffee
+++ src/coffee/stockxmlimport.coffee
@@ -1,5 +1,5 @@
 _ = require('underscore')._
-xmlHelpers = require '../lib/xmlhelpers.js'
+xmlHelpers = require '../lib/xmlhelpers'
 InventoryUpdater = require('sphere-node-sync').InventoryUpdater
 Q = require 'q'


For fun, score now depends on number of effective commands executed

--- web/scripts/gameManager.coffee
+++ web/scripts/gameManager.coffee
@@ -167,6 +167,8 @@
                 @executeAICommand character, aiCommand
         if result?.continueExecution
             @runCharacterCommand character
+        else
+            @score++
         return

     checkEvents: (protagonistDoneMoving) ->


Add class check

Add class check

--- web/scripts/referenceUI.coffee
+++ web/scripts/referenceUI.coffee
@@ -174,12 +174,11 @@
             codeland.doppioAPI.abort()
             codeland.doppioAPI.setOutputFunctions stdout, log
             srcText  = sandBoxEditor.getStudentCode()
-            # if(srcText.indexOf("[]") != -1)
-            #                 stdout('Arrays are not yet supported by our Web-based Java')
-            #                 jQuery(this).siblings("img").hide()
-            #                 jQuery(this).show()
-            #             else
-            codeland.doppioAPI.run(srcText,null, finished_cb)
+            if(srcText.indexOf("class") != -1)
+                stdout('Classes are not yet supported by our Web-based Java')
+                finished_cb()
+            else
+                codeland.doppioAPI.run(srcText,null, finished_cb)

             e.preventDefault()
             return


added page navigation confirmation dialog

--- src/main.coffee
+++ src/main.coffee
@@ -8,6 +8,10 @@

 hideCodeText = 'Hide code'
 showCodeText = 'Show code'
+
+window.addEventListener('beforeunload', (e) ->
+  e.returnValue = 'Are you sure you want to leave this page?'
+)

 window.App =
   hello: ->


main: process.title fix

main: process.title fix

--- src/main.coffee
+++ src/main.coffee
@@ -94,7 +94,7 @@

     xmppConn.on 'online', ->
         logger.info "XMPP connection established"
-        process.title "buddycloud-server #{version}: #{xmppConn.jid}"
+        process.title = "buddycloud-server #{version}: #{xmppConn.jid}"
         model.forListeners (listener) ->
             xmppConn.probePresence(listener)


correct back grid to backbone error

correct back grid to backbone error

--- src/main.coffee
+++ src/main.coffee
@@ -24,7 +24,7 @@
     backbonePaginator: "lib/backbone-paginator/lib/backbone.paginator.min"
     backgrid: "lib/backgrid/lib/backgrid"
     backgridPaginator: "lib/backgrid-paginator/backgrid-paginator" #the minified version seems to have an issue.
-    backgridRelational: "lib/backgrid-relational/backgrid-relational"
+    backboneRelational: "lib/backbone-relational/backbone-relational"
     app: "../src/app"


fix: offset bug

fix: offset bug

--- src/main.coffee
+++ src/main.coffee
@@ -27,15 +27,12 @@
 	then: (onFulfilled, onRejected) ->
 		self = @

-		if @_state == $pending
-			@_thenIndex++
+		offset = @_thenIndex * 4

-		thenIndex = @_thenIndex
-
-		addHandler self, thenIndex, onFulfilled, onRejected
+		addHandler self, offset, onFulfilled, onRejected

 		new Promise (resolve, reject) ->
-			chainHandlers self, thenIndex, resolve, reject
+			chainHandlers self, offset, resolve, reject

 	catch: (onRejected) ->

@@ -82,30 +79,31 @@
 			genTrigger(self, $rejected)
 		return

-	addHandler = (self, thenIndex, onFulfilled, onRejected) ->
+	addHandler = (self, offset, onFulfilled, onRejected) ->
 		switch self._state
 			when $pending
-				self._handlers.splice self._handlers.length, 0,
-					onFulfilled, onRejected
+				self._handlers[offset] = onFulfilled
+				self._handlers[offset + 1] = onRejected
+				self._thenIndex++

 			when $resolved
-				self._handlers[thenIndex * 4] = onFulfilled self._value
+				self._handlers[offset] = onFulfilled self._value

 			when $rejected
-				self._handlers[thenIndex * 4 + 1] = onRejected self._value
+				self._handlers[offset + 1] = onRejected self._value
 		return

-	chainHandlers = (self, thenIndex, resolve, reject) ->
+	chainHandlers = (self, offset, resolve, reject) ->
 		switch self._state
 			when $pending
-				self._handlers.splice thenIndex * 4 + 2, 0,
-					resolve, reject
+				self._handlers[offset + 2] = resolve
+				self._handlers[offset + 3] = reject

 			when $resolved
-				chainHandler self._handlers[thenIndex * 4], 0, resolve
+				chainHandler self._handlers[offset], 0, resolve

 			when $rejected
-				chainHandler self._handlers[thenIndex * 4 + 1], 0, reject
+				chainHandler self._handlers[offset + 1], 0, reject

 		return

@@ -132,6 +130,8 @@
 			handler = self._handlers[k]
 			thenHandler = self._handlers[k + 2]

+			continue if not handler
+
 			if thenHandler
 				chainHandler handler(value), thenHandler
 			else


fix spelling

fix spelling

--- src/main.coffee
+++ src/main.coffee
@@ -172,7 +172,7 @@
     return handler abort if abort
     if values.length is 0
       trailer_count  += +1
-      info '23983', "refillable source depeleted: #{trailer_count} / #{settings.repeat}" if settings.show
+      info '23983', "refillable source depleted: #{trailer_count} / #{settings.repeat}" if settings.show
       if trailer_count < settings.repeat
         value           = settings.trailer
       else


Make images draggable again

Make images draggable again

--- src/plugins/image/current.coffee
+++ src/plugins/image/current.coffee
@@ -30,7 +30,6 @@
       editable = jQuery @options.editable.element
       widget = @
       jQuery('img', editable).each (index, elem) ->
-        elem.contentEditable = false
         widget._initDraggable elem, editable

       jQuery('p', editable).each (index, elem) ->


Update src/plugins/jade-template.coffee

Update src/plugins/jade-template.coffee

Did not know this change would reflect to any pull requests.

--- src/plugins/jade-template.coffee
+++ src/plugins/jade-template.coffee
@@ -24,7 +24,7 @@
       try
         rv = jade.compile buffer.toString(),
           filename: fullpath
-          pretty: false
+          pretty: true
         callback null, new this rv
       catch error
         callback error