Scope unpack dirs to each createPackageFromFiles call

--- src/asar.coffee
+++ src/asar.coffee
@@ -9,20 +9,19 @@
 crawlFilesystem = require './crawlfs'
 createSnapshot = require './snapshot'

-_UnpackDirs = []
-
 # Return whether or not a directory should be excluded from packing due to
 # "--unpack-dir" option
 #
 # @param {string} path - diretory path to check
 # @param {string} pattern - literal prefix [for backward compatibility] or glob pattern
+# @param {array} unpackDirs - Array of directory paths previously marked as unpacked
 #
-isUnpackDir = (path, pattern) ->
+isUnpackDir = (path, pattern, unpackDirs) ->
   if path.indexOf(pattern) is 0 || minimatch path, pattern
-    _UnpackDirs.push path if _UnpackDirs.indexOf(path) is -1
+    unpackDirs.push path if unpackDirs.indexOf(path) is -1
     return true
   else
-    for dir in _UnpackDirs
+    for dir in unpackDirs
       return true if path.indexOf(dir) is 0
     return false

@@ -51,6 +50,7 @@
   metadata ?= {}
   filesystem = new Filesystem(src)
   files = []
+  unpackDirs = []

   if options.ordering
     orderingFiles = fs.readFileSync(options.ordering).toString().split('\n').map (line) ->
@@ -97,7 +97,7 @@
       when 'directory'
         shouldUnpack =
           if options.unpackDir
-            isUnpackDir path.relative(src, filename), options.unpackDir
+            isUnpackDir path.relative(src, filename), options.unpackDir, unpackDirs
           else
             false
         filesystem.insertDirectory filename, shouldUnpack
@@ -107,7 +107,7 @@
           shouldUnpack = minimatch filename, options.unpack, matchBase: true
         if not shouldUnpack and options.unpackDir
           dirName = path.relative src, path.dirname(filename)
-          shouldUnpack = isUnpackDir dirName, options.unpackDir
+          shouldUnpack = isUnpackDir dirName, options.unpackDir, unpackDirs
         files.push filename: filename, unpack: shouldUnpack
         filesystem.insertFile filename, shouldUnpack, file, options, done
         return


Add Promise.prototype.done shim

Add Promise.prototype.done shim

Base on our research, this is the only non-standard Q method people are
really calling. We didn’t check the really obscure stuff, but this
should cover the vast majority of issues.

Signed-off-by: Max Brunsfeld <78036c9b69b887700d5846f26a788d53b201ffbb@gmail.com>

--- src/atom.coffee
+++ src/atom.coffee
@@ -873,6 +873,11 @@
     ipc.send('call-window-method', 'setAutoHideMenuBar', autoHide)
     ipc.send('call-window-method', 'setMenuBarVisibility', not autoHide)

+# Preserve this deprecation until 2.0. Sorry. Should have removed Q sooner.
+Promise.prototype.done = (callback) ->
+  deprecate("Atom now uses ES6 Promises instead of Q. Call promise.then instead of promise.done")
+  @then(callback)
+
 if includeDeprecatedAPIs
   # Deprecated: Callers should be converted to use atom.deserializers
   Atom::registerRepresentationClass = ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -53,7 +53,7 @@
     [h, s, l, a] = hsl

     h = h % 360
-    a ||= 1
+    a = 1 unless a?

     r = g = b = null


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -347,7 +347,7 @@
         return hsl[2]

     ###*
-    A copy of the calling color with its hue shifted by `degrees`. This differs from the hue setter in that it will wrap around 0 and 360.
+    A copy of the calling color with its hue shifted by `degrees`. This differs from the hue setter in that it adds to the existing hue value and will wrap around 0 and 360.

     @name shiftHue
     @methodOf Color#
@@ -359,7 +359,7 @@
       @copy().shiftHue$(degrees)

     ###*
-    The calling color with its hue shifted by `degrees`. This differs from the hue setter in that it will wrap around 0 and 360.
+    The calling color with its hue shifted by `degrees`. This differs from the hue setter in that it adds to the existing hue value and will wrap around 0 and 360.

     @name shiftHue$
     @methodOf Color#
@@ -439,6 +439,16 @@

       return this

+    ###*
+    A new color. The calling color mixed with `other` using `amount` as the mixing ratio. If amount is not passed, then the colors are mixed evenly.
+
+    @name mixWith$
+    @methodOf Color#
+    @param {Color} other the other color to mix
+    @param {Number} [amount] the mixing ratio of the calling color to `other`
+
+    @returns {Color} The modified calling color after mixing it with `other`
+    ###
     saturate: (amount) ->
       @copy().saturate$(amount)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -1571,6 +1571,7 @@
 ]

 names.each (i, element) ->
+  debugger
   lookup[element[1].toLowerCase().replace(/ /g, "")] = Color(element[0])

 ###


Update to use scoped-http-client

Update to use scoped-http-client

--- src/hubot/twilio.coffee
+++ src/hubot/twilio.coffee
@@ -1,6 +1,6 @@
 Robot = require '../robot'
 HTTP  = require 'http'
-Qs    = require 'querystring'
+QS    = require 'querystring'

 class Twilio extends Robot
   constructor: ->
@@ -11,10 +11,11 @@

   send: (user, strings...) ->
     message = strings.join "\n"
-    console.log "Sending reply SMS: #{message} to #{user.id}"
     @send_sms message, user.id, (err, body) =>
       if err or not body?
-        console.log "error sending reply SMS: #{err}"
+        console.log "Error sending reply SMS: #{err}"
+      else
+        console.log "Sending reply SMS: #{message} to #{user.id}"

   reply: (user, strings...) ->
     for str in strings
@@ -25,7 +26,7 @@

   run: ->
     server = HTTP.createServer (request, response) =>
-      payload = Qs.parse(request.url)
+      payload = QS.parse(request.url)

       if payload.Body? and payload.From?
         console.log "Received SMS: #{payload.Body} from #{payload.From}"
@@ -42,8 +43,21 @@
     @receive new Robot.TextMessage user, body

   send_sms: (message, to, callback) ->
-    # rework to use scoped-http-client here
-    console.log "#{to}: #{message}"
+    auth = new Buffer(@sid + ':' + @token).toString("base64")
+    data = QS.stringify From: @from, To: to, Body: message
+
+    @http("https://api.twilio.com")
+      .path("/2010-04-01/Accounts/#{@sid}/SMS/Messages.json")
+      .header("Authorization", "Basic #{auth}")
+      .header("Content-Type", "application/x-www-form-urlencoded")
+      .post(data) (err, res, body) ->
+        if err
+          callback err
+        else if res.statusCode is 201
+          json = JSON.parse(body)
+          callback null, body
+        else
+          json = JSON.parse(body)
+          callback body.message

 module.exports = Twilio
-


:bug: guard incase closest('atom-text-editor') return non editor object #29

Not sure why this is happens although.

--- lib/main.coffee
+++ lib/main.coffee
@@ -10,7 +10,7 @@
 ]

 closestTextEditorHavingURI = (target) ->
-  editor = target?.closest?('atom-text-editor')?.getModel()
+  editor = target?.closest?('atom-text-editor')?.getModel?()
   return editor if editor?.getURI()

 createLocation = (editor, type) ->


autoload provider on timing of use(experimenting)

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,27 +1,30 @@
 {CompositeDisposable} = require 'atom'

 UI = require './ui'
-Lines = require './provider/lines'
 Search = require './provider/search'
-Fold = require './provider/fold'
 Input = require './input'
 settings = require './settings'

 module.exports =
   config: settings.config
+  providers: []

   activate: ->
     @subscriptions = new CompositeDisposable
     settings.removeDeprecated()
     @input = new Input

+    getUiOptions = =>
+      uiOptions:
+        initialInput: @getCurrentWord()
+
     getCurrentWord = @getCurrentWord.bind(this)
     @subscriptions.add atom.commands.add 'atom-workspace',
-      'narrow:lines': => @lines()
-      'narrow:lines-by-current-word': => @lines(getCurrentWord())
+      'narrow:lines': => @narrow('lines')
+      'narrow:lines-by-current-word': => @narrow('lines', getUiOptions())

-      'narrow:fold': => @fold()
-      'narrow:fold-by-current-word': => @fold(getCurrentWord())
+      'narrow:fold': => @narrow('fold')
+      'narrow:fold-by-current-word': => @narrow('fold', getUiOptions())

       'narrow:search': => @search()
       'narrow:search-by-current-word': => @search(getCurrentWord())
@@ -46,16 +49,11 @@
     else
       selection.getText()

-  # narrow: (providerName, word=null) ->
-  #   provider = require
-
-  lines: (initialInput) ->
-    ui = @getUI({initialInput})
-    new Lines(ui)
-
-  fold: (initialInput) ->
-    ui = @getUI({initialInput})
-    new Fold(ui)
+  narrow: (providerName, {uiOptions, providerOptions}={}) ->
+    if providerName not of @providers
+      @providers[providerName] = require("./provider/#{providerName}")
+    klass = @providers[providerName]
+    new klass(@getUI(uiOptions), providerOptions)

   searchCurrentProject: (word) ->
     projects = null
@@ -109,7 +107,11 @@
       vimState.searchInput.confirm()
       return text

+    getUiOptions = ->
+      uiOptions:
+        initialInput: confirmSearch()
+
     @subscriptions.add atom.commands.add 'atom-text-editor.vim-mode-plus-search',
-      'vim-mode-plus-user:narrow-lines-from-search': => @lines(confirmSearch())
+      'vim-mode-plus-user:narrow-lines-from-search': => @narrow('lines', getUiOptions())
       'vim-mode-plus-user:narrow-search': => @search(confirmSearch())
       'vim-mode-plus-user:narrow-search-current-project': => @searchCurrentProject(confirmSearch())


Refactor MarkerIndex::splice

Refactor MarkerIndex::splice

Don't require Node::splice and Leaf::splice to return remainder extents

--- src/marker-index.coffee
+++ src/marker-index.coffee
@@ -67,6 +67,10 @@

   splice: (position, oldExtent, newExtent, excludedIds) ->
     oldRangeIsEmpty = oldExtent.isZero()
+    spliceOldEnd = position.traverse(oldExtent)
+    spliceNewEnd = position.traverse(newExtent)
+    extentAfterChange = @extent.traversalFrom(spliceOldEnd)
+    @extent = spliceNewEnd.traverse(Point.max(Point.zero(), extentAfterChange))

     i = 0
     childEnd = Point.zero()
@@ -84,22 +88,19 @@
       unless childPrecedesRange
         if remainderToDelete?
           if remainderToDelete.isPositive()
-            remainderToDelete = child.splice(Point.zero(), remainderToDelete, Point.zero())
+            previousExtent = child.extent
+            child.splice(Point.zero(), remainderToDelete, Point.zero())
+            remainderToDelete = remainderToDelete.traversalFrom(previousExtent)
             childEnd = childStart.traverse(child.extent)
         else
           relativeStart = position.traversalFrom(childStart)
-          remainderToDelete = child.splice(relativeStart, oldExtent, newExtent, excludedIds)
+          child.splice(relativeStart, oldExtent, newExtent, excludedIds)
+          remainderToDelete = spliceOldEnd.traversalFrom(childEnd)
           childEnd = childStart.traverse(child.extent)

       if @children[i - 2]?.shouldMergeWith(child)
-        add = @children[i - 2].merge(child)
-        removed = @children.splice(i - 2, 2, @children[i - 2].merge(child))
+        @children.splice(i - 2, 2, @children[i - 2].merge(child))
         i--
-
-    previousExtent = @extent
-    spliceOldEnd = position.traverse(oldExtent)
-    @extent = childEnd
-    Point.max(Point.zero(), spliceOldEnd.traversalFrom(previousExtent))

   getStart: (id) ->
     return unless @ids.has(id)
@@ -213,13 +214,10 @@

   splice: (position, spliceOldExtent, spliceNewExtent, excludedIds) ->
     subtractSet(@ids, excludedIds) if excludedIds?
-
-    previousExtent = @extent
     spliceOldEnd = position.traverse(spliceOldExtent)
     spliceNewEnd = position.traverse(spliceNewExtent)
     extentAfterChange = @extent.traversalFrom(spliceOldEnd)
     @extent = spliceNewEnd.traverse(Point.max(Point.zero(), extentAfterChange))
-    Point.max(Point.zero(), spliceOldEnd.traversalFrom(previousExtent))

   getStart: (id) ->
     Point.zero() if @ids.has(id)


Allow explicitly enabling web workers.

Allow explicitly enabling web workers.

--- client.coffee
+++ client.coffee
@@ -51,6 +51,10 @@
                 data: testArray
                 message: 'ping'
                 transferable: transferable
