track young checked

track young checked

--- app/controllers/animal_details.coffee
+++ app/controllers/animal_details.coffee
@@ -70,7 +70,10 @@
       checkbox.val()

   getBabies: ->
-    !!@babiesCheckbox.attr 'checked'
+    babies = !!@babiesCheckbox.attr 'checked'
+    if babies
+      AnalyticsLogger.logEvent 'young', @animal.id
+    babies

   onClickCancel: ->
     @hide()


trying to add support for `--`

trying to add support for `--`

--- lib/bebopt.coffee
+++ lib/bebopt.coffee
@@ -81,12 +81,13 @@
       self._parent = self["_#{listName}"][name]
       return self))

-  _optError: (parent, child) ->
+  _optError: (parent, child) =>
+    if @["_#{parent}"][child] is undefined
       if child.length < 2
         console.error("#{@app}: invalid option -- '#{child}'")
         process.exit(1)
       else if child.length > 1
-        dash = if opt.parent is '_half' then '-' else '--'
+        dash = if parent is 'half' then '-' else '--'
         console.error("#{@app}: unrecognized option '#{dash}#{child}'")
         process.exit(1)

@@ -99,43 +100,38 @@
     @_parentError()
     @_parent.usage = text
     @_parent = null
-    @_log(@)
     return @

   _gather: () =>
     @__rargs = process.argv
     @__pargs = @__rargs.slice(2).map((opt, ind, arr) =>
-      len = opt.replace(/^(--?).*/, '$1').length
-      dRef = {}
-      switch len
-        when 1
-          opt = opt.replace(/^-/, '')
-          if opt.length < 2 # short
-            ref = @_short[opt]
-            console.log ref
-            @_checkOption({
-              parent: 'short',
-              ref: ref
-            }, opt)
-          else
-            ref = @_half[opt]
-            @_checkOption({
-              parent: 'half',
-              ref: ref
-            }, opt)
-        when 2
-          opt = opt.replace(/^--/, '')
-          ref = @_long[opt]
-          @_checkOption({
-            parent: 'long'
-      _ref = @_checkOption(dRef)
-      _ref.name = dRef.child
-      return _ref)
+      if /^--$/.test(opt)
+        len = opt.replace(/^(--?).*/, '$1').length
+        parent = null
+        switch len
+          when 1
+            opt = opt.replace(/^-/, '')
+            if opt.length < 2 # short
+              ref = @_short[opt]
+              parent = 'short'
+            else
+              ref = @_half[opt]
+              parent = 'half'
+          when 2
+            opt = opt.replace(/^--/, '')
+            ref = @_long[opt]
+            parent = 'long'
+        console.log opt
+        @_optError(parent, opt)
+      else
+
+      return ref)

   _log: (y) ->
     console.log(util.inspect(y, { colors: true, depth: null }))

   parse: () =>
     @_gather()
+    @_log(@)

 module.exports = Bebopt


Fix daily low values #689

Fix daily low values #689

--- app/js/services/market_service.coffee
+++ app/js/services/market_service.coffee
@@ -726,9 +726,7 @@
         trades = []
         max = 0
         dailyHigh = 0
-        dailyLow = 10^9
-        if market.shorts_available
-            dailyLow = if inverted then (1 / market.feed_price) * 2 else market.feed_price / 2
+        dailyLow = 10e9
         volume = 0
         change = 0
         open = null


Don't send empty form fields as undefined for file uploads

--- src/main/coffeescript/view/OperationView.coffee
+++ src/main/coffeescript/view/OperationView.coffee
@@ -121,7 +121,8 @@
     # add params
     for param in @model.parameters
       if param.paramType is 'form'
-        bodyParam.append(param.name, map[param.name])
+        if map[param.name] != undefined
+            bodyParam.append(param.name, map[param.name])

     # headers in operation
     headerParams = {}


cleaning up merge

cleaning up merge

--- app/scripts/alchemy/core/stats.coffee
+++ app/scripts/alchemy/core/stats.coffee
@@ -97,24 +97,7 @@

     update: () ->
-<<<<<<< HEAD
         if conf.nodeStats is true
             alchemy.stats.nodeStats()
         if conf.edgeStats is true
             alchemy.stats.edgeStats()
-=======
-        #get all the nodes
-        # nodeNum = d3.selectAll(".node")[0].length
-        # activeNodes = d3.selectAll(".node.active")[0].length
-        # inactiveNodes = d3.selectAll(".node.inactive")[0].length
-        # console.log "number of nodes: " + nodeNum + " activeNodes: " + activeNodes + " inactiveNodes: " + inactiveNodes
-
-        # #get all the edges
-        # edgeNum = d3.selectAll(".edge")[0].length
-        # activeEdges = d3.selectAll(".edge.active")[0].length
-        # inactiveEdges = d3.selectAll(".edge.inactive")[0].length
-        # console.log "number of edges: " + edgeNum + " activeEdges: " + activeEdges + " inactiveEdges: " + inactiveEdges
-
-        alchemy.stats.nodeStats()
-        alchemy.stats.edgeStats()
->>>>>>> f54c02fe24ecacca45bca2570a5f7561fe308bde


Fixes steelbrain/atom-hack#7

Fixes steelbrain/atom-hack#7

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -16,10 +16,12 @@
     @ssh_conn = null
     me = this
     atom.workspace.onDidChangeActivePaneItem ->
-      if me.errors.length isnt 0
-        me.markClear()
-        me.markErrors()
-      return
+      try
+        if me.errors.length isnt 0
+          me.markClear()
+          me.markErrors()
+        return
+      catch
     atom.workspace.observePaneItems (pane)->
       if typeof pane.onDidDestroy is 'undefined' then return
       pane.onDidDestroy ->


Fixed bug where defaultLevel is not used properly

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -258,7 +258,7 @@
     else if match.info
       level = 'info'
     else
-      level = match.level or 'error'
+      level = match.level or @defaultLevel

     # If no line/col is found, assume a full file error
     # TODO: This conflicts with the docs above that say line is required :(


Prevent recursion if the path has already been loaded.

--- lib/loader.coffee
+++ lib/loader.coffee
@@ -136,7 +136,8 @@
                 siteLookupRoot ?= siteroot
                 sites[siteroot]= m
                 for sitepath in m.paths
-                  loadTree(siteStack.concat([normalize(sitepath)]))
+                  unless fulldirname == normalize(sitepath)
+                    loadTree(siteStack.concat([normalize(sitepath)]))

       visited= {}
       visited[fs.lstatSync(rootdir).ino]= true


logger: protect against missing req.route

--- lib/logger.coffee
+++ lib/logger.coffee
@@ -6,5 +6,5 @@

   console.log JSON.stringify
     ts: req._time
-    path: req.route.path
-    verb: req.route.method
+    path: req.route?.path
+    verb: req.route?.method


:fire: blank row

:fire: blank row

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -65,6 +65,7 @@
       wasEmpty = s.isEmpty()
       unless s.isReversed() or wasEmpty
         moveCursorLeft(cursor, {allowWrap: true, preserveGoalColumn: true})
+
       @moveCursor(cursor)
       # When motion is used as target of operator, return if motion movement not happend.
       if s.isEmpty() and wasEmpty


Updated search results comment.

Updated search results comment.

--- lib/search.coffee
+++ lib/search.coffee
@@ -1,7 +1,7 @@
 SearchResults = new Meteor.Collection 'SearchResults', transform: (doc) => new SearchResult doc

 class SearchResult extends @Document
-  # query: query object or string as provided by the client
+  # query: query object or string as provided by the client (or null for a document counting available content to search over)
   # countPublications: number of publications in the results for the query
   # countPeople: number of people in the results for the query


Fix timeline record placement

Fix timeline record placement

--- app/assets/volume/slot.coffee
+++ app/assets/volume/slot.coffee
@@ -896,9 +896,8 @@
         records.sort (a, b) ->
           a.record.category - b.record.category || a.record.id - b.record.id
         t = []
-        overlaps = (rr) -> rr.record.id != r.record.id && s.overlaps(rr.segment)
+        overlaps = (rr) -> rr.record.id != r.record.id && r.overlaps(rr)
         for r in records
-          s = r.segment
           for o, i in t
             break unless o[0].record.category != r.record.category || o.some(overlaps)
           t[i] = [] unless i of t


Route to lobby when game not found

Route to lobby when game not found

--- app/client/game/result.coffee
+++ app/client/game/result.coffee
@@ -167,6 +167,7 @@

 Template.result.created = ->
   game = currentGame()
+  Router.go('lobby') unless game?
   player = Meteor.users.findOne game.playerId

   unless currentChallenge()?


half second sampling

half second sampling

--- app/client/space/space.coffee
+++ app/client/space/space.coffee
@@ -1,4 +1,4 @@
-lastEventTime = 0
+lastEventTime = (new Date()).getTime()

 player = null

@@ -10,15 +10,17 @@
   console.log("resizeHandler", evt)

 deviceMotionHandler = (eventData) ->
+  curEventTime = (new Date()).getTime()
+  if (curEventTime - lastEventTime) < 500
+    return
+
+  lastEventTime = curEventTime
+  console.log("curTime", curEventTime)
+
   console.log("deviceMotionHandler", eventData)
   info = undefined
   xyz = "[X, Y, Z]"

-  sampleTime = eventData.interval
-  console.log("sampleTime", sampleTime)
-  # // Grab the refresh interval from the results
-  document.getElementById("moInterval").innerHTML = sampleTime
-
   # Grab the acceleration from the results
   acceleration = eventData.acceleration
   info = xyz.replace("X", acceleration.x)


cli: add `completion` to help text

cli: add `completion` to help text

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -28,6 +28,8 @@
       Commands:
         ls            List tasks polled from various sources
         work          Begin, resume, and complete tasks with a timer
+
+        completion    Tab completion
       '''

   usage = ->


lib: Update cli defaults

lib: Update cli defaults

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -9,17 +9,18 @@

 npmPackage = JSON.parse fs.readFileSync require.resolve('../package.json'), 'utf8'

+
 program
     .version(npmPackage.version)
     .option('-t, --target [localhost:8080]',
-        'hostname and port to listen on', targetParser,
-        { host: 'localhost', port:8080 })
+        'hostname and port to listen on or url', targetParser,
+        { scheme: 'http:', host: 'localhost', port:8080 , path: '' })
     .option('-c, --child-registry-target [localhost:5984]',
         'NPM registry to push packages and pull from first', targetParser,
-        { host: 'localhost', port:5984 })
+        { scheme: 'http:', host: 'localhost', port:5984, path: '' })
     .option('-p, --parent-registry-target [registry.npmjs.org:80]',
         "NPM registry to pull packages from if the child doesn't have it", targetParser,
-        { host: 'registry.npmjs.org', port: 80 })
+        { scheme: 'http:', host: 'registry.npmjs.org', port: 80, path: '' })
     .parse(process.argv)

 server.createNpmProxyServer program


simplify server highlight how the socket is obtained in both the server and the client no second-guessing of what "this" is in connectionListener, always use the socket to pass events also added some debugs

--- lib/esl.coffee
+++ lib/esl.coffee
@@ -209,6 +209,9 @@
   # is normally not used directly.

   send: (command,args,cb) ->
+      if exports.debug
+        util.log util.inspect command: command, args: args
+
       # Make sure we are the only one receiving command replies
       @socket.removeAllListeners('esl_command_reply')
       @socket.removeAllListeners('esl_api_response')
@@ -403,16 +406,19 @@
     # Build request and response and send them out.
     req = new eslRequest headers,body
     res = new eslResponse socket
+    if exports.debug
+      util.log util.inspect event:event, req:req, res:res
     socket.emit event, req, res
   # Get things started
-  @emit 'esl_connect', new eslResponse socket if @emit?
+  socket.emit 'esl_connect', new eslResponse socket

 #### ESL Server

 class eslServer extends net.Server
   constructor: (requestListener) ->
-    @on 'esl_connect', requestListener
-    @on 'connection', connectionListener
+    @on 'connection', (socket) ->
+      socket.on 'esl_connect', requestListener
+      connectionListener socket
     super()

 # You can use createServer(callback) from your code.
@@ -420,11 +426,12 @@

 #### ESL client
 class eslClient extends net.Socket
-  constructor: () ->
-    @on 'connect', () -> connectionListener(this)
+  constructor: ->
+    @on 'connect', ->
+      connectionListener @
     super()

-exports.createClient = () -> return new eslClient()
+exports.createClient = -> return new eslClient()

 #### CallServer: a higher-level interface
 #


alternate version with logos

alternate version with logos

--- app/translations/en_us.coffee
+++ app/translations/en_us.coffee
@@ -23,9 +23,9 @@
         With your help, we've classified all the data we have so far. Great work!

         If you’d like to try a similar project, how about:
-

-

Chicago Wildlife Watch

-

Chimp & See

+

+

Chicago Wildlife Watch

+

Chimp & See


Update expected hover background color in test

Forgot to include this in 86d8304e4f5afb430ace91b7f68cb1337c1458db.


always uses lowercase channel name with /join

--- src/chat/user_command_handler.coffee
+++ src/chat/user_command_handler.coffee
@@ -62,12 +62,14 @@
         @chat.connect @server, @port

     @_addCommand 'join',
-      description: 'joins the channel, the current channel is used by default'
+      description: 'joins the channel, reconnects to the current channel ' +
+          'if no channel is specified'
       category: 'common'
       params: ['opt_channel']
       requires: ['connection']
       validateArgs: ->
-        @channel ?= @chan
+        @channel ?= @chan # use the current channel if no channel is specified
+        @channel = @channel.toLowerCase()
       run: ->
         @chat.join @conn, @channel


Namespace gauge keys correctly

Namespace gauge keys correctly

--- metrics.coffee
+++ metrics.coffee
@@ -29,7 +29,7 @@
 			statsd.timing(buildKey(this.key), timeSpan, this.sampleRate)

 	gauge : (key, value, sampleRate = 1)->
-		statsd.gauge key, value, sampleRate
+		statsd.gauge buildKey(key), value, sampleRate

 	mongodb: require "./mongodb"
 	http: require "./http"


Removed Config.builtins and moved 'clean' rules to external plugin.

--- src/ubs.coffee
+++ src/ubs.coffee
@@ -73,14 +73,12 @@
 run = (options) ->
   logging.debug "Command-line options #{util.inspect _.omit(options, "argv")}: tasks #{options.argv.remain}"
   Q.fcall (resolve) ->
-    rules = unless options["no-builtins"] then Config.builtinRules() else {}
-    tasklist = parseTaskList options, rules.settings
+    tasklist = parseTaskList options
     if tasklist[0].length is 0
       options.tasklist = [ DEFAULT_TASK ]
     else
       options.tasklist = tasklist[0]

-    logging.debug "Loaded rules: #{util.inspect rules}"
     # Load build.yml or result of options.build
     buildFile = options.build

@@ -88,7 +86,7 @@
       fs.readFile buildFile, "utf-8", (error, code = {}) ->
         reject(new Error error) if error

-        resolve recursiveMerge rules, yaml.safeLoad code, yaml.JSON_SCHEMA
+        resolve yaml.safeLoad code, yaml.JSON_SCHEMA

   .then (tasks) ->
     logging.debug "Tasks #{util.inspect tasks, undefined, 4}"


Make the showhide trigger be a live event.  Sadly, this will make the javascript slightly slower.

--- app/coffee/dreamplugs.coffee
+++ app/coffee/dreamplugs.coffee
@@ -64,7 +64,7 @@
   )

   # Setup showhide triggers
-  $('.trigger.showhide').click( (event) ->
+  $('.trigger.showhide').live( 'click', (event)->
     $newTargetShowHide = $(event.currentTarget).parent().find('.target.showhide')

     if $newTargetShowHide.css('display') is 'none'


Correct try and null coalesce

Correct try and null coalesce

--- resources/assets/lib/register-components.coffee
+++ resources/assets/lib/register-components.coffee
@@ -53,7 +53,7 @@
 $.subscribe 'user:update', resetNotificationWorker

 reactTurbolinks.registerPersistent 'notification-icon', NotificationIcon, true, (el) ->
-  props = try JSON.parse(el.dataset.notificationIcon) ? {}
+  props = (try JSON.parse(el.dataset.notificationIcon)) ? {}
   props.worker = notificationWorker

   props


Moved default siteUrl to startHighlighting

--- app/js/util/highlight.coffee
+++ app/js/util/highlight.coffee
@@ -12,7 +12,7 @@
 # Function which will collect all the facts for the current page
 # and select them.
 # Returns deferred object
-fetchFacts = (siteUrl=Factlink.siteUrl()) ->
+fetchFacts = (siteUrl) ->
   $.ajax
     # The URL to the Factlink backend
     url: FactlinkConfig.api + "/site?url=" + encodeURIComponent(siteUrl)
@@ -28,13 +28,13 @@
   highlighting = true

   console.info "Factlink:", "startHighlighting"
-  fetchFacts()
+  fetchFacts Factlink.siteUrl()

 # Don't check for highlighting here, as this is a
 # special hacky-patchy method for in the blog
 Factlink.highlightAdditionalFactlinks = (siteUrl) ->
   console.info "Factlink:", "highlightAdditionalFactlinks"
-  fetchFacts(siteUrl)
+  fetchFacts siteUrl

 Factlink.stopHighlighting = ->
   return unless highlighting


Update de-AT.coffee (#5786)

Update de-AT.coffee (#5786)

* Update de-AT.coffee

* Update de-AT.coffee

* Update de-AT.coffee

* Update de-AT.coffee

Co-authored-by: Valentin Bryukhanov <53545457af642b0f8712bb2cc004f120ce4436f4@gmail.com>


Forgot to add a bit of a patch

Forgot to add a bit of a patch

--- lib/installed-packages-panel.coffee
+++ lib/installed-packages-panel.coffee
@@ -81,11 +81,6 @@
     container.empty()
     packageViews = []
     for pack, index in packages
-      if index % 3 is 0
-        packageRow = $$ -> @div class: 'row'
-        container.append(packageRow)
-        packView = new AvailablePackageView(pack, @packageManager)
-        packView.widen()
       packageRow = $$ -> @div class: 'row'
       container.append(packageRow)
       # TODO if pack.valid?


add confrim before delete

add confrim before delete

--- core/static/script/panel/panel.coffee
+++ core/static/script/panel/panel.coffee
@@ -29,23 +29,31 @@
     $.post '/plugin/phpfpm/switch/', JSON.stringify {enable: enable}
     .success ->
       location.reload()
-  #nginx
-  # $ '.nginx-edit-button'
-  #   .on 'click', (e) ->
-  #     ($ '#nginxModal').modal 'show'
+  nginx
+  $ '.nginx-edit-btn'
+    .on 'click', (e) ->
+      e.preventDefault()
+      id = ($(@).closest 'tr').data 'id'
+      $.post '/plugin/nginx/site_config', JSON.stringify {
+        id: id
+      }
+      .success (data) ->
+
+      ($ '#nginxModal').modal 'show'

   $ '.nginx-remove-btn'
     .on 'click', (e) ->
-      e.preventDefault()
-      id = ($(@).closest 'tr').data 'id'
-      $.post '/plugin/nginx/update_site', JSON.stringify {
-        action: 'delete'
-        id: id
-        type: $('#nginxConfigType').find('.active a').attr('href').substr 1
-      }
-      .success ->
-        location.reload()
+      if window.confirm('确认删除?')
+        e.preventDefault()
+        id = ($(@).closest 'tr').data 'id'
+        $.post '/plugin/nginx/update_site', JSON.stringify {
+          action: 'delete'
+          id: id
+          type: $('#nginxConfigType').find('.active a').attr('href').substr 1
+        }
+        .success ->
+          location.reload()

   $ '#nginxSave'
     .on 'click', (e) ->


add space between « at » and the time for server messages, fixes #119

--- src/frontend/modules/textChat.coffee
+++ src/frontend/modules/textChat.coffee
@@ -153,7 +153,7 @@
         jqLastMessage = do (@jqMessageBox.find 'li').last
         d = new Date
         jqNewMsg  = ($ '
', { class : 'blueduckturquoise italic' }).html message.text
-        jqNewTime   = ($ '', { class : 'time' }).text ' at' + ((do d.toTimeString).substr 0, 5)
+        jqNewTime   = ($ '', { class : 'time' }).text ' at ' + ((do d.toTimeString).substr 0, 5)
         jqNewMsg.append jqNewTime
         if jqLastMessage[0]?
             jqLastMessage.append '

'
@@ -166,7 +166,7 @@
         jqLastMessage = do (@jqMessageBox.find 'li').last
         d = new Date
         jqNewMsg  = ($ '
', { class : 'blueduckturquoise' }).html action.text
-        jqNewTime   = ($ '', { class : 'time' }).text ' at' + ((do d.toTimeString).substr 0, 5)
+        jqNewTime   = ($ '', { class : 'time' }).text ' at ' + ((do d.toTimeString).substr 0, 5)
         jqNewMsg.append jqNewTime
         if jqLastMessage[0]?
             jqLastMessage.append '

'


Autocomplete in __construct

Autocomplete in __construct


php-file-parser - Try to make addUseClass smarter about the placement of the use statement.

--- lib/services/php-file-parser.coffee
+++ lib/services/php-file-parser.coffee
@@ -154,28 +154,48 @@
      * @param {string}     className Name of the class to add
     ###
     addUseClass: (editor, className) ->
-        text = editor.getText()
-        lastUse = 0
-        index = 0
-
-        splits = className.split('\\')
-        if splits.length == 1 || className.indexOf('\\') == 0
+        if className.split('\\').length == 1 || className.indexOf('\\') == 0
             return null

-        lines = text.split('\n')
-        for line in lines
-            line = line.trim()
+        i = 0
+        bestUse = 0
+        bestScore = 0
+        lastUse = null
+        placeBelow = true
+        doNewLine = true
+        lineCount = editor.getLineCount()
+
+
+        # TODO: This should probably skip comments.
+        # TODO: This should probably also stop on trait, interface, ...
+        # TODO: Needs a load of refactoring, but commit and push first so we can revert if we break things.
+
+
+        for i in [0 .. lineCount - 1]
+            line = editor.lineTextForBufferRow(i).trim()

             # If we found class keyword, we are not in namespace space, so return
             if line.indexOf('class ') != -1
-                editor.setTextInBufferRange([[lastUse+1,0], [lastUse+1, 0]], "use #{className};\n")
+                lineToInsertAt = bestUse + (if placeBelow then 1 else 0)
+
+                textToInsert = ''
+
+                if doNewLine and placeBelow
+                    textToInsert += "\n"
+
+                textToInsert += "use #{className};\n"
+
+                if doNewLine and not placeBelow
+                    textToInsert += "\n"
+
+                editor.setTextInBufferRange([[lineToInsertAt, 0], [lineToInsertAt, 0]], textToInsert)
                 return 'added'

             if line.indexOf('namespace ') == 0
-                lastUse = index
+                bestUse = i

             # Use keyword
-            if line.indexOf('use') == 0
+            if line.indexOf('use ') == 0
                 useRegex = /(?:use)(?:[^\w\\])([\w\\]+)(?![\w\\])(?:(?:[ ]+as[ ]+)(\w+))?(?:;)/g
                 matches = useRegex.exec(line)

@@ -183,12 +203,75 @@
                 if matches? and matches[1]?
                     if matches[1] == className
                         return 'exists'
-                    else
-                        lastUse = index
-
-            index += 1
+
+                    score = @scoreClassName(className, matches[1])
+
+                    #if lastUse and score < @scoreClassName(lastUse, matches[1])
+                    #    bestScore = 0 # Don't try to squeeze in between good pairs.
+
+                    if score >= bestScore and (not lastUse or score > @scoreClassName(lastUse, matches[1]))
+                        bestUse = i
+                        bestScore = score
+                        placeBelow = if className.length >= matches[1].length then true else false
+
+
+
+
+                        firstClassNameParts = className.split('\\')
+                        secondClassNameParts = matches[1].split('\\')
+
+                        firstClassNameParts.pop()
+                        secondClassNameParts.pop()
+
+                        doNewLine = if firstClassNameParts.join('\\') == secondClassNameParts.join('\\') then false else true
+
+                    lastUse = matches[1]
+
+            else
+                lastUse = null

         return null
+
+
+
+
+    scoreClassName: (firstClassName, secondClassName) ->
+        #fuzzaldrin = require 'fuzzaldrin'
+
+        firstClassNameParts = firstClassName.split('\\')
+        secondClassNameParts = secondClassName.split('\\')
+
+        maxLength = 0
+
+        if firstClassNameParts.length > secondClassNameParts.length
+            maxLength = secondClassNameParts.length
+
+        else
+            maxLength = firstClassNameParts.length
+
+        totalScore = 0
+
+        # NOTE: We don't score the last part.
+        for i in [0 .. maxLength - 1]
+            if firstClassNameParts[i] == secondClassNameParts[i]
+                totalScore += 2
+
+        firstClassNameParts.pop()
+        secondClassNameParts.pop()
+
+        if firstClassNameParts.length == secondClassNameParts.length
+            if firstClassName.length == secondClassName.length
+                totalScore += 2
+
+            else
+                # Stick closer to items that are smaller in length than items that are larger in length.
+                totalScore -= 0.001 * (secondClassName.length - firstClassName.length)
+
+        return totalScore
+
+
+
+

     ###*
      * Checks if the given name is a class or not


Fixed is class or namespace declarations are not lowercase

--- lib/services/php-file-parser.coffee
+++ lib/services/php-file-parser.coffee
@@ -32,12 +32,12 @@
       # a class (see on top of the file)
       if name == ''
         for classDeclaration in classDeclarations
-          if line.indexOf(classDeclaration) == 0
+          if line.toLowerCase().indexOf(classDeclaration) == 0
             line = line.substring(classDeclaration.length, line.length).trim()

             name = line.split(' ')[0]
       else
-        if line.indexOf(namespaceDeclaration) != -1
+        if line.toLowerCase().indexOf(namespaceDeclaration) != -1
           line = line.replace('<?php', '').trim()
           line = line.substring(namespaceDeclaration.length, line.length).trim()


Simplify default structure

Simplify default structure

--- assets/javascripts/app.coffee
+++ assets/javascripts/app.coffee
@@ -20,16 +20,6 @@
       type: 'map'
       id: 'ro'
       args: 'function(value) { return value * value; }'
-    }
-    {
-      type: 'delay'
-      id: 'roo'
-      args: '1000'
-    }
-    {
-      type: 'take',
-      id: 'rooo'
-      args: '2'
     }
   ]
 }


remove logging

remove logging

--- tools/histogram/index.coffee
+++ tools/histogram/index.coffee
@@ -36,9 +36,7 @@

   drawGraph: ({graphData, xAxis, xScale, yScale, selection, height, width}) ->
-    console.log(graphData)
     barWidth = xScale(graphData[1].x) - xScale(graphData[0].x)
-    console.log(barWidth);

     bars = @chart.selectAll('g.bar')
       .data(graphData, (d) -> d.x)


Log when token is missing

Log when token is missing

--- build/tasks/publish-build-task.coffee
+++ build/tasks/publish-build-task.coffee
@@ -37,6 +37,9 @@
       elapsedTime = Math.round((Date.now() - startTime) / 100) / 10
       grunt.log.ok("Upload time: #{elapsedTime}s")
       doneCallback(args...)
+
+    unless token
+      done(new Error('ATOM_ACCESS_TOKEN environment variable not set'))

     buildDir = grunt.config.get('atom.buildDir')
     assets = getAssets()


Extensions should be an array

Extensions should be an array

--- build/tasks/publish-build-task.coffee
+++ build/tasks/publish-build-task.coffee
@@ -58,7 +58,7 @@
       ]
     when 'linux'
       buildDir = grunt.config.get('atom.buildDir')
-      debAsset = fs.listSync(buildDir, '.deb')[0]
+      debAsset = fs.listSync(buildDir, ['.deb'])[0]
       [
         {assetName: path.basename(debAsset), sourcePath: debAsset}
       ]


Fix DOM leak in scatterplot

Fix DOM leak in scatterplot

--- web/site/src/javascript/scatter/ScatterDrawer.coffee
+++ web/site/src/javascript/scatter/ScatterDrawer.coffee
@@ -17,7 +17,7 @@

   _draw_node_status: ->
     @chart ?= new ScatterChart @parent
-    @plot_data ?=
+    @plot_data =
       [{
         key: 'Uncomfortable'
         values: []
@@ -31,7 +31,6 @@
         values: []
       }]

-    console.log @sensors[2].properties.th_times.length, @sensors[2].properties.th_times
     for {properties} in @sensors
       if properties.comf_times.length > 0 and properties.th_times.length > 0
         for comf, i in properties.comfortabilities


ui: Run all tests in suite

ui: Run all tests in suite

--- ui/main.coffee
+++ ui/main.coffee
@@ -48,13 +48,23 @@
 TestsListing = React.createFactory TestsListingClass

 # Running
+asyncSeries = (items, func, callback) ->
+  items = items.slice 0
+  results = []
+  next = () ->
+    if items.length == 0
+      return callback null, results
+    item = items.shift()
+    func item, (err, result) ->
+      return callback err if err
+      results.unshift result
+      return next()
+  next()
+
+
 runAllTests = (runner, suites, updateCallback, doneCallback) ->

-  runner.setupSuite suites[0], (err) ->
-    console.log 'setup suite', err
-    testcase = suites[0].cases[0]
-
-    console.log 'run test', testcase.name
+  runTest = (testcase, callback) ->
     runner.runTest testcase, (err, actual) ->
       console.log 'test assertion', testcase.assertion, err
       error = null
@@ -63,8 +73,20 @@
       catch e
         error = e
       testcase.passed = not error
+      testcase.error = error?.message
       console.log error, testcase.passed
       updateCallback()
+      callback error
+
+  runner.setupSuite suites[0], (err) ->
+    console.log 'setup suite', err
+
+    asyncSeries suites[0].cases, runTest, (err) ->
+      console.log 'testrun complete', err
+
+      runner.teardownSuite suites[0], (err) ->
+        console.log 'teardown suite', err
+

 # Main
 main = () ->


fuzzy tree ignores

fuzzy tree ignores

--- atom/atom.symlink/config.cson
+++ atom/atom.symlink/config.cson
@@ -3,10 +3,31 @@
     disabledPackages: [
       "whitespace"
     ]
+    excludeVcsIgnoredPaths: false
+    ignoredNames: [
+      ".git"
+      ".hg"
+      ".svn"
+      ".DS_Store"
+      "._*"
+      "Thumbs.db"
+      "node_modules"
+      ".nyc_output"
+    ]
+    projectHome: "/Users/simon/code"
     telemetryConsent: "no"
   editor:
     fontSize: 16
   "exception-reporting":
     userId: "b67e2095-f03b-6450-3c77-a0bb42fcdc80"
+  "fuzzy-finder":
+    ignoredNames: [
+      "app/*"
+      "coverage/*"
+      "releases/*"
+      "src/vendor/*"
+    ]
+  "tree-view":
+    hideIgnoredNames: true
   welcome:
     showOnStartup: false


flux: bound stack events for environments

--- client/app/lib/flux/environment/actions.coffee
+++ client/app/lib/flux/environment/actions.coffee
@@ -1,6 +1,18 @@
 kd                      = require 'kd'
 environmentDataProvider = require 'app/userenvironmentdataprovider'
 actions                 = require './actiontypes'
+
+
+_bindStackEvents = ->
+
+  { reactor, computeController } = kd.singletons
+
+  computeController.on 'StackRevisionChecked', (stack) ->
+
+    return  if _revisionStatus?.error? and not stack._revisionStatus.status
+
+    reactor.dispatch actions.STACK_UPDATED, stack
+

 loadMachines = do (isPayloadUsed = no) ->->
@@ -43,6 +55,7 @@
       else
         reactor.dispatch actions.LOAD_USER_STACKS_SUCCESS, stacks
         resolve stacks
+        _bindStackEvents()

 module.exports = {


some changes

some changes

--- client/landing/app/MainApp/kite/newkite.coffee
+++ client/landing/app/MainApp/kite/newkite.coffee
@@ -11,7 +11,7 @@
     @remoteStore  = new Store
     @autoReconnect = true
     @readyState = NOTREADY
-    @kontrolEndpoint = "http://localhost:4000/request" #kontrol addr
+    @kontrolEndpoint = "http://127.0.0.1:4000/request" #kontrol addr
     @token = ""
     @addr = ""
     @initBackoff options  if @autoReconnect
@@ -69,7 +69,7 @@
     try
       args = JSON.parse evt.data
       {method} = args
-      console.log "received", {args}
+      # console.log "received", {args}
       callback = @localStore.get method
       callback.apply this, @unscrub args
     catch e
@@ -131,9 +131,9 @@
     try
       if @readyState is READY
         @ws.send JSON.stringify data
-        console.log "sending", {data}
+        # console.log "sending", {data}
       else
-        console.log "slow down ... I'm still trying to reconnect!"
+        # console.log "slow down ... I'm still trying to reconnect!"
     catch e
       @disconnect()


Instead of before and remove, use jquery replaceWith

--- client/src/controllers/admin_controller.coffee
+++ client/src/controllers/admin_controller.coffee
@@ -61,8 +61,7 @@
   spreadsheetKey = $(this).attr('data-spreadsheet-key')
   parentEl = $(this).parent()

-  $(this).before(createSpinner())
-  $(this).remove()
+  $(this).replaceWith(createSpinner())

   $.ajax
     method: "POST"


Prevent scoring models if the response columns don't match. PP-8

--- client/src/scripts/model-selection-view.coffee
+++ client/src/scripts/model-selection-view.coffee
@@ -6,8 +6,15 @@
     selections.length > 0
   _predicate = node$ null
   _frameKey = lift$ _predicate, (predicate) -> if predicate then predicate.frameKey else ''
-  _canScore = lift$ _hasSelection, _predicate, (hasSelection, predicate) ->
-    hasSelection and predicate isnt null and predicate.type is 'compatibleWithFrame'
+  _canScore = lift$ _predicate, _selections, (predicate, selections) ->
+    if predicate isnt null
+      if predicate.type is 'compatibleWithFrame'
+        if selections.length > 0
+          responseColumn = (head selections).responseColumn
+          if (every selections, ((selection) -> selection.responseColumn is responseColumn))
+            return yes
+    no
+

   apply$ _hasSelection, (hasSelection) ->
     if hasSelection


taking out unnecessary function

taking out unnecessary function

--- src/components/exercise-identifier-link.cjsx
+++ src/components/exercise-identifier-link.cjsx
@@ -11,11 +11,6 @@
     bookUUID: React.PropTypes.string
     exerciseId: React.PropTypes.string.isRequired
     project: React.PropTypes.oneOf(['concept-coach', 'tutor'])
-    related_content: React.PropTypes.array.isRequired
-
-  buildLabel: (related) ->
-    chapterSection = @sectionFormat(related.chapter_section, @props.sectionSeparator)
-    "Comes from #{chapterSection} - #{related.title}"

   render: ->
     url = Exercise.troubleUrl(@props)


add more functionality for testing the user search database functionality, esp given the changefeed version

--- salvus/test/rethink-test.coffee
+++ salvus/test/rethink-test.coffee
@@ -283,6 +283,30 @@
             query : "sageBLAH@sagemath.com"
             cb    : (err, users) -> expect(users.length).toBe(0); done(err)

+    account_id = undefined
+    it "adds another user", (done) ->
+        db.create_account(first_name:"FOO", last_name:"BAR", created_by:"1.2.3.4",\
+                  email_address:"foo@sagemath.com", password_hash:"sage", cb:(err, x) -> account_id=x; done(err))
+    it "then checks that the new user is found by first name", (done) ->
+        db.user_search
+            query : "FOO"
+            cb    : (err, users) -> expect(users.length).toBe(1); done(err)
+    it "then checks that the new user is found by last name", (done) ->
+        db.user_search
+            query : "BAR"
+            cb    : (err, users) -> expect(users.length).toBe(1); done(err)
+    it "changes that user in place", (done) ->
+        db.table('accounts').get(account_id).update(first_name:'VERT', last_name:'RAMP').run(done)
+    it "then checks that the modified user is found", (done) ->
+        db.user_search
+            query : "VERT"
+            cb    : (err, users) -> expect(users.length).toBe(1); done(err)
+    it "but the previous name is not found", (done) ->
+        db.user_search
+            query : "BAR"
+            cb    : (err, users) -> expect(users.length).toBe(0); done(err)
+
+
 describe 'banning of users: ', ->
     before(setup)
     after(teardown)


db: some more tests...

db: some more tests...

--- salvus/test/rethink-test.coffee
+++ salvus/test/rethink-test.coffee
@@ -21,37 +21,97 @@
         else
             cb()

-test_create_account = (cb) ->
-    async.series([
-        (cb) ->
-            setup(cb)
-        (cb) ->
-            db.create_account(first_name:"Sage", last_name:"Salvus", created_by:"1.2.3.4",\
-                              email_address:"sage@example.com", password_hash:"blah", cb:cb)
-        (cb) ->
-            db.create_account(first_name:"Mr", last_name:"Smith", created_by:"10.10.1.1",\
-                              email_address:"sage-2@example.com", password_hash:"foo", cb:cb)
-        (cb) ->
-            db.get_stats
-                cb: (err, stats) ->
-                    cb(err or stats.accounts != 2)
-        (cb) ->
-            db.get_account
-                email_address:'sage-2@example.com'
-                cb:(err, account) ->
-                    if err
-                        cb(err)
-                    else
-                        cb(account.first_name=="Mr" and account.password_is_set)
-        (cb) ->
-            teardown(cb)
-    ], cb)
-
-describe 'create_account', ->
+describe 'working with accounts', ->
+    @timeout(5000)
+    before(setup)
+    after(teardown)
     it 'test creating accounts', (done) ->
-        @timeout(5000)
-        test_create_account (err) ->
+        async.series([
+            (cb) ->
+                db.account_exists
+                    email_address:'sage@example.com'
+                    cb:(err, exists) -> cb(err or exists)
+            (cb) ->
+                db.create_account(first_name:"Sage", last_name:"Salvus", created_by:"1.2.3.4",\
+                                  email_address:"sage@example.com", password_hash:"blah", cb:cb)
+            (cb) ->
+                db.account_exists
+                    email_address:'sage@example.com'
+                    cb:(err, exists) -> cb(err or not exists)
+            (cb) ->
+                db.create_account(first_name:"Mr", last_name:"Smith", created_by:"10.10.1.1",\
+                                  email_address:"sage-2@example.com", password_hash:"foo", cb:cb)
+            (cb) ->
+                db.get_stats
+                    cb: (err, stats) ->
+                        cb(err or stats.accounts != 2)
+            (cb) ->
+                db.get_account
+                    email_address:'sage-2@example.com'
+                    cb:(err, account) ->
+                        if err
+                            cb(err)
+                        else
+                            cb(account.first_name=="Mr" and account.password_is_set)
+            (cb) ->
+                db.count_accounts_created_by
+                    ip_address : '1.2.3.4'
+                    age_s      : 1000000
+                    cb         : (err, n) ->
+                        cb(err or n != 1)
+        ], (err) ->
             assert.equal(err, undefined)
             done()
+        )
+
+    it 'test deleting accounts', (done) ->
+        account_id = undefined
+        n_start = undefined
+        async.series([
+            (cb) ->
+                db.table('accounts').count().run (err, n) ->
+                    n_start = n; cb(err)
+            (cb) ->
+                db.get_account
+                    email_address:'sage-2@example.com'
+                    cb : (err, account) ->
+                        account_id = account?.account_id
+                        cb(err)
+            (cb) ->
+                db.delete_account
+                    account_id : account_id
+                    cb         : cb
+            (cb) ->
+                db.table('accounts').count().run (err, n) ->
+                    assert.equal(n, n_start-1)
+                    cb(err)
+        ], (err) ->
+            assert.equal(err, undefined)
+            done()
+        )
+
+describe 'working with logs', ->
+    @timeout(5000)
+    before(setup)
+    after(teardown)
+    it 'test central log', (done) ->
+        async.series([
+            (cb) ->
+                db.log
+                    event : "test"
+                    value : "a message"
+                    cb    : cb
+            (cb) ->
+                db.get_log
+                    start : new Date(new Date() - 10000000)
+                    end   : new Date()
+                    event : 'test'
+                    cb    : (err, log) ->
+                        cb(err or log.length != 1 or log[0].event != 'test' or log[0].value != 'a message')
+        ], (err) ->
+            assert.equal(err, undefined)
+            done()
+        )

+


Use correct error message for course ended

--- coach/src/task/errors.cjsx
+++ coach/src/task/errors.cjsx
@@ -8,10 +8,11 @@

 CourseEnded = React.createClass
-  displayName: 'NoExercises'
+  displayName: 'CourseEnded'
   render: ->
-

-      Sorry, there are no exercises for this module.
+

+      This Concept Coach course has ended. Click My Progress
+      at the top of the screen to access all previous work for this course.


Correct name of expireAfterSeconds index

--- services/web/app/coffee/models/ProjectInvite.coffee
+++ services/web/app/coffee/models/ProjectInvite.coffee
@@ -16,7 +16,7 @@
 		sendingUserId:  ObjectId
 		projectId:      ObjectId
 		privileges:     String
-		createdAt:      {type: Date, default: Date.now, index: {expiresAfterSeconds: THIRTY_DAYS_IN_SECONDS}}
+		createdAt:      {type: Date, default: Date.now, index: {expireAfterSeconds: THIRTY_DAYS_IN_SECONDS}}
 	},
 	{
 		collection: 'projectInvites'


Lint style.

Lint style.

--- server/migrations/0018-publication-cacheid.coffee
+++ server/migrations/0018-publication-cacheid.coffee
@@ -17,7 +17,8 @@
       return callback error if error
       cursor = collection.find {_schema: currentSchema, cachedId: {$exists: false}}, {cached: 1, source: 1, foreignId: 1}
       document = null
-      async.doWhilst Meteor.bindEnvironment((callback) =>
+      async.doWhilst Meteor.bindEnvironment(
+        (callback) =>
           cursor.nextObject Meteor.bindEnvironment (error, doc) =>
             return callback error if error
             document = doc
@@ -58,7 +59,8 @@
       return callback error if error
       cursor = collection.find {_schema: currentSchema, cachedId: $exists: true}, {cached: 1, cachedId: 1, source: 1, foreignId: 1}
       document = null
-      async.doWhilst Meteor.bindEnvironment((callback) =>
+      async.doWhilst Meteor.bindEnvironment(
+        (callback) =>
           cursor.nextObject Meteor.bindEnvironment (error, doc) =>
             return callback error if error
             document = doc


Replace direct call to upstream.clear() with manager clear()

This takes advantage of the 'clear' events being called which enables
bound handlers to reset their state.

--- src/coffee/cilantro/models/context/manager.coffee
+++ src/coffee/cilantro/models/context/manager.coffee
@@ -64,8 +64,7 @@
         # Updates the working and upstream trees with the server's response
         set: (attrs) ->
             if not attrs?
-                @upstream.clear(reset: true)
-                return
+                return @clear()

             # Update `upstream` tree with server response. Other than
             # annotations on nodes themselves, nothing should change. The
@@ -188,8 +187,8 @@
             @upstream.clear(reset: true)
             @upstream.trigger('clear')
             @working.trigger('clear')
-            @save()
-            return @
+            if @upstream.hasChanged()
+                @save()

         # Returns true if the node is not found upstream.
         isNew: (ident) ->


Update return statements when required checks fail

--- src/coffee/cilantro/models/context/manager.coffee
+++ src/coffee/cilantro/models/context/manager.coffee
@@ -167,7 +167,8 @@
                 @_triggerRequired [n.get('concept')]
                 return false

-            if not @_checkRequired() then return
+            if not @_checkRequired()
+                return false

             if (u = @upstream.find(ident))
                 u.destroy()
@@ -181,14 +182,15 @@
         apply: (ident, options) ->
             ident = ident.identity?() or ident

-            if not @_checkRequired() then return
-
-            if not (n = @find(ident))
-                return
+            if not @_checkRequired()
+                return false
+
+            if not (n = @find(ident))
+                return false

             # No attributes, which means this is not valid
             if not (attrs = n.toJSON())
-                return
+                return false

             # Define upstream, this is idempotent
             u = @upstream.define(ident, n.path(), type: n.type)
@@ -235,13 +237,14 @@
             ident = ident.identity?() or ident

             if not (n = @find(ident))
-                return
+                return false

             if @_isRequired(n)
                 @_triggerRequired [n.get('concept')]
-                return
-
-            if not @_checkRequired() then return
+                return false
+
+            if not @_checkRequired()
+                return false

             if (u = @upstream.find(ident))
                 u.set(enabled: false)
@@ -254,7 +257,7 @@
         clear: (options={}) ->
             if @_hasRequired()
                 @_triggerRequired()
-                return
+                return false

             @upstream.clear(reset: true)


config: remove unused kites.brokerExchange config

--- config/main.vagrant.coffee
+++ config/main.vagrant.coffee
@@ -234,7 +234,6 @@
   kites:
     disconnectTimeout: 3e3
     vhost       : 'kite'
-    brokerExchange: 'brokerKite'
   email         :
     host        : 'localhost'
     protocol    : 'http:'


Ace: Improve file content change handling.

--- client/ace/lib/ace.coffee
+++ client/ace/lib/ace.coffee
@@ -343,10 +343,8 @@

     contents = @getContents()

-    unless contents is '' or @isContentChanged()
-      if @getDelegate().parent.active
-        @notify 'Nothing to save!'
-      return
+    if contents is '' or not @isCurrentContentChanged()
+      return @notify 'Nothing to save!'  if @getDelegate().parent.active

     if @isTrimWhiteSpacesEnabled
       @trimTrailingWhitespaces options.ignoreActiveLineOnTrim


Activity: IDE to/from Activity switching scroll problem fixed

--- client/Social/Activity/AppView.coffee
+++ client/Social/Activity/AppView.coffee
@@ -27,12 +27,12 @@
     @tabs = new KDTabView
       tagName             : 'main'
       hideHandleContainer : yes
+    @tabs.unsetClass 'kdscrollview'

     @tabs.on 'PaneDidShow', (pane) =>
       if type = pane.getData()?.typeConstant
         @tabs.setAttribute 'class', KD.utils.curry 'kdview kdtabview', type

-    @tabs.unsetClass 'kdscrollview'

   viewAppended: ->

@@ -42,6 +42,15 @@
     @sidebar     = mainView.activitySidebar
     @addSubView @tabs
     @addSubView @widgetsBar
+
+    @parent.on 'PaneDidShow', =>
+
+      return  unless pane = @tabs.getActivePane()
+
+      KD.utils.defer ->
+        pane.applyScrollTops()
+        pane.scrollView.verticalTrack.thumb.handleMutation()
+        pane.scrollView.horizontalTrack.thumb.handleMutation()

   scroll: ->


fixed an issue where the test for escaped path would not check for generic whitespace

--- client/app/MainApp/fs/fshelper.coffee
+++ client/app/MainApp/fs/fshelper.coffee
@@ -116,7 +116,7 @@
     return /^([a-zA-Z]:\\)?[^\x00-\x1F"<>\|:\*\?/]+$/.test name

   @isEscapedPath = (path) ->
-    return /^ \"/.test path
+    return /^\s\"/.test path

   @escapeFilePath = (name) ->


Fix new event duration in week view, refs #364

--- client/app/views/calendar_view.coffee
+++ client/app/views/calendar_view.coffee
@@ -196,6 +196,9 @@
             endDate.subtract 1, 'days'
             startDate = startDate.format() + 'T10:00:00.000'
             endDate = endDate.format() + 'T11:00:00.000'
+        else if @view is 'agendaWeek'
+            # set default event duration to 1h
+            endDate = startDate.clone().add 1, 'hour'

         start = helpers.ambiguousToTimezoned startDate
         end = helpers.ambiguousToTimezoned endDate


Nodejs Web Server: use csrf middleware for reset handler

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -94,7 +94,7 @@
 app.post '/:name?/Login'                         , csrf,   require './handlers/login'
 app.post '/Impersonate/:nickname'                , require './handlers/impersonate'
 app.post '/:name?/Recover'                       , csrf,   require './handlers/recover'
-app.post '/:name?/Reset'                         , require './handlers/reset'
+app.post '/:name?/Reset'                         , csrf,   require './handlers/reset'
 app.post '/:name?/Optout'                        , require './handlers/optout'
 app.all  '/:name?/Logout'                        , csrf,   require './handlers/logout'
 app.get  '/humans.txt'                           , generateHumanstxt


Fix function matcher regex

Fix function matcher regex

--- source/ddry/lib/common/object/report/methods.coffee
+++ source/ddry/lib/common/object/report/methods.coffee
@@ -31,7 +31,7 @@
   format: (value) ->
     if typeof value is 'function'
       definition = "#{value}".replace(/ /g, '')
-      return definition.replace(/__cov_[^\+]*\+\+;/g, '')
+      return definition.replace(/[_]*cov_[^\+]*\+\+;/g, '')
     return value if Array.isArray value
     String value


legal: add numbered lists to copyright

legal: add numbered lists to copyright

--- client/legal/lib/copyrightview.coffee
+++ client/legal/lib/copyrightview.coffee
@@ -29,7 +29,7 @@
         appropriate action:

-

+

           An electronic or physical signature of a person authorized to act on
           behalf of the copyright owner;
@@ -70,7 +70,7 @@
         legal counsel or see 17 U.S.C. Section 512(g)(3) to confirm these requirements):

-

+

           A physical or electronic signature of the user.


Fixing Issue #738 -- rewriter skips a token.

--- rewriter.coffee
+++ rewriter.coffee
@@ -207,7 +207,7 @@
           @tokens.splice idx, 0, outdent
         @detectEnd i + 2, condition, action
         @tokens.splice i, 1 if token[0] is 'THEN'
-        return 2
+        return 1
       return 1

   # Tag postfix conditionals as such, so that we can parse them with a


Rework some spec contexts

Rework some spec contexts

--- spec_app/spec/javascripts/up/browser_spec.js.coffee
+++ spec_app/spec/javascripts/up/browser_spec.js.coffee
@@ -117,43 +117,43 @@

     describe 'up.browser.sprintf', ->

-      describe '(string argument)', ->
+      describe 'with string argument', ->

         it 'serializes with surrounding quotes', ->
           formatted = up.browser.sprintf('before %o after', 'argument')
           expect(formatted).toEqual('before "argument" after')

-      describe '(undefined argument)', ->
+      describe 'with undefined argument', ->

         it 'serializes to the word "undefined"', ->
           formatted = up.browser.sprintf('before %o after', undefined)
           expect(formatted).toEqual('before undefined after')

-      describe '(null argument)', ->
+      describe 'with null argument', ->

         it 'serializes to the word "null"', ->
           formatted = up.browser.sprintf('before %o after', null)
           expect(formatted).toEqual('before null after')

-      describe '(number argument)', ->
+      describe 'with number argument', ->

         it 'serializes the number as string', ->
           formatted = up.browser.sprintf('before %o after', 5)
           expect(formatted).toEqual('before 5 after')

-      describe '(function argument)', ->
+      describe 'with function argument', ->

         it 'serializes the function code', ->
           formatted = up.browser.sprintf('before %o after', `function foo() {}`)
           expect(formatted).toEqual('before function foo() {} after')

-      describe '(array argument)', ->
+      describe 'with array argument', ->

         it 'recursively serializes the elements', ->
           formatted = up.browser.sprintf('before %o after', [1, "foo"])
           expect(formatted).toEqual('before [1, "foo"] after')

-      describe '(element argument)', ->
+      describe 'with element argument', ->

         it 'serializes the tag name with id, name and class attributes, but ignores other attributes', ->
           $element = $('
')
@@ -161,7 +161,7 @@
           formatted = up.browser.sprintf('before %o after', element)
           expect(formatted).toEqual('before
 after')

-      describe '(jQuery argument)', ->
+      describe 'with jQuery argument', ->

         it 'serializes the tag name with id, name and class attributes, but ignores other attributes', ->
           $element1 = $('
')
@@ -169,7 +169,7 @@
           formatted = up.browser.sprintf('before %o after', $element1.add($element2))
           expect(formatted).toEqual('before $(
,
) after')

-      describe '(object argument)', ->
+      describe 'with object argument', ->

         it 'serializes to JSON', ->
           object = { foo: 'foo-value', bar: 'bar-value' }


remove policy enforcement from login form

--- client/views/pages/login/login.coffee
+++ client/views/pages/login/login.coffee
@@ -7,9 +7,6 @@
 	Session.set 'error', ''

 Template.login.events
-
-	'keyup #username': (e) ->
-		$('#username').val(e.target.value.trim().toLowerCase().replace(/[^a-z0-9]+/g, ''))

 	'submit form': (event) ->
 		event.preventDefault()


Add `BUILD_ID` to apiary reporter CI var check

--- src/reporters/apiary-reporter.coffee
+++ src/reporters/apiary-reporter.coffee
@@ -77,7 +77,7 @@
       # Cycle through all keys from
       # - config.custom.apiaryReporterEnv
       # - process.env keys
-      ciVars = /^(TRAVIS|CIRCLE|CI|DRONE)/
+      ciVars = /^(TRAVIS|CIRCLE|CI|DRONE|BUILD_ID)/
       envVarNames = @_getKeys()
       ciEnvVars = {}
       for envVarName in envVarNames when envVarName.match(ciVars)?


Fixed login redirect

Fixed login redirect

--- src/server/common/AuthService.coffee
+++ src/server/common/AuthService.coffee
@@ -34,12 +34,6 @@
 				if err then return done err
 				done null, token

-getLoginRedirect = (req) ->
-	id = req.oauth2.transactionID
-	user = req.user
-	client = req.oauth2.client
-	string = "/login?transactionID=#{id}&user=#{user}&client=#{client}"
-
 service = {}

 service.authorization = [
@@ -55,7 +49,14 @@
 				###
 				done null, client, redirectURI
 		(req, res) ->
-			res.redirect getLoginRedirect req
+			id = req.oauth2.transactionID
+			user = req.user
+			client = req.oauth2.client
+			# TODO Need to create /authorize module on front end.
+			# It'll tell an authenticated user "this app requests these permissions."
+			# The user can then accept or deny.
+			redirect = "/authorize?transactionID=#{id}&user=#{user}&client=#{client}"
+			res.redirect redirect
 	]

 service.decision = [


remove jqXHR property in cached ETags class. add methods for get and set ETags caches, let user saving and restoring caches

--- octokit.coffee
+++ octokit.coffee
@@ -36,7 +36,7 @@

       # To support ETag caching cache the responses.
       class ETagResponse
-        constructor: (@eTag, @data, @textStatus, @jqXHR) ->
+        constructor: (@eTag, @data, @textStatus) ->

       # Cached responses are stored in this object keyed by `path`
       _cachedETags = {}
@@ -131,7 +131,7 @@
             if clientOptions.useETags and _cachedETags[path]
               eTagResponse = _cachedETags[path]

-              promise.resolve(eTagResponse.data, eTagResponse.textStatus, eTagResponse.jqXHR)
+              promise.resolve(eTagResponse.data, eTagResponse.textStatus, jqXHR)
             else
               promise.resolve(jqXHR.responseText, textStatus, jqXHR)

@@ -155,7 +155,7 @@
             # Cache the response to reuse later
             if 'GET' == method and jqXHR.getResponseHeader('ETag') and clientOptions.useETags
               eTag = jqXHR.getResponseHeader('ETag')
-              _cachedETags[path] = new ETagResponse(eTag, data, textStatus, jqXHR)
+              _cachedETags[path] = new ETagResponse(eTag, data, textStatus)

             promise.resolve(data, textStatus, jqXHR)

@@ -198,6 +198,15 @@
       # Clear the local cache
       # -------
       @clearCache = clearCache = () -> _cachedETags = {}
+
+      ## Get the local cache
+      # -------
+      @getCache = getCache = () -> _cachedETags
+
+      ## Set the local cache
+      # -------
+      @setCache = setCache = (cachedETags) ->
+        _cachedETags = _.extend {}, _cachedETags, cachedETags

       # Add a listener that fires when the `rateLimitRemaining` changes as a result of
       # communicating with github.


update to latest coffeescript

update to latest coffeescript

--- coffee/iphone-style-checkboxes.coffee
+++ coffee/iphone-style-checkboxes.coffee
@@ -2,14 +2,14 @@
 # Copyright Thomas Reynolds, licensed GPL & MIT

 # Constructor
-$.iphoneStyle: (elem, options) ->
+$.iphoneStyle = (elem, options) ->
   @elem = $(elem)

   # Import options into instance variables
-  obj: this
+  obj = this

   for key, value of options
-    obj[key]: value
+    obj[key] = value

   # Initialize the control
   @wrapCheckboxWithDivs()
@@ -21,35 +21,35 @@

   @initialPosition()

-$.iphoneStyle::isDisabled: ->
+$.iphoneStyle::isDisabled = ->
   return @elem.filter(':disabled').length

 # Wrap the existing input[type=checkbox] with divs for styling and grab DOM references to the created nodes
-$.iphoneStyle::wrapCheckboxWithDivs: ->
+$.iphoneStyle::wrapCheckboxWithDivs = ->
   @elem.wrap("
");

-  @container: @elem.parent()
-
-  @offLabel:  $("" +
-                  "$@uncheckedLabel" +
-                "").appendTo(@container)
+  @container = @elem.parent()
+
+  @offLabel  = $("" +
+                   "$@uncheckedLabel" +
+                 "").appendTo(@container)

-  @offSpan:   @offLabel.children('span')
-
-  @onLabel:   $("" +
+  @offSpan   = @offLabel.children('span')
+
+  @onLabel   = $("" +
                   "$@checkedLabel" +
                 "").appendTo(@container)

-  @onSpan:    @onLabel.children('span')
-
-  @handle:    $("
" +
-                  "
" +
-                    "
" +
-                  "
" +
-                "
").appendTo(this.container)
+  @onSpan    = @onLabel.children('span')
+
+  @handle    = $("
" +
+                   "
" +
+                     "
" +
+                   "
" +
+                 "
").appendTo(this.container)

 # Disable IE text selection, other browsers are handled in CSS
-$.iphoneStyle::disableTextSelection: ->
+$.iphoneStyle::disableTextSelection = ->
   return if not $.browser.msie

   # Elements containing text should be unselectable
@@ -57,18 +57,18 @@
     $(el).attr("unselectable", "on")

 # Automatically resize the handle or container
-$.iphoneStyle::optionallyResize: (mode) ->
-  onLabelWidth:  @onLabel.width()
-  offLabelWidth: @offLabel.width()
-  newWidth:      if (onLabelWidth < offLabelWidth) then onLabelWidth else offLabelWidth
+$.iphoneStyle::optionallyResize = (mode) ->
+  onLabelWidth  = @onLabel.width()
+  offLabelWidth = @offLabel.width()
+  newWidth      = if (onLabelWidth < offLabelWidth) then onLabelWidth else offLabelWidth

   if mode is "container"
     newWidth += @handle.width() + 15
-    @container.css({ width: newWidth })
+    @container.css width: newWidth
   else
-    @handle.css({ width: newWidth })
-
-$.iphoneStyle::attachEvents: ->
+    @handle.css width: newWidth
+
+$.iphoneStyle::attachEvents = ->
   # A mousedown anywhere in the control will start tracking for dragging
   @container.bind 'mousedown touchstart', (event) =>
     event.preventDefault()
@@ -76,9 +76,9 @@
     return if @isDisabled()

     x: event.pageX || event.originalEvent.changedTouches[0].pageX
-    $.iphoneStyle.currentlyClicking: @handle
-    $.iphoneStyle.dragStartPosition: x
-    $.iphoneStyle.handleLeftOffset:  parseInt(@handle.css('left'), 10) || 0
+    $.iphoneStyle.currentlyClicking = @handle
+    $.iphoneStyle.dragStartPosition = x
+    $.iphoneStyle.handleLeftOffset  = parseInt(@handle.css('left'), 10) || 0

   # Utilize event bubbling to handle drag on any element beneath the container
@@ -87,13 +87,13 @@

     return if @isDisabled()

-    p: (x + $.iphoneStyle.handleLeftOffset - $.iphoneStyle.dragStartPosition) / @rightSide
-    p: 0 if p < 0
-    p: 1 if p > 1
-
-    @handle.css({         left: p * @rightSide })
-    @onLabel.css({       width: p * @rightSide + 4 })
-    @offSpan.css({ marginRight: -p * @rightSide })
+    p = (x + $.iphoneStyle.handleLeftOffset - $.iphoneStyle.dragStartPosition) / @rightSide
+    p = 0 if p < 0
+    p = 1 if p > 1
+
+    @handle.css({         left: p * @rightSide        })
+    @onLabel.css({       width: p * @rightSide + 4    })
+    @offSpan.css({ marginRight: -p * @rightSide       })
     @onSpan.css({   marginLeft: -(1 - p) * @rightSide })

   # Utilize event bubbling to handle drag end on any element beneath the container
@@ -101,13 +101,13 @@
     return if @isDisabled()

     if $.iphoneStyle.dragging
-      p: (x - $.iphoneStyle.dragStartPosition) / @rightSide
+      p = (x - $.iphoneStyle.dragStartPosition) / @rightSide
       @elem.attr('checked', (p >= 0.5))
     else
       @elem.attr('checked', !@elem.attr('checked'))

-    $.iphoneStyle.currentlyClicking: null
-    $.iphoneStyle.dragging: null
+    $.iphoneStyle.currentlyClicking = null
+    $.iphoneStyle.dragging          = null
     @elem.change()

   # Animate when we get a change event
@@ -120,51 +120,51 @@

     new_left: if @elem.attr('checked') then @rightSide else 0

-    @handle.animate({         left: new_left },              @duration)
-    @onLabel.animate({       width: new_left + 4 },          @duration)
-    @offSpan.animate({ marginRight: -new_left },             @duration)
+    @handle.animate({         left: new_left              }, @duration)
+    @onLabel.animate({       width: new_left + 4          }, @duration)
+    @offSpan.animate({ marginRight: -new_left             }, @duration)
     @onSpan.animate({   marginLeft: new_left - @rightSide }, @duration)

   # Setup the control's inital position
-$.iphoneStyle::initialPosition: ->
+$.iphoneStyle::initialPosition = ->
   @offLabel.css({ width: @container.width() - 5 })

-  offset: if $.browser.msie and $.browser.version < 7 then 3 else 6
-  @rightSide: @container.width() - @handle.width() - offset
+  offset     = if $.browser.msie and $.browser.version < 7 then 3 else 6
+  @rightSide = @container.width() - @handle.width() - offset

   if @elem.filter(':checked').length
-    @handle.css({         left: @rightSide })
+    @handle.css({         left: @rightSide     })
     @onLabel.css({       width: @rightSide + 4 })
-    @offSpan.css({ marginRight: -@rightSide })
+    @offSpan.css({ marginRight: -@rightSide    })
   else
-    @onLabel.css({     width: 0 })
+    @onLabel.css({     width: 0           })
     @onSpan.css({ marginLeft: -@rightSide })

   @container.addClass(@disabledClass) if @isDisabled()

-$.fn.iphoneStyle: (options) ->
-  checkboxes: this.filter(':checkbox')
+$.fn.iphoneStyle = (options) ->
+  checkboxes = this.filter(':checkbox')

   # Fail early if we don't have any checkboxes passed in
   return this if not checkboxes.length

   # Merge options passed in with global defaults
-  opt: $.extend({}, $.iphoneStyle.defaults, options)
+  opt = $.extend({}, $.iphoneStyle.defaults, options)

   for checkbox in checkboxes
     $(checkbox).data("iphoneStyle", new $.iphoneStyle(checkbox, opt))

-  if (!$.iphoneStyle.initComplete)
+  if not $.iphoneStyle.initComplete
     # As the mouse moves on the page, animate if we are in a drag state
     $(document).bind 'mousemove touchmove', (event) =>
       return if not $.iphoneStyle.currentlyClicking
       event.preventDefault()

-      x: event.pageX || event.originalEvent.changedTouches[0].pageX
-
-      if (!$.iphoneStyle.dragging &&
+      x = event.pageX || event.originalEvent.changedTouches[0].pageX
+
+      if (not $.iphoneStyle.dragging &&
           (Math.abs($.iphoneStyle.dragStartPosition - x) > opt.dragThreshold))
-        $.iphoneStyle.dragging: true
+        $.iphoneStyle.dragging = true

       $(event.target).trigger 'iPhoneDrag', [x]

@@ -173,14 +173,14 @@
       return if not $.iphoneStyle.currentlyClicking
       event.preventDefault()

-      x: event.pageX || event.originalEvent.changedTouches[0].pageX
+      x = event.pageX || event.originalEvent.changedTouches[0].pageX
       $($.iphoneStyle.currentlyClicking).trigger 'iPhoneDragEnd', [x]

-    $.iphoneStyle.initComplete: true
+    $.iphoneStyle.initComplete = true

   this

-$.iphoneStyle.defaults: {
+$.iphoneStyle.defaults = {
   # Time spent during slide animation
   duration:          200

@@ -206,4 +206,4 @@

   # Pixels that must be dragged for a click to be ignored
   dragThreshold:     5
-}+}


Home: reverted wrong commit

Home: reverted wrong commit

--- client/Intro/includes.coffee
+++ client/Intro/includes.coffee
@@ -1,4 +1,4 @@
 module.exports = [
-  "home.coffee",
-  "styl/home.styl"
+  "main.coffee",
+  "main.styl"
 ]


Terminal: Since Webterm.Terminal is not a KDObject destroy won’t help, so we need to destroy @keyinput when the container destroyed

--- client/Terminal/src/Terminal.coffee
+++ client/Terminal/src/Terminal.coffee
@@ -23,8 +23,8 @@
     @keyInput = new KDCustomHTMLView
       tagName: 'input'
       cssClass: 'offscreen'
-
     @keyInput.appendToDomBody()
+    containerView.on 'KDObjectWillBeDestroyed', @keyInput.bound 'destroy'

     @currentWidth             = 0
     @currentHeight            = 0
@@ -77,10 +77,6 @@
       sessionEnded: =>
         @sessionEndedCallback()

-  destroy: ->
-    @keyInput?.destroy()
-    super()
-
   keyDown: (event) ->
     @inputHandler.keyDown event


A small fix to the answer dice deletion

A small fix to the answer dice deletion

--- client/src/Screen/GameScreen.coffee
+++ client/src/Screen/GameScreen.coffee
@@ -192,6 +192,7 @@
     $("#goal-dice-ctnr").html(DiceFace.listToHtml(Game.getGoalValues()))

+  colorDice: () ->

@@ -199,20 +200,20 @@
   addDiceToAnswerArea: (index) ->
     # Only let him add dice to the area if his brackets are complete
     # and if the dice hasn't already been added
-
     if(@equationBuilder.hasCompletedBrackets() && $.inArray(index, @answerAreaDice) is -1)
-      @answerAreaDice.push(index)
       thisReference = this
-      # Work out the dice we want to add to the answer area based on index
+      # Work out dice face from global dice array then make the html. add to list. add to dom
+      @answerAreaDice.push(index)
       diceFace = DiceFace.faceToHtml(Game.globalDice[index])
       html = "
#{diceFace}
"
       # Add it to the answers
       $(@equationBuilder.addDiceToEnd(html)).bind 'click', (event) ->
-
-        thisReference.removeDiceFromAnswerArea($(this).data('index'));
-
-
-  removeDiceFromAnswerArea: (index) ->
+        pos = $('li.dice[data-index="#{index}"]').index('ul#answers')
+        thisReference.removeDiceFromAnswerArea(pos, $(this).data('index'));
+
+
+  removeDiceFromAnswerArea: (pos, index) ->
+    @answerAreaDice.splice(pos, 1)
     removedElement = @equationBuilder.removeDiceByIndex(index)


KDEventEmitter: code style

KDEventEmitter: code style

avoid clobbering the window event, since we're needing to do this stateful this.event thing to support wildcards, and maintain a reasonable API

--- core/KDEventEmitter.coffee
+++ core/KDEventEmitter.coffee
@@ -174,6 +174,8 @@
     return

   emit: (eventName, rest...) ->
+    "use strict"
+
     oldEvent = @event  if @hasOwnProperty 'event'

     @event = eventName


do not report ridiculous ping times.

do not report ridiculous ping times.

--- src/smc-webapp/client_browser.coffee
+++ src/smc-webapp/client_browser.coffee
@@ -294,6 +294,12 @@
             #log("pong latency=#{conn.latency}")
             if not window.document.hasFocus? or window.document.hasFocus()
                 # networking/pinging slows down when browser not in focus...
+                if conn.latency > 10000
+                    # We get some ridiculous values from Primus when the browser
+                    # tab gains focus after not being in focus for a while (say on ipad but on many browsers)
+                    # that throttle.  Just discard them, since otherwise they lead to ridiculous false
+                    # numbers displayed in the browser.
+                    return
                 @emit "ping", conn.latency

         #conn.on 'outgoing::ping', () =>


fixed

fixed

--- source/parrot.initialize.coffee
+++ source/parrot.initialize.coffee
@@ -46,5 +46,13 @@
       obj = parrot.url._URLS[arguments[0]] or url: arguments[0] if typeof arguments[0] is 'string'
       if (obj.query and obj.path)
         obj.url = "#{_url}/#{obj.path}?#{obj.query}"
+      else
+        obj.url = "#{_url}?#{obj.query}" if obj.query and not obj.path
+        obj.url = "#{_url}/#{obj.path}" if obj.path and not obj.query
+    else
+      if (obj.url isnt _url) and (obj.url isnt "#{_url}/#{obj.path}") and (obj.url isnt "#{_url}?#{obj.query}") and (obj.url isnt "#{_url}/#{obj.path}?#{obj.query}")
+        obj.url = "#{obj.url}/#{obj.path}" if obj.path?
+        obj.url = "#{obj.url}?#{obj.query}" if obj.query?
+
     promise = @_createAjaxPromise(obj)
     promise.then (err, result) -> cb(err, result)


Suite of tests... almost working

Suite of tests... almost working

--- spec/UiMessageQueue_spec.coffee
+++ spec/UiMessageQueue_spec.coffee
@@ -1,21 +1,26 @@
-#global.window = require("jsdom").jsdom().createWindow();
-
-window = require('jsdom').jsdom('

').createWindow()
-
+
+# Imports
+
+global.window = require("jsdom").jsdom().createWindow()
+jQuery = require('jQuery').jQuery
+global.jQuery = global.$ = jQuery
+
+AlertDisplay = require('../lib/AlertDisplay').AlertDisplay
+DomDisplay = require('../lib/DomDisplay').DomDisplay
 UiMessageQueue = require('../lib/UiMessageQueue').UiMessageQueue
-#$ = require("jasmine-jquery")
+FifoQueue = require('../lib/FifoQueue').FifoQueue
+

 describe "UiMessageQueue", ->

-    fixture = null
-
-    describe 'handling options', ->
-
-        it 'should create instance with valid options', ->
-
-            options =
-                timeoutMiliSecs: 1
+    describe 'handle options', ->
+
+        it 'should create instance with all, valid options', ->
+
+            options =
+                delay: 1
                 message_box_div_id: "myDiv"
+                emptyDisplayString: "hello world"

             messageQueue = new UiMessageQueue(options)
             expect(messageQueue).toBeDefined()
@@ -31,40 +36,181 @@
             ).toThrow 'Missing arguments. UiMessageQueue requires arguments to run.'

-        it 'throws Exception if timeoutMiliSecs is a string', ->
-
-            options =
-                timeoutMiliSecs: "foo"
-
-            expect(->
-
-                new UiMessageQueue(options)
-
-            ).toThrow 'Invalid argument: timeoutMiliSecs is not numeric'
-
-
-    describe 'displays messages', ->
-
-        it 'displays one message immediately', ->
-
-            #el = document.createElement("div");
-            #el.id = "myDiv"
-            #document.body.appendChild(el)
-
-            #loadFixtures('fixture.html');
-            #fixture = $('#tabs')
-
-            #spyOn(global.window.document, 'getElementById').andReturn(el)
-
-            options =
-                timeoutMiliSecs: 1
-                message_box_div_id: "myDiv"
-
-            messageQueue = new UiMessageQueue(options)
-            messageQueue.push "Foo"
-
-            expect(el.innerHtml).toBe("Foo")
-
-
-
-
+        describe 'using the delay option', ->
+
+            it 'throws Exception if delay is a string', ->
+
+                options =
+                    delay: "foo"
+
+                expect(->
+
+                    messageQueue = new UiMessageQueue(options)
+
+                ).toThrow 'Invalid argument: delay is not numeric'
+
+
+            it 'assigns value when valid', ->
+
+                options =
+                    delay: 999
+
+                messageQueue = new UiMessageQueue(options)
+
+                expect(messageQueue._delay).toEqual(999)
+
+
+        describe 'using the emptyDisplayString option', ->
+
+            it 'assigns value when valid', ->
+
+                options =
+                    emptyDisplayString: "foo"
+
+                messageQueue = new UiMessageQueue(options)
+
+                expect(messageQueue._emptyDisplayString).toEqual("foo")
+
+
+            it 'throws Exception if emptyDisplayString is not a string', ->
+
+                # todo: do we really care about this? its not a comprehensive test anyway
+                options =
+                    emptyDisplayString: true
+
+                expect(->
+
+                    messageQueue = new UiMessageQueue(options)
+
+                ).toThrow 'Invalid argument: emptyDisplayString is not a String'
+
+
+        describe 'default options', ->
+
+            it 'uses default value of 1000 for delay', ->
+
+                options =
+                    message_box_div_id: "myDiv"
+
+                messageQueue = new UiMessageQueue(options)
+
+                expect(messageQueue._delay).toEqual(1000)
+
+
+            it 'uses default value of "" for emptyDisplayString', ->
+
+                options =
+                    message_box_div_id: "myDiv"
+
+                messageQueue = new UiMessageQueue(options)
+
+                expect(messageQueue._emptyDisplayString).toEqual("")
+
+
+    describe 'select display method', ->
+
+        it 'given options without dom element id should select alert display', ->
+
+            options =
+                delay: 999
+
+            messageQueue = new UiMessageQueue(options)
+
+            expect(messageQueue._displayer instanceof AlertDisplay).toBeTruthy()
+
+
+        it 'given invalid dom element id should select alert display', ->
+
+            options =
+                delay: 1
+                outputElementId: ""
+
+            messageQueue = new UiMessageQueue(options)
+
+            expect(messageQueue._displayer instanceof AlertDisplay).toBeTruthy()
+
+
+    describe 'messages', ->
+
+        messageQueue = null
+
+        beforeEach ->
+            options =
+                delay: 10000000
+                outputElementId: ""
+
+            mockDisplayer = createSpyObj('mockDisplayer', ['displayMessage']) # just mock the method here, we dont need a response
+
+            messageQueue = new UiMessageQueue(options)
+            messageQueue._displayer = mockDisplayer # todo: we cant mock any instance with Jasmine so have to 'inject' it +repeated below
+
+            jasmine.Clock.useMock()
+
+        afterEach ->
+            messageQueue = null
+
+        describe 'add messages to the queue', ->
+
+            it 'should add message to queue', ->
+
+                mockQueue = createSpyObj('mockQueue', ['push', 'hasItems', 'getItem'])
+                messageQueue._messageStack = mockQueue
+
+                messageQueue.push "message"
+
+                expect(mockQueue.push).toHaveBeenCalledWith("message")
+
+            it 'should only add message to queue if a string', ->
+
+                mockQueue = createSpyObj('mockQueue', ['push', 'hasItems', 'getItem'])
+                messageQueue._messageStack = mockQueue
+
+                messageQueue.push new Object()
+
+                expect(mockQueue.push).not.toHaveBeenCalled()
+
+
+        describe 'display messages from queue', ->
+
+            it 'should pass display to display strategy', ->
+
+                mockDisplayer = createSpyObj('mockDisplayer', ['displayMessage'])
+
+                messageQueue._displayer = mockDisplayer
+
+                messageQueue._displayMessage "foo" # todo: do i really write this test
+
+                expect(mockDisplayer.displayMessage).toHaveBeenCalledWith("foo")
+
+            it 'should display message from queue when pushed', ->
+
+                mockDisplayer = createSpyObj('mockDisplayer', ['displayMessage'])
+
+                messageQueue._displayer = mockDisplayer
+
+                messageQueue.push "foo"
+
+                expect(mockDisplayer.displayMessage).toHaveBeenCalledWith("foo")
+
+            it 'should display message from queue when pushed after interval', ->
+
+                mockDisplayer = createSpyObj('mockDisplayer', ['displayMessage'])
+
+                messageQueue._displayer = mockDisplayer
+
+                messageQueue.push "foo"
+
+                expect(mockDisplayer.displayMessage).not.toHaveBeenCalled();
+
+                jasmine.Clock.tick(100000001);
+
+                expect(mockDisplayer.displayMessage).toHaveBeenCalled()
+
+
+
+
+
+
+
+
+


Refactor and document

Refactor and document

--- src/trix/controllers/input_controller.coffee
+++ src/trix/controllers/input_controller.coffee
@@ -77,20 +77,20 @@
         @reset()

   mutationIsExpected: ({textAdded, textDeleted}) ->
-    if @inputSummary
-      if @inputSummary.preferDocument?
-        @inputSummary.preferDocument
-      else
-        unhandledAddition = textAdded isnt @inputSummary.textAdded
-        unhandledDeletion = textDeleted? and not @inputSummary.didDelete
-
-        if textDeleted is "\n" and unhandledDeletion
-          if textAdded and not unhandledAddition
-            if range = @responder?.getSelectedRange()
-              if @responder?.positionIsBlockBreak(range[1] + textAdded.length)
-                unhandledDeletion = false
-
-        not (unhandledAddition or unhandledDeletion)
+    return true if @inputSummary.preferDocument
+
+    unhandledAddition = textAdded isnt @inputSummary.textAdded
+    unhandledDeletion = textDeleted? and not @inputSummary.didDelete
+
+    # Expect newline removal at the end of a block caused
+    # by the extra <br> rendered to represent them.
+    if textDeleted is "\n" and unhandledDeletion
+      if textAdded and not unhandledAddition
+        if range = @responder?.getSelectedRange()
+          if @responder?.positionIsBlockBreak(range[1] + textAdded.length)
+            unhandledDeletion = false
+
+    not (unhandledAddition or unhandledDeletion)

   unlessMutationOccurs: (callback) ->
     mutationCount = @mutationCount


Validate redirect URL also for the Chrome app

--- components/LoginOAuth.coffee
+++ components/LoginOAuth.coffee
@@ -33,6 +33,9 @@
     if typeof chrome isnt 'undefined' and chrome.identity
       # With Chrome apps we do login via the WebAuthFlow
       redirect = chrome.identity.getRedirectURL()
+      unless isRedirectValid redirect
+        return callback new Error "App URL must match GitHub app configuration $NOFLO_OAUTH_CLIENT_REDIRECT"
+
       chrome.identity.launchWebAuthFlow
         interactive: true
         url: getUrl


removed unnecessary display block setting

--- oridomi.coffee
+++ oridomi.coffee
@@ -414,8 +414,6 @@
     @el.style.backgroundColor = 'transparent'
     @el.style.backgroundImage = @el.style.border = @el.style.outline = 'none'
     @el.style.position = 'relative'
-    # Show the left stage to start with.
-    @stages.left.style.display = 'block'

     # Create an element to hold stages.
     @stageHolder = document.createElement 'div'
@@ -478,7 +476,7 @@
           else
             panel.style[anchor] = '100%'
         else
-          content.style[anchors[0]] = -(count - i - 1) * 100 + '%'
+          content.style[anchors[0]] = (count - i - 1) * -100 + '%'
           panel.style[css.origin] = anchor
           if i is 0
             panel.style[anchors[0]] = 100 - percent + '%'


as needed compilation for jade and stylus

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -33,7 +33,7 @@
         dest: 'build/'
         src: ['demo/scripts/*.coffee']
         ext: '.js'
-      bare:
+      src:
         options:
           bare: true
         expand: true
@@ -86,18 +86,12 @@
         src: ['*.js']

     jade:
-      options:
-        pretty: true
-      demo:
+      all:
         dest: 'build/'
         expand: true
         ext: ['.html']
-        src: ['demo/*.jade', '!demo/content.jade']
-      tests:
-        dest: 'build/'
-        expand: true
-        ext: ['.html']
-        src: ['tests/**/*.jade']
+        pretty: true
+        src: ['demo/*.jade', 'tests/**/*.jade', '!demo/content.jade']

     'string-replace':
       tests:
@@ -117,43 +111,41 @@
         src: ['build/src/**/*.js']

     stylus:
-      demo:
+      all:
         expand: true
         dest: 'build/'
-        src: ['demo/styles/*.styl']
-        ext: ['.css']
-      tests:
-        expand: true
-        dest: 'build/'
-        src: ['tests/mocha/*.styl']
+        src: ['demo/styles/*.styl', 'tests/mocha/*.styl']
         ext: ['.css']

     watch:
       options:
         nospawn: true
-      demo:
+      coffee_demo:
         files: ['demo/scripts/*.coffee']
         tasks: ['coffee:demo']
-      jade_demo:
-        files: ['demo/*.jade']
-        tasks: ['jade:demo']
-      jade_test:
-        files: ['tests/**/*.jade']
-        tasks: ['jade:tests']
-      stylus:
-        files: ['demo/styles/*.styl']
-        tasks: ['stylus:demo']
-      src:
+      coffee_src:
         files: ['index.coffee', 'src/**/*.coffee']
-        tasks: ['coffee:bare', 'string-replace', 'browserify', 'concat']
-      test:
+        tasks: ['coffee:src', 'string-replace', 'browserify', 'concat']
+      coffee_test:
         files: ['tests/mocha/**/*.coffee']
         tasks: ['coffee:test']
+      jade:
+        files: ['demo/*.jade', 'tests/**/*.jade', '!demo/content.jade']
+        tasks: ['jade']
+      stylus:
+        files: ['demo/styles/*.styl', 'tests/mocha/*.styl']
+        tasks: ['stylus']

   grunt.event.on('watch', (action, filepath) ->
-    if grunt.file.isMatch(grunt.config('watch.src.files'), filepath)
-      grunt.config('coffee.bare.src', filepath)
+    if grunt.file.isMatch(grunt.config('watch.coffee_demo.files'), filepath)
+      grunt.config('coffee.demo.src', filepath)
+    else if grunt.file.isMatch(grunt.config('watch.coffee_src.files'), filepath)
+      grunt.config('coffee.src.src', filepath)
+    else if grunt.file.isMatch(grunt.config('watch.jade.files'), filepath)
+      grunt.config('jade.all.src', filepath)
+    else if grunt.file.isMatch(grunt.config('watch.stylus.files'), filepath)
+      grunt.config('stylus.all.src', filepath)
   )

   # Default task.


Updated Gruntfile to deploy assets folder as part of the copy:dist task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -80,6 +80,7 @@
                         'bower_components/**'
                         'js/**'
                         'css/*.css'
+                        'assets/**'
                     ]
                     dest: 'dist/'
                 },{


Set resource path as single argument

Set resource path as single argument

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -229,7 +229,7 @@
     commands.push (callback) ->
       exec('pkill', ['Atom'], ignoreFailures: true, callback)
     commands.push (result, callback) ->
-      exec(path.join(CONTENTS_DIR, 'MacOS', 'Atom'), ['--test', '--resource-path', __dirname], callback)
+      exec(path.join(CONTENTS_DIR, 'MacOS', 'Atom'), ['--test', "--resource-path=#{__dirname}"], callback)
     grunt.util.async.waterfall commands, (error) -> done(!error?)

   grunt.registerTask('compile', ['coffee', 'less', 'cson'])


Gruntfile: add string-replace handling

Gruntfile: add string-replace handling

sets version number so far

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -11,10 +11,20 @@
             'src/*/*.coffee',
             'src/main.coffee'
           ]
+    'string-replace':
+      version:
+        files:
+          'generated_bundle.js': 'generated_bundle.js'
+        options:
+          replacements: [
+            pattern: "insert_version_here"
+            replacement: Date.now()
+          ]
+

   # Load plugins
   grunt.loadNpmTasks 'grunt-contrib-coffee'
   grunt.loadNpmTasks 'grunt-string-replace'

   # Tasks runners
-  grunt.registerTask 'default', ['coffee']
+  grunt.registerTask 'default', ['coffee', 'string-replace']


S87682: adds build steps for npm publish and bump

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -21,6 +21,8 @@
   grunt.loadNpmTasks 'grunt-parallel-spec-runner'
   grunt.loadNpmTasks 'grunt-text-replace'
   grunt.loadNpmTasks 'grunt-curl'
+  grunt.loadNpmTasks 'grunt-bump'
+  grunt.loadNpmTasks 'grunt-npm'

   grunt.loadTasks 'grunt/tasks'

@@ -32,6 +34,8 @@
   grunt.registerTask 'nexus:__createartifact__', 'Internal task to create and publish the nexus artifact', ['version', 'nexus:push:publish', 'clean:target']
   grunt.registerTask 'nexus:deploy', 'Deploys to nexus', ['build', 'nexus:__createartifact__']
   grunt.registerTask 'nexus:verify', 'Fetches the last build and verifies its integrity', ['clean:nexus', 'version', 'nexus:push:verify']
+
+  grunt.registerTask 'npm:publish', 'Publish to our private npm registry', ['bump-commit', 'npm-publish']

   grunt.registerTask 'check', 'Run convention tests on all files', ['regex-check']
   grunt.registerTask 'ci', 'Does a full build, runs tests and deploys to nexus', ['build', 'test:ci', 'nexus:__createartifact__']
@@ -89,6 +93,18 @@
     pkg: grunt.file.readJSON 'package.json'

     buildVersion: version
+
+    bump:
+      options:
+        commitFiles: ['package.json']
+        commitMessage: '[ci npm:publish autobump v%VERSION%]',
+        push: true
+        pushTo: 'origin master'
+
+    'npm-publish':
+      options:
+        # the CI will be producing version files and what-not. they can be safely ignored.
+        abortIfDirty: false

     clean:
       build: ['build/', 'src/apps/**/*.html', 'temp/']


ajusta grunt file para executar os testes

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,60 +1,81 @@
 GruntVTEX = require 'grunt-vtex'

 module.exports = (grunt) ->
-	pkg = grunt.file.readJSON 'package.json'
+  pkg = grunt.file.readJSON 'package.json'

-	config = GruntVTEX.generateConfig grunt, pkg,
-		replaceGlob: "build/**/vtex-message.js"
+  config = GruntVTEX.generateConfig grunt, pkg,
+    replaceGlob: "build/**/vtex-message.js"

-	# Add app files to coffe compilation and watch
-	config.watch.coffee.files.push 'src/coffee/**/*.coffee'
-	config.watch.main.files.push 'src/**/*.html'
-	config.coffee.main.files[0].cwd = 'src/'
-	config.coffee.main.files[0].dest = 'build/<%= relativePath %>/'
+  # Add app files to coffe compilation and watch
+  config.watch.coffee.files.push 'src/coffee/**/*.coffee'
+  config.watch.main.files.push 'src/**/*.html'
+  config.coffee.main.files[0].cwd = 'src/'
+  config.coffee.main.files[0].dest = 'build/<%= relativePath %>/'

-	config.uglify.options.banner = "/* #{pkg.name} - v#{pkg.version} */\n"
-	config.uglify.target =
-		files:
-			'dist/vtex-message.min.js': ['build/front-messages-ui/script/vtex-message.js']
+  config.uglify.options.banner = "/* #{pkg.name} - v#{pkg.version} */\n"
+  config.uglify.target =
+    files:
+      'dist/vtex-message.min.js': ['build/front-messages-ui/script/vtex-message.js']

-	config.less.main.files[0].src = ['style.less', 'print.less', 'vtex-message.less']
+  config.less.main.files[0].src = ['style.less', 'print.less', 'vtex-message.less']

-	config.cssmin =
-		target:
-			files: [
-				expand: true,
-				cwd: "build/front-messages-ui/style/"
-				src: ['vtex-message.css'],
-				dest: 'dist/',
-				ext: '.min.css'
-			]
-			options:
-				banner: "/* #{pkg.name} - v#{pkg.version} */\n"
+  config.coffee.test =
+    expand: true
+    cwd: 'spec/'
+    src: ['**/*.coffee']
+    dest: 'build/<%= relativePath %>/spec/'
+    ext: '.js'

-	config.copy.test =
-		expand: true
-		cwd: 'spec/'
-		src: ['**', '!**/*.coffee']
-		dest: 'build/<%= relativePath %>/spec/'
+  config.cssmin =
+    target:
+      files: [
+        expand: true,
+        cwd: "build/front-messages-ui/style/"
+        src: ['vtex-message.css'],
+        dest: 'dist/',
+        ext: '.min.css'
+      ]
+      options:
+        banner: "/* #{pkg.name} - v#{pkg.version} */\n"

-	tasks =
-	# Building block tasks
-		build: ['clean', 'copy:main', 'copy:pkg', 'coffee', 'less']
-		min: ['uglify'] # minifies files
-	# Deploy tasks
-		dist: ['build', 'min', 'cssmin'] # Dist - minifies files
-		test: ['default', 'karma:unit', 'watch:test']
-		vtex_deploy: ['shell:cp', 'shell:cp_br']
-	# Development tasks
-		dev: ['nolr', 'build', 'watch']
-		default: ['build', 'connect', 'watch']
-		devmin: ['build', 'min', 'connect:http:keepalive'] # Minifies files and serve
+  config.copy.test =
+    expand: true
+    cwd: 'spec/'
+    src: ['**', '!**/*.coffee']
+    dest: 'build/<%= relativePath %>/spec/'

-	# Project configuration.
-	grunt.initConfig config
-	grunt.loadNpmTasks name for name of pkg.devDependencies when name[0..5] is 'grunt-' and name isnt 'grunt-vtex'
-	grunt.registerTask 'nolr', ->
-		# Turn off LiveReload in development mode
-		grunt.config 'watch.options.livereload', false
-		return true
-	grunt.registerTask taskName, taskArray for taskName, taskArray of tasks+  config.karma =
+    options:
+      configFile: 'karma.conf.js'
+    unit:
+      configFile: 'karma.conf.js',
+      background: true
+    deploy:
+      singleRun: true
+
+  config.watch.test =
+    files: ['src/**/*.html', 'src/**/*.coffee', 'src/**/*.js', 'src/**/*.less', 'spec/**/*.*']
+    tasks: ['dev', 'karma:unit:run']
+
+  tasks =
+  # Building block tasks
+    build: ['clean', 'copy:main', 'copy:pkg', 'coffee', 'less']
+    dev: ['clean', 'copy:main', 'copy:pkg', 'copy:test', 'coffee', 'less']
+    min: ['uglify'] # minifies files
+  # Deploy tasks
+    dist: ['build', 'min', 'cssmin'] # Dist - minifies files
+    test: ['dev', 'connect', 'karma:unit', 'watch:test']
+    vtex_deploy: ['shell:cp', 'shell:cp_br']
+  # Development tasks
+    default: ['build', 'connect', 'watch']
+    devmin: ['build', 'min', 'connect:http:keepalive'] # Minifies files and serve
+    tdd: ['dev', 'karma:unit', 'watch:test']
+
+  # Project configuration.
+  grunt.initConfig config
+  grunt.loadNpmTasks name for name of pkg.devDependencies when name[0..5] is 'grunt-' and name isnt 'grunt-vtex'
+  grunt.registerTask 'nolr', ->
+    # Turn off LiveReload in development mode
+    grunt.config 'watch.options.livereload', false
+    return true
+  grunt.registerTask taskName, taskArray for taskName, taskArray of tasks


Add grunt task: default Test

Add grunt task: default Test

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -108,6 +108,7 @@

   # Default task(s)
   grunt.registerTask 'default', ['connect','watch']
+  grunt.registerTask 'default-test', ['connect', 'uglify:test','watch']
   grunt.registerTask 'build', ['concat','string-replace','uglify:min','test']
   grunt.registerTask 'test', ['jshint','jscs','uglify:test','qunit']
   grunt.registerTask 'test-travis', ['build']


Adjust CPP linter

Adjust CPP linter

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -26,6 +26,7 @@
         'src/**/*.cc'
         'src/**/*.h'
       ]
+      linelength: 100
       reporter: 'spec'
       verbosity: 1
       filters:


Revert "Run tests in different port so we don't clash with 'npm start'"

This reverts commit 2e82edf3ba2bdaeda14cd7826fec406a5c989223.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -369,7 +369,7 @@
     connect:
       server:
         options:
-          port: 9998
+          port: 9999
           hostname: '*' # Allow connection from mobile
           livereload: false

@@ -392,18 +392,18 @@
       unit:
         options:
           reporter: 'spec'
-          urls: ['http://localhost:9998/spec/tests.html']
+          urls: ['http://localhost:9999/spec/tests.html']
           failWithOutput: true
       browser:
         options:
           reporter: 'spec'
-          urls: ['http://localhost:9998/spec/browser/tests.html']
+          urls: ['http://localhost:9999/spec/browser/tests.html']

     # BDD tests on browser
     'saucelabs-mocha':
       all:
         options:
-          urls: ['http://127.0.0.1:9998/spec/browser/tests.html']
+          urls: ['http://127.0.0.1:9999/spec/browser/tests.html']
           browsers: [
             browserName: 'googlechrome'
             version: '55'


Try to fix mochacov.

Try to fix mochacov.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -22,7 +22,7 @@
     mochacov:
       options:
         compilers: ['coffee:coffee-script/register']
-        files: ['test/*.coffee']
+        files: ['test/expander-test.coffee', 'test/glob-html-test.coffee', 'test/processor-test.coffee']
       coverage:
         options:
           coveralls: true


Fixed the bump task to push to origin

Fixed the bump task to push to origin

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -15,6 +15,11 @@
 				dest: "lib"
 				ext: ".js"

+		# Bump files
+		bump:
+			options:
+				pushTo: "origin"
+

 		# Watch for changes
 		watch:


Add build & "basic" test task

Add build & "basic" test task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -80,3 +80,13 @@
         "clean"
         "codo"
     ]
+
+    grunt.registerTask "build", [
+        "coffeelint"
+        "coffee"
+    ]
+
+    grunt.registerTask "test", [
+        "clean"
+        "codo"
+    ]


Only copy the app file

Only copy the app file

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -125,7 +125,7 @@
   grunt.registerTask 'build', 'Build the application', ->
     rm BUILD_DIR
     mkdir path.dirname(BUILD_DIR)
-    cp 'atom-shell/Atom.app', BUILD_DIR
+    cp 'atom-shell/Atom.app', SHELL_APP_DIR

     mkdir APP_DIR


test: fix `time-grunt` load dep

test: fix `time-grunt` load dep

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -4,7 +4,9 @@
   # Load all grunt tasks
   matchdep = require("matchdep")
   matchdep.filterDev("grunt-*").forEach grunt.loadNpmTasks
-  matchdep = require("time-grunt") grunt
+
+  # Track tasks load time
+  require("time-grunt") grunt

   # Configurable paths
   coreConfig =


buildcontrol

buildcontrol

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -289,9 +289,11 @@
           ".tmp"
         ]

+
       jekyllMetadata:
         src: [
-          "<%= config.dist %>"
+          "<%= config.dist %>/*"
+          "!<%= config.dist %>/.git"
           "<%= config.app %>/.jekyll-metadata"
         ]

@@ -368,11 +370,11 @@
         dir: '_site'
         commit: true
         push: true
-        message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
+        message: 'Built from commit %sourceCommit% on branch %sourceBranch%'
       pages:
         options:
-          remote: 'https://github.com/jarylwang/jarylwang.github.io.git'
-          branch: 'master'
+          remote: 'https://github.com/jarylwang/www.jarylwang.com.git'
+          branch: 'gh-pages'

   grunt.registerTask "reset", "Reset user availability", (target) ->


update grunt task (Rakefile -> Gruntfile.coffee) (jsdoc isn't ported)

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -126,7 +126,7 @@

   # Default task.
   grunt.registerTask 'default', [
-    'jshint:core', 'concat', 'uglify', 'qunit', 'exec:lang']
+    'jshint:core', 'concat', 'uglify', 'qunit', 'lang']

   grunt.registerTask 'lang', 'Make lang file.', ()->
     grunt.log.writeln 'processing..'


Allow local running of Saucelabs tests and exclude IE8

IE8 is timing out and causing travis to give up the build.  Need to test this on a local IE8 browser to figure out what is causing the error.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,4 +1,8 @@
 module.exports = (grunt) ->
+
+  travisJobId = 0 unless process.env.TRAVIS_JOB_ID
+  travisBuildNo = '-1' unless process.env.TRAVIS_BUILD_NUMBER
+  travisBranch = 'local' unless process.env.TRAVIS_BRANCH

   srcFiles = [
     'tmp/js/MeducationTemplates.js'
@@ -140,15 +144,15 @@
           urls: ['http://127.0.0.1:8000/_SpecRunner.html']
           concurrency: 3
           testname: 'Meducation front-end tests'
-          build: process.env.TRAVIS_JOB_ID
-          tags: [process.env.TRAVIS_BUILD_NUMBER, process.env.TRAVIS_BRANCH]
+          build: travisJobId
+          tags: [travisBuildNo, travisBranch]
           testTimeout: 60000
+          detailedError: true
           browsers: [{
             browserName: 'internet explorer',
             version: '11',
             platform: 'Windows 8.1'
-          },
-          {
+          }, {
             browserName: 'internet explorer',
             version: '10',
             platform: 'Windows 8'
@@ -156,11 +160,10 @@
             browserName: 'internet explorer',
             version: '9',
             platform: 'Windows 7'
-          }, {
-          }, {
-            browserName: 'internet explorer',
-            version: '8',
-            platform: 'Windows 7'
+#          }, {
+#            browserName: 'internet explorer',
+#            version: '8',
+#            platform: 'Windows 7'
           }, {
             browserName: 'chrome',
             platform: 'Linux',


fixed bug where baseurl wasnt properly configured

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -188,7 +188,7 @@
     'exec:bundler'
     'bower'
     'copy'
-    'exec:lo_baseurl'
+    # 'exec:lo_baseurl'
     'exec:jekyll'
     'sass'
     'coffee'
@@ -197,6 +197,7 @@
     'copy:image'
   ]
   grunt.registerTask 'serve', [
+    'exec:lo_baseurl'
     'build'
     'connect:server'
     'exec:status'


Explicitly register CoffeeScript’s compiler using coffee-script/register

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -12,7 +12,7 @@
 		mochacli:
 			options:
 				bail: true
-				compilers: ['coffee:coffee-script']
+				compilers: ['coffee:coffee-script/register']
 				# Compiling takes time…
 				timeout: 8000
 			files: ['test/']


Disabling max line length test

Disabling max line length test

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -15,18 +15,14 @@
             config:
                 files:
                     src: ['config/*.coffee']
-                options:
-                    'max_line_length':
-                        'level': 'ignore'
             controllers:
                 files:
                     src: ['controllers/*.coffee']
-                options:
-                    'max_line_length':
-                        'level': 'ignore'
             options:
                 'indentation':
                     value: 4
+                'max_line_length':
+                    'level': 'ignore'
         compass:
             dist:
                 options:


Remove blank file dependancy for else.js

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -114,9 +114,6 @@
         options:
             bare: false
             sourceMap: true
-        files:
-            # all of the files used in testing and development - configuration, etc.
-            ".tmp/scripts/else.js": [".tmp/scripts/*.coffee", "!.tmp/scripts/alchemy.src.coffee"]

       test:
         files: [