+          catch error
+            # There was an error creating a worker, but disableWorker is
+            # explicitly set to false, so fallback is forbidden, let's rethrow
+            throw error if @disableWorker is false

         if not cryptoWorker
           cryptoWorker = new FallbackCryptoWorker @, @size, @onProgress, @chunkSize
@@ -66,7 +70,8 @@
         # copy may not work

       else
-        if not @disableWorker and Crypto._browserSupport.useWorker
+        # Or is disableWorker not set and we can use a web worker, or disableWorker is explicitly set to false
+        if (not @disableWorker? and Crypto._browserSupport.useWorker) or @disableWorker is false
           cryptoWorker = new WebCryptoWorker @, @size, @onProgress, @workerSrc, @chunkSize
         else
           cryptoWorker = new FallbackCryptoWorker @, @size, @onProgress, @chunkSize


clean up protocol handling

clean up protocol handling

--- client.coffee
+++ client.coffee
@@ -93,6 +93,13 @@
                 url = url + '?' + querystring.stringify(query)
             return url

+        httplibs: {
+            "http": require('http'),
+            "https": require('https'),
+            "http://": require('http'),
+            "https://": require('https')
+            }
+
         fetch: (options, callback) ->
             if not @useBrowserHttpStack?
                 @useBrowserHttpStack = typeof XMLHttpRequest != "undefined"
@@ -100,9 +107,7 @@
                 @fetch_browser options, callback
             else
                 if not @httplib?
-                    http = require('http')
-                    https = require('https')
-                    @httplib = {http:http, https:https}[@protocol]
+                    @httplib = @httplibs[@protocol]
                 @fetch_node options, callback

         fetch_browser: (options, callback) ->


status metrics

status metrics

--- src/model/statsd.coffee
+++ src/model/statsd.coffee
@@ -46,17 +46,18 @@
   var render_url = "/render?target=transformNull(summarize(stats.counters." + domain + ".Channels." + channelId

   if (type == 'status'){
-    var path = render_url + ".*.count,'1day'))&from=-7days&format=json";
+    var path = render_url + ".*.count,'1week'))&from=-1weeks&format=json";
     var raw = yield fetchData(path);
-    console.log(JSON.stringify(raw.data));
+    console.log(JSON.stringify(raw));
     console.log(path);
-    _.forEach(raw.data, function *(item) {
+    var i = 0;
+    //_.forEach(raw, function *(item) {
       data.push({
-        failed: raw.data[i][0],
-        successful: raw.data1[i][0],
+        failed: raw.data[0][0],
+        successful: raw.data1[0][0]
       });
-
-    });
+    //  i++;
+    //});

   } else {


example cleaning

example cleaning

--- src/index.coffee
+++ src/index.coffee
@@ -1,6 +1,5 @@
 b = require './browser' #some handy output functions
 Example = require './example' #just an example
-

 b.print 'Hai hacker! You are all set to rock!'
 do b.hr
@@ -11,18 +10,14 @@
 b.print example.getFoo()

 # Example of table
-table =
-	[
-		['January', 'February', 'March']
-		['April', 'May', 'June']
-		['July', 'August', 'September']
-	]
-
-b.table table
+b.table [
+	['January', 'February', 'March']
+	['April', 'May', 'June']
+	['July', 'August', 'September']
+]

 b.h1 'Headline 1'
 b.h2 'Headline 2'
 b.h3 'Headline 3'

-list = ['January', 'February', 'March']
-b.list list+b.list ['January', 'February', 'March']


docs added

docs added

--- src/index.coffee
+++ src/index.coffee
@@ -1,5 +1,43 @@
 b = require './browser' #some handy output functions
 Example = require './example' #just an example
+
+b.h1 'Playgroung'
+
+b.h2 'Docs'
+
+b.print 'There are many cool commands in browser.coffee module'
+
+b.h3 '#h1 (value)'
+b.print 'Renders <h1> containing desired value passed through only argument'
+
+b.h3 '#h2 (value)'
+b.print 'Renders <h2> containing desired value passed through only argument'
+
+b.h3 '#h3 (value)'
+b.print 'Renders <h3> containing desired value passed through only argument'
+
+b.h3 '#print (value, escaped = true)'
+b.print 'Prints value to the output and escape HTML tags'
+
+b.h3 '#reset'
+b.print 'Clean the page'
+
+b.h3 '#image (src)'
+b.print 'Add image from src to the page'
+
+b.h3 '#list (list)'
+b.print 'Renders unordered list, accepts list as only argument'
+
+b.h3 '#hr'
+b.print 'Renders horizontal line'
+
+b.h3 '#table (matrix)'
+b.print 'Renders table, accepts matrix as only argument'
+
+b.h3 '#code (value, escaped = true)'
+b.print 'Renders code block (escaped)'
+
+b.h2 'Examples'

 b.print 'Hai hacker! You are all set to rock!'
 do b.hr
@@ -16,9 +54,13 @@
 	['July', 'August', 'September']
 ]

-b.h1 'Headline 1'
-b.h2 'Headline 2'
-b.h3 'Headline 3'
+b.code "b.table [\n
+  \t['January', 'February', 'March']\n
+  \t['April', 'May', 'June']\n
+  \t['July', 'August', 'September']\n
+]\n
+"
+

 b.list ['January', 'February', 'March']


remove parens

remove parens

--- src/index.coffee
+++ src/index.coffee
@@ -74,7 +74,7 @@
 require('electron-debug')({showDevTools: true})

 ipc_main = require('electron').ipcMain
-ipc_main.on('open-image', (event, arg) ->
+ipc_main.on 'open-image', (event, arg) ->
   # If this is a tif then we can't open it in the browser window.
   # If it is any other type of image we open it in our own window.
   if _.includes(arg, 'tif')
@@ -90,9 +90,9 @@
       image_window = null
     image_window.loadURL(arg)
     image_window.show()
-)

-ipc_main.on('open-jp2', (event, arg) ->
+
+ipc_main.on 'open-jp2', (event, arg) ->
   jp2_window = new (electron.BrowserWindow)(
     show: false
     width: 1000
@@ -106,7 +106,7 @@
   url = "file://#{__dirname}/views/openseadragon.html?id=#{encoded_image_path}"
   jp2_window.loadURL(url)
   jp2_window.show()
-)
+

 ipc_main.on 'retry-launch', (event, arg) ->
   checkWhich()


Added missing logging which was resulting in program termination without errors.

--- src/index.coffee
+++ src/index.coffee
@@ -150,17 +150,20 @@
   .then ->
     d = Q.defer()
     logDebug "Fetching version info..."
-    client.execute "SELECT version FROM #{keyspace}.schema_version LIMIT 1", (error, results) ->
+    versionQuery = "SELECT version FROM #{keyspace}.schema_version LIMIT 1"
+    client.execute versionQuery, (error, results) ->
       if error?
+        logError "Error reading version information from the version table", error
         d.reject new Error("Error reading version information from the version table: #{error}", error)
       else if _(results.rows).size() > 0
         version = _(results.rows)?.first()?.version ? 0
         version = parseInt(version)
+        logDebug "Current version is #{version}"
         d.resolve version
       else
+        logDebug "Current version is 0"
         d.resolve 0
     d.promise
-

 runQuery = (config, client, query, version) ->
   d = Q.defer()
@@ -184,7 +187,7 @@
     " VALUES (0, #{version}, '#{moment().toISOString()}');"

   queryStrings.push cql
-  logDebug "Queries:", queryStrings
+  #logDebug "Queries:", queryStrings

   queries = _(queryStrings)
   .map (cql) ->
@@ -256,6 +259,8 @@
         migrate config, client, keyspace, migrationFiles, schemaVersion
       .then (version) ->
         code = 0
+      .catch (error) ->
+        logError "Migration Error", error
   .catch (error) ->
     logError "Error reading configuration file", error
   .finally ->


inject jQuery and lodash

inject jQuery and lodash

--- src/index.coffee
+++ src/index.coffee
@@ -1,7 +1,7 @@
-((root) ->
+((root, $, _) ->
   GMailUI = {}
   root.GMailUI = GMailUI
-
+
   class GMailUI.Helper
     @hover: (target, hoverClass) ->
       target.hover ((e) ->
@@ -548,4 +548,4 @@
         tooltip:   tooltip
       super GMailUI.PopupCheckbox.hoverClass, GMailUI.PopupCheckbox.selectedClass
       @setSelected selected
-) @
+) @, jQuery, _


fix sourcemaps generation

fix sourcemaps generation

--- src/index.coffee
+++ src/index.coffee
@@ -5,13 +5,16 @@
 esprima = require('esprima')
 escodegen = require('escodegen')
 PluginError = gutil.PluginError;
+G = {}

-fileList = {}
-definitions = ''
-packages = null
-rootPath = process.cwd()
-lineOffset = 1
-resultMap = null
+init = ->
+  G =
+    fileList: {}
+    definitions: ''
+    packages: null
+    rootPath: process.cwd()
+    lineOffset: 1
+    resultMap: null

 generateSourceMap = (file) ->
   ast = esprima.parse file.contents,
@@ -25,14 +28,16 @@
   JSON.parse astGen.map.toString()

 getRelativePath = (path) ->
-  for pPath in packages
-    base = rootPath + '/' + pPath + '/'
+  for pPath in G.packages
+    base = G.rootPath + '/' + pPath + '/'
     if path.indexOf(base) is 0
       return path.slice(base.length).slice(0, -extname(path).length)

   return ''

 compileSource = (file) ->
+  if file.isStream()
+    throw new PluginError 'gulp-stitch-sourcemap', ''
   name = getRelativePath file.path
   source = String file.contents
   exportList = []
@@ -40,54 +45,59 @@
   if matches
     exportList.push match.replace /exports\./, '' for match in matches

-  fileList[name] = exportList
+  G.fileList[name] = exportList

   # source map
-  lineOffset++
+  G.lineOffset++
   if file.sourceMap
     orig = new SourceMapConsumer file.sourceMap
     orig.eachMapping (m) =>
-      resultMap.addMapping
+      G.resultMap.addMapping
         generated:
-          line: m.generatedLine + lineOffset
+          line: parseInt(m.generatedLine) + G.lineOffset
           column: m.generatedColumn
         original:
-          line: m.originalLine or m.generatedLine
-          column: m.originalColumn or m.generatedColumn
+          line: m.originalLine
+          column: m.originalColumn
         source: file.sourceMap.sources[0]
         name: m.name
-    resultMap.setSourceContent file.sourceMap.sources[0], file.sourceMap.sourcesContent[0]
-
-  lineOffset += source.split('\n').length - 1
-  ", '#{name}': function(exports, require, module) {\n#{source}}"
+    G.resultMap.setSourceContent file.sourceMap.sources[0], file.sourceMap.sourcesContent[0]
+  G.lineOffset += source.split('\n').length
+  ", '#{name}': function(exports, require, module) {\n#{source}\n}"

 wrapSources = (sources)->
   result = "(function(/*!Stitch!*/){if (!this.require) { var modules = {}, cache = {}, require = function(name, root) { var path = expand(root, name), module = cache[path], fn; if (module) { return module.exports; } else if (fn = modules[path] || modules[path = expand(path, './index')]) { module = {id: path, exports: {}}; try { cache[path] = module; fn(module.exports, function(name) { return require(name, dirname(path)); }, module); return module.exports; } catch (err) { delete cache[path]; throw err; } } else { throw 'module \\'' + name + '\\' not found'; } }, expand = function(root, name) { var results = [], parts, part; if (/^\\.\\.?(\\/|$)/.test(name)) { parts = [root, name].join('/').split('/'); } else { parts = name.split('/'); } for (var i = 0, length = parts.length; i < length; i++) { part = parts[i]; if (part == '..') { results.pop(); } else if (part != '.' && part != '') { results.push(part); } } return results.join('/'); }, dirname = function(path) { return path.split('/').slice(0, -1).join('/'); }; this.require = function(name) { return require(name, ''); }; this.require.define = function(bundle) { for (var key in bundle) modules[key] = bundle[key];};}"

   result += "var a = {};"
-  for filePath,exportedNames of fileList
+  for filePath,exportedNames of G.fileList
     for exportedName in exportedNames
       if exportedName.match /^[a-z]+[a-z0-9]*$/i
         result += "a['#{exportedName}']='#{filePath}';"

   result += "this.require.exportsFileMap=a;return this.require.define;}).call(this)({\n#{sources}});"

-module.exports = (fileName, _packages) ->
-  if not _packages
+module.exports = (fileName, packages) ->
+  init()
+  if not packages
     throw new PluginError 'gulp-stitch-sourcemap', 'Missing packages option for gulp-stitch-sourcemap'
-  packages = _packages
-  resultMap = new SourceMapGenerator
+  G.packages = packages
+  G.resultMap = new SourceMapGenerator
     file: fileName

   return through.obj (file, enc, cb) ->
+    if file.isStream()
+      this.emit 'error', new PluginError('gulp-concat',  'Streaming not supported')
+      cb()
+      return
     file.sourceMap = generateSourceMap file if not file.sourceMap
-    definitions += compileSource file
+    G.definitions += compileSource file
     cb()
+
   , (cb) ->
     file = new gutil.File();
-    file.path = join(rootPath, fileName);
-    file.contents = new Buffer wrapSources definitions.slice(2)
-    file.sourceMap = JSON.parse(resultMap.toString())
+    file.path = join(G.rootPath, fileName);
+    file.contents = new Buffer wrapSources G.definitions.slice(2)
+    file.sourceMap = JSON.parse(G.resultMap.toString())
     this.push(file)
     cb()


Small update to the NationalRail script

Small update to the NationalRail script

--- src/scripts/nationalrail.coffee
+++ src/scripts/nationalrail.coffee
@@ -36,4 +36,4 @@
               else
                 msg.send "The #{info[1]} to #{info[2]} is #{/[^;]*$/.exec(info[3])[0].trim().toLowerCase()}"
         else
-          msg.send "I couldn't find trains from: #{trainFrom} to #{trainTo}"
+          msg.send "I couldn't find trains from: #{trainFrom} to #{trainTo}. Please make sure and use station codes (https://en.wikipedia.org/wiki/UK_railway_stations)"


Added context errors for unexpected tokens in preprocessor.

--- src/preprocessor.coffee
+++ src/preprocessor.coffee
@@ -35,7 +35,11 @@
           'TERM'
         else
           inspect c
-    throw new Error "Unexpected " + token
+    # This isn't perfect for error location tracking, but since we normally call this after a scan, it tends to work well.
+    lines = @ss.str.substr(0, @ss.pos).split(/\n/) || ['']
+    columns = if lines[lines.length-1]? then lines[lines.length-1].length else 0
+    context = pointToErrorLocation @ss.str, lines.length, columns
+    throw new Error "Unexpected #{token}\n#{context}"

   peek: -> if @context.length then @context[@context.length - 1] else null


deleted some blank spaces and useless console logs

--- src/smc-webapp/editor_chat.cjsx
+++ src/smc-webapp/editor_chat.cjsx
@@ -87,7 +87,7 @@

 {User} = require('./users')

-redux_name = redux_name = (project_id, path) ->
+redux_name = (project_id, path) ->
     return "editor-#{project_id}-#{path}"

 class ChatActions extends Actions
@@ -160,8 +160,8 @@

         @syncdb.update
             set :
-                history     : [{author_id: author_id, content:mesg, date:time_stamp}].concat(message.get('history').toJS())
-                editing     : message.get('editing').remove(author_id).toJS()
+                history : [{author_id: author_id, content:mesg, date:time_stamp}].concat(message.get('history').toJS())
+                editing : message.get('editing').remove(author_id).toJS()
             where :
                 date: message.get('date')
             is_equal: (a, b) => (a - 0) == (b - 0)
@@ -760,7 +760,7 @@
             {@list_messages()}
         </div>

-ChatRoom = ChatRoom = (name) -> rclass
+ChatRoom = (name) -> rclass
     displayName: "ChatRoom"

     reduxProps :
@@ -829,7 +829,6 @@

     button_off_click: ->
         @props.actions.set_is_preview(false)
-        console.log(ReactDOM.findDOMNode(@refs.input))
         ReactDOM.findDOMNode(@refs.input.refs.input).focus()

     button_on_click: ->


:lipstick:

:lipstick:

--- src/stdlib/child-process.coffee
+++ src/stdlib/child-process.coffee
@@ -14,8 +14,8 @@
       options.stderr = @bufferLines(options.stderr) if options.stderr

     $native.exec command, options, (exitStatus, stdout, stderr) ->
-      options.stdout(stdout) if options.stdout
-      options.stderr(stderr) if options.stderr
+      options.stdout?(stdout)
+      options.stderr?(stderr)
       try
         if exitStatus != 0
           deferred.reject({command, exitStatus})


Update ProjectMilestones.coffee

Update ProjectMilestones.coffee

--- src/Models/ProjectMilestones.coffee
+++ src/Models/ProjectMilestones.coffee
@@ -2,12 +2,29 @@
 Utils = require '../Utils'

 class ProjectMilestones extends BaseModel
-  list: (projectId, query, fn = null) =>
-    @debug "Projects::milestones()"
-     if 'function' is typeof query
-      fn = query
-      query = {}
-    @get "projects/#{Utils.parseProjectId projectId}/milestones", query, (data) => fn data if fn
+  list = (projectId, fn = null) ->
+    console.log 'DEPRECATED: milestone.list. Use milestone.all instead'
+    @all arguments...
+  all: (projectId, fn = null) =>
+    @debug "Projects::Milestones::all()"
+    params = {}
+    params.page ?= 1
+    params.per_page ?= 100
+
+    data = []
+    cb = (err, retData) =>
+      if err
+        return fn(retData || data) if fn
+      else if retData.length == params.per_page
+        @debug "Recurse Projects::Milestones::all()"
+        data = data.concat(retData)
+        params.page++
+        return @get "projects/#{Utils.parseProjectId projectId}/milestones", params, cb
+      else
+        data = data.concat(retData)
+        return fn data if fn
+
+    @get "projects/#{Utils.parseProjectId projectId}/milestones", params, cb

   show: (projectId, milestoneId, fn = null) =>
     @debug "Projects::milestone()"


Block: Added opts.padded, defaults to true.

--- src/elements/Block/Block.coffee
+++ src/elements/Block/Block.coffee
@@ -33,6 +33,9 @@
 		@addClass("appearance-"+@_appearance)
 		@addClass("cui-block-level-"+@_level)

+		if @_padded
+			@addClass("cui-block--padded")
+
 	initOpts: ->
 		super()
 		@addOpts
@@ -50,12 +53,14 @@
 				mandatory: true
 				default: 1
 				check: [1, 2, 3]
-
 			appearance:
 				deprectated: true
 				default: "normal"
 				mandatory: true
 				check: ["title","subtitle","normal"]
+			padded:
+				check: Boolean
+				default: true

 	readOpts: ->
 		super()


remove unused javascript

remove unused javascript

--- app/assets/javascripts/distributors.js.coffee
+++ app/assets/javascripts/distributors.js.coffee
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/


Show 7 operations in accounts/:id/show

Show 7 operations in accounts/:id/show

--- app/src/controllers/wallet/accounts/wallet_accounts_show_view_controller.coffee
+++ app/src/controllers/wallet/accounts/wallet_accounts_show_view_controller.coffee
@@ -35,7 +35,7 @@
   _updateOperations: ->
     operations = @getAccount().get 'operations'
     @view.emptyContainer.hide() if operations.length > 0
-    render 'wallet/operations/operations_table', {operations: operations.slice(0, 6)}, (html) =>
+    render 'wallet/operations/operations_table', {operations: operations.slice(0, 7)}, (html) =>
       @view.operationsList.html html

   getAccount: () ->


Warn if leaving review editor without saving

--- app/assets/javascripts/reviewEditor.js.coffee
+++ app/assets/javascripts/reviewEditor.js.coffee
@@ -35,13 +35,11 @@

     # Prepare feedback containers
     for category in @rubricEditor.feedbackCategories
-      #category.name = ' ' if !category.name? || category.name.length < 1
-
       feedbackHeight = Math.floor(100.0 / @rubricEditor.feedbackCategories.length) + "%"
       feedback = {
-        id: category.id,
-        title: category.name,
-        value: ko.observable(''),
+        id: category.id
+        title: category.name
+        value: ko.observable('')
         height: feedbackHeight
       }
       @feedback.push(feedback)
@@ -71,6 +69,11 @@
         phrase.highlighted(true)

     @grade(data['grade']) if data['grade']?
+
+    @grade.subscribe(=> @rubricEditor.saved = false)
+
+    for category in @feedback
+      category.value.subscribe((newValue) => @rubricEditor.saved = false )

   to_json: ->
     feedback = @feedback.map (fb) -> { category_id: fb.id, text: fb.value() }
@@ -109,6 +112,7 @@
     if @nextPage
       @nextPage.showTab()
     else
+      @rubricEditor.finish()
       $('#tab-finish-link').tab('show')

     window.scrollTo(0, 0)
@@ -170,12 +174,13 @@

   clickGrade: ->
     @criterion.setGrade(this) if @grade?
+    @rubricEditor.saved = false

 class @ReviewEditor

   constructor: () ->
-    @paused = ko.observable(true)
+    @saved = true
     @finishedText = ko.observable('')
     @finalizing = ko.observable(false)

@@ -192,6 +197,8 @@
     @gradingMode = 'none'
     @finalGrade = ko.observable()

+    $(window).bind 'beforeunload', =>
+      return "You have unsaved changes. Leave anyway?" unless @saved

   #
   # Loads the rubric by AJAX
@@ -316,11 +323,12 @@
       @averageGrade = ko.observable()

     ko.applyBindings(this)
-    @paused(false)
+
+    @finalGrade.subscribe(=> @saved = false )
+    @finishedText.subscribe(=> @saved = false )

     # Activate the finalizing tab
     $('#tab-finish-link').tab('show') if @finalizing()
-

   # Returns the review as JSON
@@ -357,6 +365,8 @@
       status = 'started'

     $('#review_status').val(status)
+
+    @saved = true

     return true


Fix model parameter comparison table

Fix model parameter comparison table

--- src/ext/modules/routines.coffee
+++ src/ext/modules/routines.coffee
@@ -270,24 +270,32 @@
     render_ frames, -> H2O.FramesOutput _, frames
     frames

-  #TODO rename
-  inspectMultimodelParameters = (models) -> ->
+  inspectParametersAcrossModels = (models) -> ->
     leader = head models
-    vectors = for i in [ 0 ... leader.parameters.length ]
+    vectors = for parameter, i in leader.parameters
       data = for model in models
         value = model.parameters[i].actual_value
-        if value? then value else undefined
+        switch parameter.type
+          when 'Key<Frame>', 'Key<Model>'
+            if value? then value.name else undefined
+          when 'VecSpecifier'
+            if value? then value.column_name else undefined
+          else
+            if value? then value else undefined
+
       switch parameter.type
-        when 'enum', 'Frame', 'string', 'string[]', 'byte[]', 'short[]', 'int[]', 'long[]', 'float[]', 'double[]'
+        when 'enum', 'Frame', 'string'
           createFactor parameter.label, TString, data
         when 'byte', 'short', 'int', 'long', 'float', 'double'
           createVector parameter.label, TNumber, data
+        when 'string[]', 'byte[]', 'short[]', 'int[]', 'long[]', 'float[]', 'double[]'
+          createList parameter.label, data, (a) -> if a then a else undefined
         when 'boolean'
           createList parameter.label, data, (a) -> if a then 'true' else 'false'
         else
           createList parameter.label, data

-    modelKeys = (model.key for model in models)
+    modelKeys = (model.key.name for model in models)

     createDataframe 'parameters', vectors, (sequence models.length), null,
       description: "Parameters for models #{modelKeys.join ', '}"
@@ -377,7 +385,6 @@
       createVector 'categorical_column_count', TNumber, [ output.categorical_column_count ]
       createVector 'iterations', TNumber, [ output.iterations ]
       createFactor 'model_category', TString, [ output.model_category ]
-
     ]

     createDataframe 'output', vectors, (sequence 1), null,
@@ -528,7 +535,7 @@
     algos = unique (model.algo for model in models)
     if algos.length is 1
       inspect_ models,
-        parameters: inspectMultimodelParameters models
+        parameters: inspectParametersAcrossModels models

     render_ models, -> H2O.ModelsOutput _, models


Fix URL encoding issue

Fix URL encoding issue

Google API complains if the URL is sent encoded with `encodeURIComponent`.
Sending the URL in plain-text works.

--- src/googl.coffee
+++ src/googl.coffee
@@ -20,7 +20,7 @@

     robot.respond /shorten (.+)/i, (res) ->
         data = JSON.stringify
-            longUrl: encodeURIComponent(res.match[1])
+            longUrl: res.match[1]

         params = {}
         params.key = GOOGLE_API_KEY if GOOGLE_API_KEY?


Logic for loaded URN construction.

Logic for loaded URN construction.

--- src/js/collection-editor.coffee
+++ src/js/collection-editor.coffee
@@ -138,6 +138,12 @@
           existing_versions = parseInt(data['rows'][0][0])
           latest_urn = "#{urn_prefix_matches[1]}.#{existing_versions + 1}"
           console.log "Latest URN: #{latest_urn}"
+          loaded_urn = urn_prefix_matches[1]
+          if (urn_prefix_matches[2]?) && (parseInt(urn_prefix_matches[2].substring(1)) <= existing_versions)
+            loaded_urn += urn_prefix_matches[2]
+          else
+            loaded_urn += ".#{existing_versions}"
+          console.log "Loading data from: #{loaded_urn}"
           callback(latest_urn)
         else # invalid URN passed in, strip and retry
           console.log "No existing versions for passed URN, constructing latest URN from scratch"


Tweaks the configuration to appropriately separate pure environment variables from runtime configurable settings

--- source/server.coffee
+++ source/server.coffee
@@ -11,15 +11,9 @@
       log: {
         enabled: ['SPACE_ES_LOG_ENABLED', false, 'bool']
       }
-      commits: {
-        mongoUrl: ['SPACE_ES_COMMITS_MONGO_URL', '', 'string']
-        mongoOplogUrl: ['SPACE_ES_COMMITS_MONGO_OPLOG_URL', '', 'string']
-        collectionName: ['SPACE_ES_COMMITS_COLLECTION_NAME', 'space_eventSourcing_commits', 'string']
-      }
       snapshotting: {
         enabled: ['SPACE_ES_SNAPSHOTTING_ENABLED', true, 'bool']
         frequency: ['SPACE_ES_SNAPSHOTTING_FREQUENCY', 10, 'int']
-        collectionName: ['SPACE_ES_SNAPSHOTTING_COLLECTION_NAME', 'space_eventSourcing_snapshots', 'string']
       }
     }
   })
@@ -42,7 +36,6 @@
     @_setupLogging() # if @Configuration.eventSourcing.log.enabled

   afterInitialize: ->
-
     @_setupCommitsCollection()
     @_setupSnapshotting() if @Configuration.eventSourcing.snapshotting.enabled
     @commitPublisher = @injector.get('Space.eventSourcing.CommitPublisher')
@@ -65,7 +58,8 @@
     if Space.eventSourcing.commitsCollection?
       CommitsCollection = Space.eventSourcing.commitsCollection
     else
-      CommitsCollection = new @mongo.Collection @Configuration.eventSourcing.commits.collectionName, @_collectionOptions()
+      commitsName = Space.getenv('SPACE_ES_COMMITS_COLLECTION_NAME', 'space_eventSourcing_commits')
+      CommitsCollection = new @mongo.Collection commitsName, @_collectionOptions()
       CommitsCollection._ensureIndex { "sourceId": 1, "version": 1 }, unique: true
       Space.eventSourcing.commitsCollection = CommitsCollection
     @injector.map('Space.eventSourcing.Commits').to CommitsCollection
@@ -74,7 +68,8 @@
     if Space.eventSourcing.snapshotsCollection?
       SnapshotsCollection = Space.eventSourcing.snapshotsCollection
     else
-      SnapshotsCollection = new @mongo.Collection @Configuration.eventSourcing.snapshotting.collectionName, @_collectionOptions()
+      snapshotsName = Space.getenv('SPACE_ES_SNAPSHOTTING_COLLECTION_NAME', 'space_eventSourcing_snapshots')
+      SnapshotsCollection = new @mongo.Collection snapshotsName, @_collectionOptions()
       SnapshotsCollection._ensureIndex { "snapshot.state": 1, "snapshot.version": 1 }, unique: true
       Space.eventSourcing.snapshotsCollection = SnapshotsCollection
     snapshotter = new Space.eventSourcing.Snapshotter {
@@ -88,16 +83,16 @@
   _collectionOptions: ->
     if @_externalMongo()
       if @_externalMongoNeedsOplog()
-        driverOptions = { oplogUrl:  @Configuration.eventSourcing.commits.mongoOplogUrl }
+        driverOptions = { oplogUrl:  Space.getenv('SPACE_ES_COMMITS_MONGO_OPLOG_URL') }
       else
         driverOptions = {}
-      { mongoUrl } = @Configuration.eventSourcing.commits
+      mongoUrl = Space.getenv('SPACE_ES_COMMITS_MONGO_URL')
       return _driver: new @mongoInternals.RemoteCollectionDriver(mongoUrl, driverOptions)
     else
       return {}

   _externalMongo: ->
-    true if @Configuration.eventSourcing.commits.mongoUrl?.length > 0
+    true if Space.getenv('SPACE_ES_COMMITS_MONGO_URL', '').length > 0

   _externalMongoNeedsOplog: ->
-    true if @Configuration.eventSourcing.commits.mongoOplogUrl?.length > 0+    true if Space.getenv('SPACE_ES_COMMITS_MONGO_OPLOG_URL', '').length > 0


add metric for getdoc bytes

add metric for getdoc bytes

--- services/document-updater/app/coffee/RedisManager.coffee
+++ services/document-updater/app/coffee/RedisManager.coffee
@@ -41,6 +41,7 @@
 			logger.error {err: error, doc_id: doc_id, docLines: docLines}, error.message
 			return callback(error)
 		docHash = RedisManager._computeHash(docLines)
+		metrics.summary "redis.setDoc", docLines.length, {status: "set"}
 		logger.log {project_id, doc_id, version, docHash, pathname, projectHistoryId}, "putting doc in redis"
 		RedisManager._serializeRanges ranges, (error, ranges) ->
 			if error?


updated strength and weight test to with a chained expression

--- spec/compiler.coffee
+++ spec/compiler.coffee
@@ -60,13 +60,14 @@

   describe 'with a simple statement and a constraint strength and weight', ->
     source = """
-    4 == 5 !strong:10
+    4 == 5 == 6 !strong:10
     """
     expect =
       selectors: []
       vars: []
       constraints: [
         ['eq', ['number', 4], ['number', 5], 'strong', 10]
+        ['eq', ['number', 5], ['number', 6], 'strong', 10]
       ]
     result = null
     it 'should be able to produce a result', ->


Expect newer protocol version

Expect newer protocol version

--- spec/protocol.coffee
+++ spec/protocol.coffee
@@ -104,9 +104,9 @@
         done()
     it 'type should be "msgflo"', ->
       chai.expect(info.type).to.equal "msgflo"
-    it 'protocol version should be "0.4"', ->
+    it 'protocol version should be "0.7"', ->
       chai.expect(info.version).to.be.a "string"
-      chai.expect(info.version).to.equal "0.4"
+      chai.expect(info.version).to.equal "0.7"
     describe 'capabilities', ->
       it 'should be an array', ->
         chai.expect(info.capabilities).to.be.an "array"


Fixed wallspec

Fixed wallspec

--- spec/wallspec.coffee
+++ spec/wallspec.coffee
@@ -1,5 +1,5 @@
 requirejs.config({
-    baseUrl: '../',
+  #baseUrl: '../',
 })

 require(['game', 'walls', 'synchronizedtime'], (Game, Walls, SynchronizedTime) ->
@@ -13,4 +13,4 @@
     it "detects a collision", ->
       SynchronizedTime.setTimeForTesting(0)
       game.start()
-
+)


Fix bug: first option was always selected

--- webapp/coffee/vis.coffee
+++ webapp/coffee/vis.coffee
@@ -429,6 +429,7 @@
   # we are storing the current text in the search component
   # just to make things easy
   key = decodeURIComponent(location.search).replace("?","")
+  key = key.replace("/","")
   text = texts.filter((t) -> t.key == key)[0]

   # default to the first text if something gets messed up


Use paddingTop/Left, as this also works in firefox

--- app/js/jail_iframe/classes/content_box.coffee
+++ app/js/jail_iframe/classes/content_box.coffee
@@ -2,7 +2,7 @@
   $element = $(element)
   offset = $element.offset()

-  top: offset.top + parseInt window.getComputedStyle(element)['padding-top']
-  left: offset.left + parseInt window.getComputedStyle(element)['padding-left']
+  top: offset.top + parseInt window.getComputedStyle(element).paddingTop
+  left: offset.left + parseInt window.getComputedStyle(element).paddingLeft
   width: $element.width()
   height: $element.height()


Ive removed the transcription call for projects (which has now closed) and updated the biomedical call for projects (#3450)

--- app/pages/get-involved/call-for-projects.cjsx
+++ app/pages/get-involved/call-for-projects.cjsx
@@ -5,61 +5,36 @@
 counterpart.registerTranslations 'en',
   callForProjects:
    pageContent: '''
-    ## Call for Project Proposals
+    ## Call for Biomedical Project Proposals
+    Would your research benefit from the involvement of thousands of volunteers? We are currently seeking proposals for biomedical projects to be developed as part of the Zooniverse platform. The Zooniverse is the world’s largest and most successful online platform for crowd-sourced research; we currently have over 1.5 million registered volunteers working in collaboration with professional researchers on more than 50 research projects across a range of disciplines, from physics to biology.

-    Would your research benefit from the involvement of hundreds or even thousands of volunteers? We are currently seeking proposals in two different disciplines: biomedical research, and text transcription projects in the humanities or Galleries/Libraries/Archives/Museums (GLAM) sector, to be developed as part of the Zooniverse platform.
-
-    Zooniverse is the world’s largest and most successful online platform for crowd-sourced research; we currently have over 1.5 million registered volunteers working in collaboration with professional researchers on more than 70 research projects across a range of disciplines, from astronomy to biology.
-
-    Using our unique [Project Builder](/lab) you can now build your own Zooniverse project for free with a set of tried and tested tools, including multiple-choice questions and region marking or drawing tools. If we don’t yet offer the tools you would need, please propose your project below. We are particularly interested in developing projects that extend the functionality of our platform.
+    Using our unique [Project Builder](/lab) you can create your own Zooniverse project for free with a set of tried and tested tools, including multiple-choice questions and region marking or drawing tools. If we don’t yet offer the tools you need, please propose your project below; we are particularly interested in developing novel projects that extend the functionality of our platform.

-    ## Call for Biomedical Project Proposals
+    ### Project Selection
+    We are looking for biomedical projects that will help us expand the functionality of the Zooniverse and build on the selection of tools available to researchers via our platform. Projects may involve a processing task applied to images, graphs, videos or another data format, data collection, or a combination of the two. Successful projects will be developed and hosted by the Zooniverse team, in close collaboration with the applicants.

-    You can see examples of our current biomedical projects at Zooniverse.org. These include [Microscopy Masters](https://www.zooniverse.org/projects/jbrugg/microscopy-masters), where citizen scientists classify cryo-electron microscopy images to advance understanding of protein and virus structure, and [Worm Watch Lab](https://www.wormwatchlab.org/), which aims to improve understanding of the relationship between genes and behaviour.
+    Examples of our current biomedical projects include [Microscopy Masters](https://www.zooniverse.org/projects/jbrugg/microscopy-masters), where volunteers classify cryo-electron microscopy images to advance understanding of protein and virus structure, and [Worm Watch Lab](https://www.wormwatchlab.org/), which aims to improve understanding of the relationship between genes and behaviour.

-    ### Project Selection
-
-    Successful biomedical projects will be selected from responses to this call, and will be developed and hosted by the Zooniverse team in close collaboration with the applicants. Project proposals are expected to clearly describe the research question addressed and intended research outputs. Those writing proposals should review the existing range of Zooniverse projects and be confident that their project cannot be built with the existing tools.
+

     ### Selection Criteria:

-    1.	Alignment with biomedical research (long-term aim of research is to improve human health outcomes).
-    2.	Merit and usefulness of the data expected to result from the project.
+    1.	Projects extending the capability of the Zooniverse platform or serving as case studies for crowdsourcing in new areas are encouraged.
+    2.  Alignment with biomedical research (long-term aim of research is to improve human health outcomes).
+    3.	Merit and usefulness of the data expected to result from the project.

-    3.	Novelty of the problem; projects which require extending the capability of the Zooniverse platform or serve as case studies for crowdsourcing in new areas are encouraged.
+
+

     ### Deadline

-    Applicants can submit project proposals until January 31st, 2017.
-
-
-    [SUBMIT A BIOMEDICAL PROPOSAL](https://goo.gl/forms/uUGdO5CpWDNFE5Uz2)
+    Project proposals are accepted on a rolling basis. Applications will be reviewed at the beginning of each month.

-    ## Call for Text Transcription Project Proposals
-
-    You can see examples of our current text transcription projects at [Zooniverse.org](http://zooniverse.org/). These include [AnnoTate](https://anno.tate.org.uk/#/), where volunteers transcribe twentieth-century British artists’ sketchbooks and papers; [Decoding the Civil War](https://www.decodingthecivilwar.org), in which volunteers transcribe telegrams from the American Civil War, and [Shakespeare’s World](https://www.shakespearesworld.org/#/), where volunteers transcribe documents produced in and around Shakespeare’s lifetime (sixteenth and seventeenth century). The resulting datasets will be incorporated into the Tate, Huntington, and Folger Shakespeare Library’s respective content management systems (CMS), and will also be used for research by humanities specialists. We are particularly keen to work on projects where the data can be incorporated into an existing CMS and used for research purposes.
-
-    ### Project Selection
-
-    Using our free [Project Builder](/lab) you can now build your own Zooniverse project with a set of tried and tested tools such as questions (yes/no; multiple choice) and region marking or drawing tools. More recently we’ve released transcription tools, but are keen to expand their functionality. To that end, we are seeking one or more transcription projects that will help us test and expand the remit of our text transcription tools. These projects need not replicate the functionality of AnnoTate, Decoding the Civil War or Shakespeare’s World. They could involve letter-by-letter transcription using a bespoke keyboard, as in Ancient Lives, the first Zooniverse transcription project, which is now retired.
-
-    ### Selection Criteria:
-
-    1. 	We are looking for projects that harness crowdsourced text transcription for the purposes of unlocking big data currently trapped in GLAM sources that cannot be machine read, and for which human effort is truly necessary.
-    2. 	The data extracted must have an audience or usefulness, be this to academic researchers, members of the public or both.
-    3. 	Your material must be imaged or there must be funds available to image it. We are not able to fund imaging.
-    4. 	Project teams need clear plans for how to make the crowdsourced data openly and publicly available, ideally through a content management system (CMS) or site hosted and maintained by your institution.
-    5. 	We will not accept projects for which the material has previously been edited, i.e. the novels of Jane Austen.
-
-    ### Deadline
-    Applicants can submit project proposals until January 31st, 2017.
-
-
-    [SUBMIT A TEXT TRANSCRIPTION PROPOSAL](https://goo.gl/forms/VZX5IdlK59B1eBez2)
+    [SUBMIT A BIOMEDICAL PROPOSAL](https://goo.gl/forms/uUGdO5CpWDNFE5Uz2)

     '''


Fixed bug with GET action

Fixed bug with GET action

--- src/explorer/explorerController.coffee
+++ src/explorer/explorerController.coffee
@@ -40,7 +40,11 @@

   getAction: (option) ->
     switch option
-      when 'GET' then @$location.path('explorer/' + encodeURIComponent(@$scope.url) + '/GET')
+      when 'GET'
+        if @$location.path() is '/explorer/' + encodeURIComponent(@$scope.url) + '/GET'
+          @$route.reload()
+        else
+          @$location.path('explorer/' + encodeURIComponent(@$scope.url) + '/GET')
       when 'DELETE' then @$location.path('explorer/' + encodeURIComponent(@$scope.url) + '/DELETE')
       when 'POST', 'PUT', 'PATCH' then @openActionModal(option)
       when 'OPTIONS' then @$scope.optionsAlert = "The OPTIONS request for this URL returned the verbs that you see as buttons below"


fix(file-uploads): Critical fix to bad method call to session.draft instead of session.draft()

--- src/flux/tasks/file-upload-task.coffee
+++ src/flux/tasks/file-upload-task.coffee
@@ -104,7 +104,7 @@

     DraftStore = require '../stores/draft-store'
     DraftStore.sessionForClientId(@messageClientId).then (session) =>
-      files = _.clone(session.draft.files) ? []
+      files = _.clone(session.draft().files) ? []
       files.push(file)
       session.changes.add({files})
       session.changes.commit().then ->


Bug#184: Fixed dependency injection on wiki diff filter

--- app/coffee/filters.coffee
+++ app/coffee/filters.coffee
@@ -91,4 +91,4 @@
 module.filter("truncate", TruncateFilter)
 module.filter("onlyVisible", OnlyVisibleFilter)
 module.filter("sizeFormat", SizeFormatFilter)
-module.filter("diff", DiffFilter)
+module.filter("diff", ['$sce', DiffFilter])


Fixed an include instead of require

Fixed an include instead of require

--- app/lib/soundcloud.coffee
+++ app/lib/soundcloud.coffee
@@ -1,3 +1,3 @@
-http = include( 'http' )
+http = require( 'http' )

 fetchRecs = ( url, count ) ->


Update zh-HANS.coffee

Update zh-HANS.coffee

--- app/locale/zh-HANS.coffee
+++ app/locale/zh-HANS.coffee
@@ -701,36 +701,37 @@
     more_info_3: "是个与其他使用CodeCombat的教育工作者联系的良好平台。"

 #  teachers_quote:
-#    name: "Quote Form"
-#    title: "Request a Quote"
-#    subtitle: "Get CodeCombat in your classroom, club, school or district!"
-#    email_exists: "User exists with this email."
-#    phone_number: "Phone number"
-#    phone_number_help: "Where can we reach you during the workday?"
-#    role_label: "Your role"
-#    role_help: "Select your primary role."
-#    tech_coordinator: "Technology coordinator"
-#    advisor: "Advisor"
-#    principal: "Principal"
-#    superintendent: "Superintendent"
-#    parent: "Parent"
-#    organization_label: "Name of School/District"
-#    city: "City"
-#    state: "State"
-#    country: "Country"
-#    num_students_help: "How many do you anticipate enrolling in CodeCombat?"
-#    education_level_label: "Education Level of Students"
-#    education_level_help: "Choose as many as apply."
-#    elementary_school: "Elementary School"
-#    high_school: "High School"
-#    please_explain: "(please explain)"
-#    middle_school: "Middle School"
-#    college_plus: "College or higher"
-#    anything_else: "Anything else we should know?"
-#    thanks_header: "Thanks for requesting a quote!"
-#    thanks_p: "We'll be in touch soon. Questions? Email us:"
-#    thanks_anon: "Login or sign up with your account below to access your two free enrollments (we’ll notify you by email when they have been approved, which usually takes less than 48 hours). As always, the first hour of content is free for an unlimited number of students."
-#    thanks_logged_in: "Your two free enrollments are pending approval. We’ll notify you by email when they have been approved (usually within 48 hours). As always, the first hour of content is free for an unlimited number of students."
+    name: "申请表"
+    title: "申请一个班级"
+    subtitle: "在教室，兴趣小组，学校或社区使用CodeCombat"
+    email_exists: "此邮箱地址已被占用"
+    phone_number: "联系电话"
+    phone_number_help: "我们在何时可以联系您？"
+    role_label: "您的职务"
+    role_help: "选择您主要的职务"
+    tech_coordinator: "技术指导"
+    advisor: "指导教授"
+    principal: "校长"
+    superintendent: "教育负责人"
+    parent: "家长"
+    organization_label: "学校名称或地区"
+    city: "市"
+    state: "州／省"
+    country: "国家"
+    num_students_help: "您期望有多少学生参与CodeCombat?"
+    education_level_label: "学生受教育程度"
+    education_level_help: "可多选"
+    elementary_school: "小学"
+    high_school: "初中"
+    please_explain: "其它"
+    middle_school: "高中"
+    college_plus: "大学或以上"
+    anything_else: "其它我们应该知道的？"
+    thanks_header: "谢谢您的申请表"
+    thanks_p: "我们将尽快联系您。有问题？我们的电子邮件："
+    thanks_anon: "登陆您的账户，获取两个免费的名额（当您的申请被批准，我们将通过邮件通知您，这个过程通常不会超过48个小时）。通常，第一个小时的内容是免费的，不限学生数量。"
+    thanks_logged_in: "您有两个免费名额有待批准。您的申请被批准，我们将通过邮件通知您（通常在48小时之内）。并且通常，第一个小时的内容是免费的，不限学生数量。"
+

   versions:
     save_version_title: "保存新版本"


Properly calculate end offset after block split

--- src/trix/models/document.coffee
+++ src/trix/models/document.coffee
@@ -167,8 +167,9 @@
       startBlock = @getBlockAtIndex(start.index)
       if (start.offset = startBlock.findLineBreakInDirectionFromPosition("backward", start.offset))?
         @insertBlockBreakAtLocationRange(Trix.LocationRange.forLocationWithLength(start, 1))
+        end.index += 1
+        end.offset -= @getBlockAtIndex(start.index).getLength()
         start.index += 1
-        end.index += 1
       start.offset = 0

       endBlock = @getBlockAtIndex(end.index)


add scale support to Helper.tween

add scale support to Helper.tween

--- src/3d/Helper.coffee
+++ src/3d/Helper.coffee
@@ -437,6 +437,9 @@
     options.position.rX = options.mesh.rotation.x
     options.position.rY = options.mesh.rotation.y
     options.position.rZ = options.mesh.rotation.z
+    options.position.sX = options.mesh.scale.x
+    options.position.sY = options.mesh.scale.y
+    options.position.sZ = options.mesh.scale.z

     if options.relative
       for e in ['x', 'y', 'z']
@@ -449,6 +452,11 @@
           options.target[e] += options.mesh.rotation[e.toLowerCase()[1]]
         else
           options.target[e] = options.mesh.rotation[e.toLowerCase()[1]]
+      for e in ['sX', 'sY', 'sZ']
+        if options.target[e]?
+          options.target[e] += options.mesh.scale[e.toLowerCase()[1]]
+        else
+          options.target[e] = options.mesh.scale[e.toLowerCase()[1]]
     else
       options.target.x ?= options.position.x
       options.target.y ?= options.position.y
@@ -456,6 +464,9 @@
       options.target.rX ?= options.position.rX
       options.target.rY ?= options.position.rY
       options.target.rZ ?= options.position.rZ
+      options.target.sX ?= options.position.sX
+      options.target.sY ?= options.position.sY
+      options.target.sZ ?= options.position.sZ

     throw new Error('target same as position') if options.position == options.target

@@ -464,6 +475,7 @@
       .onUpdate( ->
         options.mesh.position.set(@x, @y, @z)
         options.mesh.rotation.set(@rX, @rY, @rZ)
+        options.mesh.scale.set(@sX, @sY, @sZ)
       )
     tween.delay(options.delay) if options.delay != 0
     tween


default animation function

default animation function

--- src/index.coffee
+++ src/index.coffee
@@ -2,8 +2,6 @@

   constructor: (source, options = { }) ->
     return new Oscilloscope source, options unless this instanceof Oscilloscope
-
-    @drawRequest = 0

     unless source instanceof AudioNode
       throw new Error "Signal must be an AudioNode"
@@ -15,26 +13,24 @@
       source.connect @analyser

     @analyser.fftSize = options.fftSize if options.fftSize;  # range 32 to 32768
+    @timeDomain = new Uint8Array(@analyser.frequencyBinCount)
+    @drawRequest = 0

-    @timeDomain = new Uint8Array(@analyser.frequencyBinCount)
-
-    console.log('fft', @analyser.fftSize)
-    console.log('bincount', @analyser.frequencyBinCount)
+  # begin default signal animation
+  animate: (@ctx, x0, y0, width, height) =>
+    return if @drawRequest
+    do drawLoop = =>
+      @ctx.clearRect 0, 0, @ctx.canvas.width, @ctx.canvas.height
+      @draw @ctx, x0, y0, width, height
+      @drawRequest = window.requestAnimationFrame drawLoop

-
-  # todo: make auto start/stop (after babel)
-  # begin default signal animation
-  animate: ->
-    return if @drawRequest
-    @drawRequest = window.requestAnimationFrame @_drawSignal
-
-  # todo: stop signal animation
-  stop: ->
+  # stop default signal animation
+  stop: =>
     cancelAnimationFrame @drawRequest
     @drawRequest = 0
-    #@drawContext.clearRect 0, 0, @canvas.width, @canvas.height
+    @ctx.clearRect 0, 0, @ctx.canvas.width, @ctx.canvas.height

-  # draw signal animation
+  # draw signal
   draw: (ctx, x0, y0, width, height) =>
     # TODO: maybe cache values
     x0 = x0 || 0
@@ -42,9 +38,8 @@
     width = width || (ctx.canvas.width - x0)
     height = height || (ctx.canvas.height - y0)

+    @analyser.getByteTimeDomainData @timeDomain
     step = width / @timeDomain.length
-
-    @analyser.getByteTimeDomainData @timeDomain

     ctx.beginPath()
     # drawing loop (skipping every second record)


Avoid silently ignoring error when JSON cannot be parsed, close #7

--- src/index.coffee
+++ src/index.coffee
@@ -48,13 +48,17 @@
         callback null, yaml.load(rawTranslations)
       catch e
         callback e
+    when '.json'
+      try
+        callback null, JSON.parse(rawTranslations)
+      catch e
+        callback e
     else
       callback {message: 'unknown format'}

 loadResources = (locale, options, callback) ->
   file = path.join(options.localesPath, options.localeFile).replace('__lng__', locale)
   extension =  path.extname file
-  return callback(null) if extension == '.json'
   fs.readFile file, options.encoding, (err, data) ->
     return callback(err) if err
     parseTranslations extension, data, callback


Fix debug env.

Fix debug env.

--- src/index.coffee
+++ src/index.coffee
@@ -3,7 +3,8 @@
 initSkeleton = require 'init-skeleton'

 start = ->
-  process.env.DEBUG = 'brunch:*' if arguments[1]?.debug
+  isDebug = arguments[1]?.debug or arguments[2]?.debug
+  process.env.DEBUG = 'brunch:*' if isDebug
   (require './watch').apply null, arguments

 module.exports = {


Export modules in a lazy, but tern-compatible way.

--- src/index.coffee
+++ src/index.coffee
@@ -1,5 +1,6 @@
-for mod in ['run', 'utils', 'vm', 'watch', 'walk']
-  do (mod) ->
-    Object.defineProperty module.exports, mod,
-      enumerable: true
-      get: -> require "./#{mod}"
+Object.defineProperties module.exports,
+  run:   enumerable: true, get: -> require './run'
+  utils: enumerable: true, get: -> require './utils'
+  vm:    enumerable: true, get: -> require './vm'
+  walk:  enumerable: true, get: -> require './walk'
+  watch: enumerable: true, get: -> require './watch'


Sorted options.

Sorted options.

--- src/index.coffee
+++ src/index.coffee
@@ -235,11 +235,11 @@
   program
     .version moduleVersion
     .usage '[options] <config_file>'
+    .option '-d, --debug', 'Increase verbosity and error detail'
+    .option '-h, --hosts <hosts>', 'A comma separated list of cassandra hosts', parseCassandraHosts
+    .option '-k, --keyspace <keyspace>', 'Cassandra keyspace used for migration and schema_version table'
     .option '-q, --quiet', 'Silence non-error output (default is false)'
-    .option '-d, --debug', 'Increase verbosity and error detail'
-    .option '-k, --keyspace <keyspace>', 'Cassandra keyspace used for migration and schema_version table'
     .option '-t, --target-version <version>', 'Maximum migration version to apply (default runs all migrations)'
-    .option '-h, --hosts <hosts>', 'A comma separated list of cassandra hosts', parseCassandraHosts
     .parse(process.argv)

   configFile = _(program.args).last()


feat: expose requestAsync with max retries instead of the basic one

--- src/index.coffee
+++ src/index.coffee
@@ -8,7 +8,7 @@
   Processors:
     DeadLetterProcessor: require "./processors/deadletter.processor"
     RequestProcessor: require "./processors/request.processor"
-    RequestAsyncProcessor: require "./processors/request.async.processor"
+    RequestAsyncProcessor: require "./processors/requestWithRetries.async.processor"
     JobProcessor: require "./processors/job"
     MaxRetriesProcessor: require "./processors/maxRetries.processor"
   Exceptions:


Check if connected on bootup

Check if connected on bootup

--- src/index.coffee
+++ src/index.coffee
@@ -17,12 +17,12 @@
     return callback()

   onConfig: (device, callback=->) =>
-    desiredState = _.get device, 'desiredState', {}
-    return callback() if _.isEmpty desiredState
-
     @_computeState (error, state) =>
       return callback error if error
       return @_emitNoClient {state}, callback unless state.hasClient
+
+      desiredState = _.get device, 'desiredState', {}
+      return callback() if _.isEmpty desiredState

       @_handleDesiredState desiredState, (error) =>
         if error


Stop app crashing if monster not found

Stop app crashing if monster not found

--- src/index.coffee
+++ src/index.coffee
@@ -73,16 +73,16 @@
                 # Lets have a string
                 monsterStr = monster.toString()

+                # Setup an object we can send back to the client
+                re = data.body
+
                 # Was monster found?
                 if monsterStr.match 'unknown monster:'
                     # Return a null value
-                    monsterStr = null
-
-                # Setup an object we can send back to the client
-                re = data.body
-
-                # Add the monster and render the console escape codes to html
-                re.monster = ansi_up.ansi_to_html monsterStr
+                    re.monster = null
+                else
+                    # Add the monster and render the console escape codes to html
+                    re.monster = ansi_up.ansi_to_html monsterStr

                 # Cache the result
                 cache[query] = re


Ensured that the karma filename is unique across all projects

--- src/index.coffee
+++ src/index.coffee
@@ -52,7 +52,10 @@
 framework = (files, config={}) ->
   # Create an empty temp file for the global dependency bundle and add it to the
   # Karma files list.
-  tmp = path.join (if os.tmpdir then os.tmpdir() else os.tmpDir()), 'karma-browerify.js'
+  # MUST create a unique name else different projects can interfer with each other.
+  fileName = __dirname.replace(/[\\\/]/g, '_').replace('_node_modules_karma-browserify_lib', '');
+  fileName = fileName + '_karma-browerify.js';
+  tmp = path.join (if os.tmpdir then os.tmpdir() else os.tmpDir()), fileName
   fs.writeFileSync tmp, ''
   files.unshift pattern: tmp, included: true, served: true, watched: true


Added `update` action (PUT).

Added `update` action (PUT).

--- src/index.coffee
+++ src/index.coffee
@@ -165,6 +165,27 @@
       obj = @instance2json(item)
       res.json(obj)

+  action_update: (req, res) ->
+    format = req.format or @options.defaultFormat
+    @traceAction(req, 'update', "#{@_mount_info.url_prefix}:#{@idParam} format:#{format}")
+    id = req.params[@idParam]
+    @findOne req, id, (err, item) =>
+      # console.log("find returned err:", err, "  item:", item)
+      return res.send(500, { error: "#{err}", id: id })  if err
+      # return res.json(500, { error: "#{err}", id: id })  if err
+      if item is null
+        return res.send(404, { error: "Resource not found", id: id })
+        # return res.json(404, { error: "Resource not found", id: id })
+      @_update_instance_from_body_values(req, item)
+      item.save (err) =>
+        return res.send(500, { error: "#{err}" })  if err
+        #console.log("created #{@name} with id:#{item.id}")
+        if (req.body._format? and req.body._format == 'html') or (format == 'html')
+          return res.redirect @_mount_info.url_prefix + "/#{item.id}" + ".html"
+        else
+          obj = @instance2json(item)
+          return res.json(obj)
+
   action_create: (req, res) ->
     format = req.format or @options.defaultFormat
     @traceAction(req, 'create', "#{@_mount_info.url_prefix} format:#{format}")
@@ -286,6 +307,9 @@
     middleware = @options.middleware?.show or []
     app.get @_mount_info.url_prefix + "/:#{@idParam}.:format?", middleware, bind(@action_show, @)

+    middleware = @options.middleware?.update or []
+    app.put @_mount_info.url_prefix + "/:#{@idParam}.:format?", middleware, bind(@action_update, @)
+
     middleware = @options.middleware?.delete or []
     app.delete @_mount_info.url_prefix + "/:#{@idParam}", middleware, bind(@action_delete, @)
     @


Remove now-unnecessary load call

Remove now-unnecessary load call

--- scripts/gh-book/opf-file.coffee
+++ scripts/gh-book/opf-file.coffee
@@ -44,8 +44,6 @@
         # HACK: `?` is because `inherits/container.add` calls `trigger('change')`
         setNavModel(options)

-      @load()
-

     _loadComplex: (fetchPromise) ->
       fetchPromise


added allow-risky

added allow-risky

--- src/beautifiers/php-cs-fixer.coffee
+++ src/beautifiers/php-cs-fixer.coffee
@@ -24,6 +24,7 @@
       "fix"
       "--rules=#{options.rules}" if options.rules
       "--config=#{configFile}" if configFile
+      "--allow-risky=#{options.allowRisky}" if options.allowRisky
       "--using-cache=no"
     ]
     if version is 1


Remove request loggin

Remove request loggin

--- app/src/background.coffee
+++ app/src/background.coffee
@@ -83,7 +83,6 @@
     req = request.request.command
     data = request.request
   payload = {}
-  console.log("Request ", request);
   switch req
     when 'ping'
       window.externalSendResponse { command: "ping", result: true }


Added serveral utility classes to ease the process of writing build tasks

--- dist/buildscripts/src/tools.coffee
+++ dist/buildscripts/src/tools.coffee
@@ -10,15 +10,18 @@
 after = exports['after'] = (s, what) -> s[(s.indexOf(what) + what.length)...]
 divide = exports['divide'] = (s, what) -> [before(s, what), after(s, what)]

+prompt.start()
 promptBool = exports['promptBool'] = ([msg, def]..., callback) ->
+    console.log 'anus'
     _cb = (error, result) ->
-        if error?
-            callback(error, false)
-        else
-            val = result.val.toLowerCase() == 'y'
-            callback(null, val)
+        #prompt.stop()
+        _.defer ->
+            if error?
+                callback(error, false)
+            else
+                val = result.val.toLowerCase() == 'y'
+                callback(null, val)

-    prompt.start()
     prompt.get({
         properties: {
             val: {
@@ -28,15 +31,61 @@
         }
     }, _cb)

-Build = exports['Build'] = class
+exports['Build'] = class Build
     constructor: ->
         null

     execute: (callback) ->
-        null
+        _.defer(_.partial(callback, null, null))

-    confirm: (callback) ->
-        _.defer(_.partial(callback, null, true))
+    confirm: ([msg, def=no]..., callback) ->
+        promptBool msg, def, callback
+
+exports['Task'] = class Task extends Build
+    constructor: ->
+        @promptMessage = 'perform this task?'
+        @promptDefault = no
+        @promptResult = null
+
+    perform: (callback) ->
+        super( callback )
+
+    confirm: ( cb ) ->
+        if not @promptResult?
+            callback = (error, value) =>
+                if error?
+                    cb(error, null)
+                else
+                    @promptResult = value
+                    cb(error, value)
+            super(@promptMessage, @promptDefault, callback)
+        else
+            _.defer(_.partial(cb, null, @promptResult))
+
+    execute: ( cb ) ->
+        @confirm (error, perform) =>
+            if error?
+                cb error, null
+            else if perform
+                @perform cb
+            else
+                cb(null, null)
+
+exports['Batch'] = class Batch extends Task
+    constructor: (tasks=[]) ->
+        super()
+        @tasks = tasks
+
+    perform: ( cb ) ->
+        self = this
+        conf = (t)->(f)->t.confirm(f)
+        perf = (t)->(f)->t.execute(f)
+
+        confs = (conf( t ) for t in @tasks)
+        perfs = (perf( t ) for t in @tasks)
+
+        async.series confs, (err, vals) ->
+            async.series perfs, cb

 # Path related stuff
 scriptdir = exports['scriptdir'] = (stuff...) ->


Redirect to uid route of reassigned machine

--- client/ide/lib/routes.coffee
+++ client/ide/lib/routes.coffee
@@ -117,7 +117,11 @@
   then {workspaceSlug} = latestWorkspace
   else workspaceSlug   = 'my-workspace'

-  identifier = if machine.isPermanent() then machine.uid else machine.slug
+  identifier = machine.slug
+
+  if machine.isPermanent() or machine.jMachine.meta?.oldOwner
+    identifier = machine.uid
+
   kd.getSingleton('router').handleRoute "/IDE/#{identifier}/#{workspaceSlug}"


home/billing: pass down correct var as prop

--- client/home/lib/billing/components/creditcardcontainer.coffee
+++ client/home/lib/billing/components/creditcardcontainer.coffee
@@ -72,7 +72,7 @@
   props =
     isDirty: isDirty(FORM_NAME)(state)
     formValues:
-      number: realNumber and formValue state, 'number'
+      number: number
       name: formValue state, 'name'
       exp_year: formValue state, 'exp_year'
       exp_month: formValue state, 'exp_month'


fix dataset tests

fix dataset tests

--- test/integration/dataset.coffee
+++ test/integration/dataset.coffee
@@ -30,16 +30,12 @@
       result = browser.location.href
       result.should.match /\/dataset\/(\w+)/

-    it 'shows the tools I can use on this dataset', ->
-      tools = browser.queryAll '.dataset-tools .tool'
-      tools.length.should.be.above 0
-
-    xit 'shows the "Code your own View" tool', ->
-      tools = browser.queryAll '.dataset-tools .tool'
-      $(tools).text().toLowerCase().should.include 'code your own view'
+    it 'shows two tools I can use on this dataset', ->
+      tools = browser.queryAll '.dataset-views .tool'
+      tools.length.should.equal 2

     it 'has not shown the input box', ->
-      @input = browser.query '#header h2 input'
+      @input = browser.query '#subnav-path input'
       $(@input).is(':visible').should.be.false

     context 'when I click the title', ->
@@ -56,7 +52,7 @@
         before (done) ->
           browser.fill '#subnav-path input', randomname, ->
             browser.evaluate """
-              var e = jQuery.Event("keypress")
+              var e = jQuery.Event("keyup")
               e.which = 13
               e.keyCode = 13
               $('#subnav-path input').trigger(e)


dataset integration test should test a dataset

--- test/integration/dataset.coffee
+++ test/integration/dataset.coffee
@@ -16,18 +16,18 @@
   before (done) ->
     browser.fill '#username', 'ehg'
     browser.fill '#password', 'testing'
-    browser.pressButton '#login', ->
-      browser.visit '/tools', done
+    browser.pressButton '#login', done

-  context 'when I click on the newdataset dataset', ->
+  context 'when I click on a newdataset dataset', ->
     before (done) ->
-      link = browser.query('.tool a[href="/tool/newdataset"]')
+      body = browser.query('body')
+      link = $(body).find('.dataset:contains("newdataset")').first()[0]
       browser.fire 'click', link, ->
         browser.wait done

     it 'takes me to the highrise dataset page', ->
       result = browser.location.href
-      result.should.equal "#{url}/tool/newdataset"
+      result.should.match /\/dataset\/(\w+)/

     it 'has not shown the input box', ->
       @input = browser.query '#header h2 input'


test: Add tests for hooks glob pattern resolving

--- test/unit/add-hooks-test.coffee
+++ test/unit/add-hooks-test.coffee
@@ -124,6 +124,77 @@
         assert.isOk globStub.sync.called
         globStub.sync.restore()
         done()
+
+    it 'should return files alphabetically sorted', (done) ->
+      addHooks runner, transactions, (err) ->
+        return done err if err
+
+        expected = [
+          'multifile_hooks.coffee',
+          'test2_hooks.js',
+          'test_hooks.coffee'
+        ]
+
+        actual = runner.hooks.configuration.options.hookfiles
+
+        actual.forEach (item, index) ->
+          assert.include item, expected[index]
+
+        done()
+
+    it 'should return files with resolved paths', (done) ->
+      addHooks runner, transactions, (err) ->
+        return done err if err
+
+        expected = [
+          pathStub.resolve(process.cwd(), './test/fixtures/multifile/multifile_hooks.coffee'),
+          pathStub.resolve(process.cwd(), './test/fixtures/test2_hooks.js'),
+          pathStub.resolve(process.cwd(), './test/fixtures/test_hooks.coffee')
+        ]
+
+        actual = runner.hooks.configuration.options.hookfiles
+
+        assert.deepEqual actual, expected
+
+        done()
+
+    it 'should return resolved path for non existing file', (done) ->
+      runner =
+        configuration:
+          options:
+            hookfiles: 'foo/bar/hooks'
+
+      addHooks runner, transactions, (err) ->
+        return done err if err
+
+        actual = runner.hooks.configuration.options.hookfiles
+        expected = [pathStub.resolve(process.cwd(), 'foo/bar/hooks')]
+
+        assert.deepEqual actual, expected
+
+        done()
+
+    it 'should handle mixed filepaths and globs', (done) ->
+      runner =
+        configuration:
+          options:
+            hookfiles: ['foo/bar/hooks', './**/*_hooks.*']
+
+      addHooks runner, transactions, (err) ->
+        return done err if err
+
+        actual = runner.hooks.configuration.options.hookfiles
+        expected = [
+          pathStub.resolve(process.cwd(), 'foo/bar/hooks')
+          pathStub.resolve(process.cwd(), './test/fixtures/multifile/multifile_hooks.coffee')
+          pathStub.resolve(process.cwd(), './test/fixtures/test2_hooks.js')
+          pathStub.resolve(process.cwd(), './test/fixtures/test_hooks.coffee')
+        ]
+
+        assert.deepEqual actual, expected
+
+        done()
+

     describe 'when files are valid js/coffeescript', () ->
       runner = null


changed uses of the `slice` method to CS slices in `nodes.coffee`

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -388,7 +388,7 @@
     name = last @properties
     if @properties.length < 2 and not @base.isComplex() and not name?.isComplex()
       return [this, this]  # `a` `a.b`
-    base = new Value @base, @properties.slice 0, -1
+    base = new Value @base, @properties[...-1]
     if base.isComplex()  # `a().b`
       bref = new Literal o.scope.freeVariable 'base'
       base = new Value new Parens new Assign bref, base
@@ -420,8 +420,8 @@
         return ifn
       for prop, i in @properties when prop.soak
         prop.soak = off
-        fst = new Value @base, @properties.slice 0, i
-        snd = new Value @base, @properties.slice i
+        fst = new Value @base, @properties[...i]
+        snd = new Value @base, @properties[i..]
         if fst.isComplex()
           ref = new Literal o.scope.freeVariable 'ref'
           fst = new Parens new Assign ref, fst
@@ -839,7 +839,7 @@
   # Merge the properties from a top-level object as prototypal properties
   # on the class.
   addProperties: (node, name, o) ->
-    props = node.base.properties.slice 0
+    props = node.base.properties[0..]
     exprs = while assign = props.shift()
       if assign instanceof Assign
         base = assign.variable.base
@@ -1028,7 +1028,7 @@
   compileConditional: (o) ->
     [left, rite] = @variable.cacheReference o
     if "?" in @context then o.isExistentialEquals = true
-    new Op(@context.slice(0, -1), left, new Assign(rite, @value, '=') ).compile o
+    new Op(@context[0...-1], left, new Assign(rite, @value, '=') ).compile o

   # Compile the assignment from an array splice literal, using JavaScript's
   # `Array#splice` method.
@@ -1173,14 +1173,14 @@
       code = list[0].compile o, LEVEL_LIST
       return code if apply
       return "#{ utility 'slice' }.call(#{code})"
-    args = list.slice index
+    args = list[index..]
     for node, i in args
       code = node.compile o, LEVEL_LIST
       args[i] = if node instanceof Splat
       then "#{ utility 'slice' }.call(#{code})"
       else "[#{code}]"
-    return args[0] + ".concat(#{ args.slice(1).join ', ' })" if index is 0
-    base = (node.compile o, LEVEL_LIST for node in list.slice 0, index)
+    return args[0] + ".concat(#{ args[1..].join ', ' })" if index is 0
+    base = (node.compile o, LEVEL_LIST for node in list[0...index])
     "[#{ base.join ', ' }].concat(#{ args.join ', ' })"

 #### While


init tr first

init tr first

--- src/list.coffee
+++ src/list.coffee
@@ -16,4 +16,5 @@
     body.removeClass("sun")
     body.addClass(type)

+$('tr').eq(1).click()


Match exact group aliases

Match exact group aliases

--- src/alias.coffee
+++ src/alias.coffee
@@ -48,6 +48,6 @@
   # Compile RegEx to match only the aliases
   # Note this matches (alias) :alias: and @alias
   aliases = _.keys(groups).join('|')
-  regex = new RegExp('((?:\\(|\\:|@)(' + aliases + ')(?:\\|\\:)*)', 'i')
+  regex = new RegExp('(?:@(' + aliases + ')\\b)|(?:\\(|\\:)(' + aliases + ')(?:\\)|\\:)', 'i')
   robot.hear regex, (msg) ->
     msg.send expand(msg.message.text)


Join github and atom dirs

Join github and atom dirs

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

 delegate.browserMainParts.preMainMessageLoopRun = ->
   commandLineArgs = parseCommandLine()
-  require('module').globalPaths.push(path.join(commandLineArgs.resourcePath, "src"))
+  require('module').globalPaths.push(path.join(commandLineArgs.resourcePath, 'src'))
   AtomApplication = require('atom-application')
   atomApplication = new AtomApplication(commandLineArgs)

@@ -25,7 +25,7 @@
   if args['resource-path']
     resourcePath = args['resource-path']
   else if args['dev']
-    resourcePath = path.join(getHomeDir(), 'github/atom')
+    resourcePath = path.join(getHomeDir(), 'github', 'atom')

   try
     fs.statSync resourcePath


removed unnecessary hangupsjs client instance which created a cookie file in the module directory. fixes #16

--- src/main.coffee
+++ src/main.coffee
@@ -8,8 +8,6 @@
 clipboard = require 'clipboard'

 tmp.setGracefulCleanup()
-
-client = new Client()

 app = require 'app'
 BrowserWindow = require 'browser-window'


Removed stats resetting from bg

Removed stats resetting from bg

--- src/bg/bg.coffee
+++ src/bg/bg.coffee
@@ -1,5 +1,4 @@
 stats = require('../stats')
-stats.reset()

 hashCheck = require '../checkers/hash-check'
 regCheck = require '../checkers/reg-check'
@@ -26,4 +25,4 @@
 	urls: ['http://*/*', 'https://*/*', 'chrome-extension://*/*'],
 	types: ['script', 'stylesheet']
 	["blocking"]
-)+)


Added get() method to resolve templates.

--- src/Templates.coffee
+++ src/Templates.coffee
@@ -93,3 +93,9 @@
       Session.set(sessionVarName, value)
     reactiveVar

+  get: (templateOrName) ->
+    if templateOrName instanceof Blaze.Template
+      templateOrName
+    else
+      Template[templateOrName]
+


close modal if click overlay

close modal if click overlay

--- _source/client/templates/components/modal.coffee
+++ _source/client/templates/components/modal.coffee
@@ -4,11 +4,16 @@

   events: -> [
     "click [data-dismiss]": @.destroy
+    "click": @.closeIfOverlay
   ]

   destroy: (event) ->
     event?.preventDefault()
     Blaze.remove @._internals.templateInstance.view
+
+  closeIfOverlay: (event) ->
+    if $(event.currentTarget).hasClass("overlay--solid-dark")
+      @.destroy(event)

   renderContent: ->
     if @.data()?.template


Mark function delimiters as keywords

Mark function delimiters as keywords

--- grammars/apl.cson
+++ grammars/apl.cson
@@ -324,7 +324,7 @@
 			patterns: [{ include: "$self" }]
 			beginCaptures:
 				0:  {name: "entity.function.definition.apl"}
-				1:  {name: "punctuation.definition.function.begin.nabla.apl"}
+				1:  {name: "keyword.operator.nabla.apl"}
 				2:  {name: "entity.function.return-value.apl"}
 				3:  {name: "keyword.operator.assignment.apl"}
 				4:  {name: "entity.function.arguments.left.apl"}
@@ -339,8 +339,8 @@
 				13: {name: "invalid.illegal.local-variables.apl"}
 				14: {name: "comment.line.apl"}
 			endCaptures:
-				1:  {name: "punctuation.definition.function.end.nabla.apl"}
-				2:  {name: "punctuation.definition.function.end.lock.apl"}
+				1:  {name: "keyword.operator.nabla.apl"}
+				2:  {name: "keyword.operator.lock.apl"}
 				3:  {name: "comment.line.apl"}
 		}]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/clamp.coffee
+++ src/clamp.coffee
@@ -2,7 +2,7 @@
   Object.reverseMerge I,
     clamps: {}

-      self.bind "update", ->
+  self.bind "update", ->
     for name, cooldownOptions of I.cooldowns
       {approachBy, target} = cooldownOptions


* Hex Codec use the Buffer to hex string.

--- src/codec.coffee
+++ src/codec.coffee
@@ -1,4 +1,4 @@
-# Copyright (c) 2014 Riceball LEE, MIT License
+# Copyright (c) 2014-2015 Riceball LEE, MIT License
 util        = require("abstract-object/lib/util")
 Errors      = require('abstract-object/Error')
 Codec       = module.exports = require './abstract-codec'
@@ -59,6 +59,14 @@
     throw new InvalidFormatError('invalid hex string.') if length % 2 isnt 0
     length = length >> 1
     if isBuffer destBuffer
+      length = destBuffer.write data, offset, length, 'hex'
+    length
+  _encodeBuffer2: (data, destBuffer, offset, encoding)->
+    offset = Number(offset) || 0
+    length = data.length
+    throw new InvalidFormatError('invalid hex string.') if length % 2 isnt 0
+    length = length >> 1
+    if isBuffer destBuffer
       i = destBuffer.length - offset
       length = i if length > i
       i = 0
@@ -69,6 +77,8 @@
         i++
     length
   _decodeBuffer: (buf, start, end)->
+    buf.toString 'hex', start, end
+  _decodeBuffer2: (buf, start, end)->
     len = buf.length
     start = 0 if !start or start < 0
     end = len if !end or end < 0 or end > len


Run bower install during versal codio initialization

--- src/codio.coffee
+++ src/codio.coffee
@@ -1,5 +1,6 @@
 path = require 'path'
 fs = require 'fs-extra'
+spawn = require('child_process').spawn

 # Creates .codio file with versal buttons in current folder
 module.exports = (options, callback) ->
@@ -14,3 +15,14 @@
   fs.copy source, target, (err) ->
     if err then return callback err
     callback()
+
+  # If bower.json is present, run bower install detached.
+  bowerPath = path.resolve cwd, 'bower.json'
+  fs.exists bowerPath, (exists) ->
+    process.nextTick ->
+      # config.interactive is to prevent bower asking for
+      # permission to collect statistics anonymously
+      child = spawn 'bower',
+        ['install', '--config.interactive=false'],
+        { detached: true }
+      child.unref()


cleaning up array constructions

cleaning up array constructions

--- src/color.coffee
+++ src/color.coffee
@@ -94,7 +94,7 @@
         rgbMap = color.splice(0, 3).map (channel) ->
           parseFloat channel
         parsedColor = rgbMap.concat(parseFloat alpha)
-      else if !Object.isArray(color)
+      else
         parsedColor = lookup[normalizeKey(color)] || parseHex(color) || parseRGB(color)
         parsedColor[3] = alpha
     else if arguments.length > 2


Cosmetic change to comments.coffee

Cosmetic change to comments.coffee

--- app/assets/javascripts/channels/comments.coffee
+++ app/assets/javascripts/channels/comments.coffee
@@ -5,6 +5,6 @@
   disconnected: ->
     # Called when the subscription has been terminated by the server

+  # Called when there's incoming data on the websocket for this channel
   received: (data) ->
     $('#comments').prepend(data)
-    # Called when there's incoming data on the websocket for this channel


only create channel when user logged in

only create channel when user logged in

--- app/assets/javascripts/channels/manifest.coffee
+++ app/assets/javascripts/channels/manifest.coffee
@@ -1,9 +1,10 @@
-App.manifest = App.cable.subscriptions.create "ManifestChannel",
-  connected: ->
-    # Called when the subscription is ready for use on the server
+if typeof current_provider_id != "undefined"
+  App.manifest = App.cable.subscriptions.create "ManifestChannel",
+    connected: ->
+      # Called when the subscription is ready for use on the server

-  disconnected: ->
-    # Called when the subscription has been terminated by the server
+    disconnected: ->
+      # Called when the subscription has been terminated by the server

-  received: (data) ->
-    # Called when there's incoming data on the websocket for this channel
+    received: (data) ->
+      # Called when there's incoming data on the websocket for this channel


Fixed infinite pages cookie naming.

Fixed infinite pages cookie naming.

--- app/assets/javascripts/infinite_pages.js.coffee
+++ app/assets/javascripts/infinite_pages.js.coffee
@@ -3,7 +3,6 @@
   selButton: '#endless-toggle'
   selButtonText: '#endless-toggle .text'
   slug: $('#gallery').data('slug')
-  cookie: 'endless_' + this.slug

   pause: ->
     $(this.selMain).infinitePages('pause')
@@ -14,6 +13,8 @@
     $(this.selButtonText).text('Endless')

 $(document).on 'content:update', ->
+  inf.cookie = 'endless_' + inf.slug
+
   $(inf.selMain).infinitePages
     loading: ->
       $(this).text('← Loading...')


fix a typo in variable name "newTransactionName"

--- src/add-hooks.coffee
+++ src/add-hooks.coffee
@@ -18,11 +18,11 @@
     for hookType in ['beforeHooks', 'afterHooks']
       for transactionName, hooks of allHooks[hookType]
         if transactionName.match(pattern) != null
-          newTranscationName = transactionName.replace(pattern, '')
-          if allHooks[hookType][newTranscationName] != undefined
-            allHooks[hookType][newTranscationName] = hooks.concat allHooks[hookType][newTranscationName]
+          newTransactionName = transactionName.replace(pattern, '')
+          if allHooks[hookType][newTransactionName] != undefined
+            allHooks[hookType][newTransactionName] = hooks.concat allHooks[hookType][newTransactionName]
           else
-            allHooks[hookType][newTranscationName] = hooks
+            allHooks[hookType][newTransactionName] = hooks

           delete allHooks[hookType][transactionName]


Remove object.values from subject-viewer (#4490)

--- app/components/subject-viewer.cjsx
+++ app/components/subject-viewer.cjsx
@@ -175,7 +175,7 @@

               {for i in [0...@props.subject?.locations.length ? 0]
-
+
                   <input type="radio" name="frame" value={i} onChange={@handleFrameChange.bind this, i} />{i + 1}
                 }


Correctly list users

Correctly list users

#22

--- annotran/static/scripts/widget-controller.coffee
+++ annotran/static/scripts/widget-controller.coffee
@@ -86,7 +86,7 @@

         # note: selectedUser is set in user-list.js directive
         if $scope.$root == undefined or $scope.$root == null or $scope.$root.selectedUser == undefined
-          return
+          $scope.$root.selectedUser = undefined

         selectedUser = $scope.$root.selectedUser


Comments need to include issue comments, too.

--- prviewer.coffee
+++ prviewer.coffee
@@ -111,12 +111,29 @@

     (pulls, cb) ->
       iterator = (pull, pullCb) ->
-        github.pullRequests.getComments {
-          user: settings.github.user
-          repo: settings.github.repo
-          number: pull.number
-        }, (err, comments) ->
+
+        async.parallel [
+
+          (cb2) ->
+            github.pullRequests.getComments {
+              user: settings.github.user
+              repo: settings.github.repo
+              number: pull.number
+            }, cb2
+
+          (cb2) ->
+            github.issues.getComments {
+              user: settings.github.user
+              repo: settings.github.repo
+              number: pull.number
+            }, cb2
+
+        ], (err, results) ->
           return pullCb err if err
+
+          # Combine issue comments and pull comments, then sort.
+          comments = results[0].concat results[1]
+          comments.sort (a, b) -> if a.updated_at < b.updated_at then -1 else 1

           # Record number of comments.
           pull.num_comments = comments.length


Show prices for each domain in domain list item and provide 5 year selection for domain register. Add a simple buy button

--- client/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
+++ client/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
@@ -56,6 +56,8 @@
     domainName            = "#{domainName.getValue()}.#{domains.getValue()}"
     {getDomainInfo, getDomainSuggestions} = KD.remote.api.JDomain

+    @newDomainEntryForm.domainList?.setClass 'out'
+
     getDomainInfo domainName, (err, status)=>

       if err
@@ -72,9 +74,6 @@
           createButton.hideLoader()
           return warn err if err
           @newDomainEntryForm.setAvailableDomainsData suggestions
-
-      new KDNotificationView
-        title: if status.available then 'Yay its available' else 'Sorry dude.'

   createSubDomain: ->
     {domains, domainName} = @subDomainEntryForm.inputs
@@ -246,10 +245,23 @@

   constructor:(options={}, data)->
     options.cssClass = KD.utils.curry "domain-buy-items", options.cssClass
+
     super options, data
+
+    @buyButton = new KDButtonView
+      title    : "Buy"
+      style    : "clean-gray"
+      callback : ->
+        alert 'Buy it'
+
+    price = (parseFloat @getData().price).toFixed 2
+    selectOptions = \
+      ({title: "#{i} year for $ #{(price * i).toFixed 2}" , value: i} for i in [1..5])
+    @yearBox = new KDSelectBox {name:'year', selectOptions}

   pistachio:->
     """
-      {h1#domain{#(domain)}}
-      {{#(price)}}
+      {h1{#(domain)}}
+      {{> @yearBox}}
+      {{> @buyButton}}
     """


Fixed issue with selectbox not hiding

Fixed issue with selectbox not hiding

--- app/assets/javascripts/due_date_select.js.coffee
+++ app/assets/javascripts/due_date_select.js.coffee
@@ -1,18 +1,25 @@
 class @DueDateSelect
   constructor: ->
+    $loading = $('.js-issuable-update .due_date').find('.block-loading').hide()
+
     $('.js-due-date-select').each (i, dropdown) ->
       $dropdown = $(dropdown)
       $dropdownParent = $dropdown.closest('.dropdown')
       $addBtn = $('.js-due-date-add', $dropdownParent)
       $datePicker = $dropdownParent.find('.js-due-date-calendar')
       $block = $dropdown.closest('.block')
-      $loading = $block.find('.block-loading').hide()
       $selectbox = $dropdown.closest('.selectbox')
       $value = $block.find('.value')

       fieldName = $dropdown.data('field-name')
       abilityName = $dropdown.data('ability-name')
       issueUpdateURL = $dropdown.data('issue-update')
+
+      $dropdown.glDropdown(
+        hidden: ->
+          $selectbox.hide()
+          $value.removeAttr('style')
+      )

       $addBtn.on 'click', (e) ->
         e.preventDefault()


Fix optimistic chat creation

Fix optimistic chat creation

--- app/assets/javascripts/views/chat_view.js.coffee
+++ app/assets/javascripts/views/chat_view.js.coffee
@@ -8,7 +8,7 @@
   events:
     'click   .js-chat-load-more' : 'onLoadMore'
     'click   .js-chat-create-wip': 'onCreateWip'
-
+
   initialize: (options)->
     @readyToLoadMore = true
     @listenTo(@collection, 'add', @render)
@@ -23,7 +23,7 @@
       'overflow-y':'scroll'
     )
     @scrollContainer.on 'scroll', this.loadMoreWhenNearTop
-
+
     $(window).resize(@onWindowResize.bind(this))
     @onWindowResize()

@@ -32,7 +32,7 @@
   render: =>
     @$('.js-chat-load-more').toggle(@collection.length >= 25)
     @scrollToLatestActivity() if @stuckToBottom
-
+
   scrollToLatestActivity: ->
     $(@scrollContainer).scrollTop(999999)

@@ -44,7 +44,7 @@
       created: (new Date()).toISOString()
       actor:  app.currentUser().attributes
       subject: {
-        body: comment.body
+        body: comment.get('body')
         tips: []
         total_tips: 0
       }
@@ -69,7 +69,7 @@
   scrollToElement: (anchor)=>
     console.log(anchor)
     @scrollContainer.scrollTop $("#" + anchor).offset().top
-
+
   loadMore: (e)=>
     return if @readyToLoadMore == false
     @readyToLoadMore = false
@@ -88,7 +88,7 @@
         console.log(curretTopEvent)
         newHeight = @scrollContainer[0].scrollHeight
         @scrollContainer.scrollTop (newHeight - oldHeight)
-
+
       success: (datas) =>
         fixScroll =>
           for data in _.sortBy(datas, (data) -> data['created']).reverse()
@@ -96,7 +96,7 @@

           loadMoreButton.text(originalText).attr('disabled', false)
     )
-
+
   onCreateWip: (e) ->
     e.preventDefault()
     id = $(e.currentTarget).attr('href')


Thorax already fetches unpopulated models, don't fetch by hand

--- app/assets/javascripts/views/dashboard.js.coffee
+++ app/assets/javascripts/views/dashboard.js.coffee
@@ -7,9 +7,13 @@
   events:
     model:
       change: ->
-        unless @model.isLoading()
+        if @model.isPopulated()
           clearInterval(@timeout) if @timeout?
           d3.select(@el).select('.pop-chart').datum(_(lower_is_better: @lower_is_better).extend @model.result()).call(@popChart)
+        else
+          @timeout ?= setInterval =>
+            @model.fetch()
+          , 3000
       rescale: ->
         if @model.isPopulated()
           if PopHealth.currentUser.populationChartScaledToIPP() then @popChart.maximumValue(@model.result().IPP) else @popChart.maximumValue(PopHealth.patientCount)
@@ -33,11 +37,6 @@
   initialize: ->
     @popChart = PopHealth.viz.populationChart().width(125).height(25).maximumValue(PopHealth.patientCount)
     @model.set('providers', [@provider_id]) if @provider_id?
-    unless @model.isPopulated()
-      @timeout = setInterval =>
-        @model.fetch()
-      , 3000
-

 class Thorax.Views.DashboardSubmeasureView extends Thorax.View


fix issue with hidden regrade tour overlay blocking quiz edit links

fixes CNVS-16719

test plan:
  - as a teacher
    - create a quiz

  - as a student
    - take the quiz

  - as a teacher
    - edit the quiz
    - the left side navigation links should all be clickable
    - edit the question to change the answer and regrade
    - the regrade tour should appear
    - close the regrade tour
    - the hidden overlay should still not be blocking the navigation links

Change-Id: I656de5f08c7c5696ff9f8e202e3e869e529cf1b0
Reviewed-on: https://gerrit.instructure.com/44411
Reviewed-by: Jason Madsen <bcb5fff571a20f07c35d17ba9c364855c776dce7@instructure.com>
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
QA-Review: Trevor deHaan <85df72112e8a9484fec7b3db2e31789c2c2d4e07@instructure.com>
Product-Review: Derek DeVries <ff87a0776152510efd53bb1f4ea089dbbc90e730@instructure.com>

--- app/coffeescripts/views/tours/QuizRegrade.coffee
+++ app/coffeescripts/views/tours/QuizRegrade.coffee
@@ -9,7 +9,7 @@

     template: template

-    attach: ->
+    attachTour: ->
       $(document).one 'click', '.select_answer_link', =>
         setTimeout =>
           @tour.start()


skip one spec

skip one spec