configure auth endpoint

configure auth endpoint

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -15,7 +15,9 @@
   .describe('clientSecret', 'your OAuth client secret for the SPHERE.IO API')
   .describe('accessToken', 'an OAuth access token for the SPHERE.IO API')
   .describe('sphereHost', 'SPHERE.IO API host to connecto to')
-  .describe('sphereProtocol', 'SPHERE.IO API protocol to connecto to')
+  .describe('sphereProtocol', 'SPHERE.IO API protocol to connect to')
+  .describe('sphereAuthHost', 'SPHERE.IO OAuth host to connect to')
+  .describe('sphereAuthProtocol', 'SPHERE.IO OAuth protocol to connect to')
   .describe('file', 'XML or CSV file containing inventory information to import')
   .describe('csvHeaders', 'a list of column names to use as mapping, comma separated')
   .describe('csvDelimiter', 'the delimiter type used in the csv')
@@ -105,6 +107,10 @@

   options.host = argv.sphereHost if argv.sphereHost
   options.protocol = argv.sphereProtocol if argv.sphereProtocol
+  if argv.sphereAuthHost
+    options.oauth_host = argv.sphereAuthHost
+    options.rejectUnauthorized = false
+  options.oauth_protocol = argv.sphereAuthProtocol if argv.sphereAuthProtocol

   stockimport = new StockImport logger, options


Show random colour for CPU stats

Show random colour for CPU stats

--- app/assets/javascripts/dashboard/stats.js.coffee
+++ app/assets/javascripts/dashboard/stats.js.coffee
@@ -77,14 +77,24 @@
     colours = {}
     $('.nv-legend-text').each () ->
       colours[$(this).text()] = $(this).prev('circle').css('fill')
-
+
+    randomColor = (i) ->
+      colors = d3.scale.category20().range()
+      colors[i % colors.length-1]
+
+    colourSelector = (server) ->
+      if @servers.length > Object.keys(colours).length
+        return randomColor(server.id)
+      else
+        return colours[server.name]
+
     # Create multiple charts, one for each server, to be included using the compose() method
     lines = []
     _.each @servers, (server) ->
       lines.push({
         name: server.name,
         yData: (datapoint) -> datapoint[server.name],
-        colour: colours[server.name]
+        colour: colourSelector(server)
       })

     # Sum all the servers usages for a given date


OX-409 optimized tree color generation

OX-409 optimized tree color generation

--- app/assets/javascripts/oxalis/model/route.coffee
+++ app/assets/javascripts/oxalis/model/route.coffee
@@ -11,6 +11,7 @@
 # This takes care of the route.

 # Constants
+GOLDEN_RATIO      = 0.618033988749895
 BUFFER_SIZE       = 262144 # 1024 * 1204 / 4
 INIT_TIMEOUT      = 10000 # 10s
 TYPE_USUAL        = 0
@@ -387,14 +388,16 @@

   getNewTreeColor : ->

-    switch @treeIdCount
-      when 1 then return 0xFF0000
-      when 2 then return 0x00FF00
-      when 3 then return 0x0000FF
-      when 4 then return 0xFF00FF
-      when 5 then return 0xFFFF00
-      else
-        new THREE.Color().setHSV(Math.random(), 1, 1).getHex()
+    # this generates the most distinct colors possible, using the golden ratio
+    if @trees.length == 0
+      @currentHue = null
+      return 0xFF0000
+    else
+      unless @currentHue
+        @currentHue = new THREE.Color().setHex(_.last(@trees).color).getHSV().h
+      @currentHue += GOLDEN_RATIO
+      @currentHue %= 1
+      new THREE.Color().setHSV(@currentHue, 1, 1).getHex()

   createNewTree : ->


add link to YUL

add link to YUL

--- app/assets/javascripts/partials/yale-footer.cjsx
+++ app/assets/javascripts/partials/yale-footer.cjsx
@@ -8,7 +8,9 @@
     { if @props.display == 1

-
+
+
+


Finally fixed the popover

Finally fixed the popover

--- app/assets/javascripts/pt/tags/popover.js.coffee
+++ app/assets/javascripts/pt/tags/popover.js.coffee
@@ -12,10 +12,8 @@
         $el = $ $el[0]
         attr = $el.ptAttr()

-        log $el.data()
-
-        if not $el.data('ptPopoverLoaded') and $el.data('ptPopoverFocused')
-          $el.data 'ptPopoverLoaded', true
+        if not $el.data('ptPopoverLoading') and $el.data('ptPopoverFocused')
+          $el.data 'ptPopoverLoading', true
           url = attr.popover

           #if not a url assume it's content
@@ -26,7 +24,7 @@
               trigger: 'manual'
               container: attr.popoverContainer or 'body'
               placement: attr.popoverPlacement or 'right'
-            $el.removeData 'ptPopoverLoaded'
+            $el.data 'ptPopoverLoaded', true
             $el.popover 'show' if $el.data 'ptPopoverFocused'
           else
             $.ajax
@@ -38,13 +36,10 @@
                   trigger: 'manual'
                   container: attr.popoverContainer or 'body'
                   placement: attr.popoverPlacement or 'right'
-                $el.removeData 'ptPopoverLoaded'
+                $el.data 'ptPopoverLoaded', true
                 $el.popover 'show' if $el.data 'ptPopoverFocused'
         else if $el.data('ptPopoverFocused')
-          if not attr.popoverContainer
-            $el.popover 'show'
-          else
-            $el.closest(attr.popoverContainer).find('.popover').popover 'show'
+          $el.popover 'show'
       , 500)

@@ -54,10 +49,10 @@
     callback: ->
       $el = $ this
       attr = $el.ptAttr()
-      $el.removeData 'ptPopoverLoaded'
       $el.removeData 'ptPopoverFocused'
-      if not attr.popoverContainer
-        $('body').find('.popover').remove()
-        # $el.popover 'hide'
-      else
-        $el.closest(attr.popoverContainer).find('.popover').remove()
+      if $el.data 'ptPopoverLoaded'
+        $el.popover 'hide'
+      # if not attr.popoverContainer
+      #   $('body').find('.popover').popover 'hide'
+      # else
+      #   $el.closest(attr.popoverContainer).popover 'hide'


adjust fetch timer in dashboard to pass cms171,172

--- app/assets/javascripts/views/dashboard.js.coffee
+++ app/assets/javascripts/views/dashboard.js.coffee
@@ -37,7 +37,8 @@
           else
             @timeout ?= setInterval =>
               @model.fetch()
-            , 3000
+            , 10000 # we had to set this from 3000 to 10000 to pass certification on CMS171,172 which
+                    # have a lot of submeasures.
       rescale: ->
         if @model.isPopulated()
           if PopHealth.currentUser.populationChartScaledToIPP() then @popChart.maximumValue(@model.result().IPP) else @popChart.maximumValue(PopHealth.patientCount)


Show sharing always

Show sharing always

--- app/backbone/views/comments/react_comment.coffee
+++ app/backbone/views/comments/react_comment.coffee
@@ -55,11 +55,10 @@
         _a ["spec-sub-comments-link", href:"javascript:", onClick: @_toggleSubcomments],
           "(#{sub_comment_count}) Reply"

-        unless window.is_kennisland
-          _span [],
-            @_separator()
-            ReactSlidingShareButton
-              model: @model()
+        _span [],
+          @_separator()
+          ReactSlidingShareButton
+            model: @model()
       if @_show_subcomments()
         ReactSubComments
           model: @model().sub_comments()


disable new course form while submitting, fixes #10246

prevent users from double-submitting the form while they wait

test plan:
1. go to the dashboard
2. click the "Start a New Course" button
3. try to double or triple-submit the form
4. confirm that the form gets disabled and only one course gets created

Change-Id: If58995a5404b9d7d572d7068ad98142ffabbc044
Reviewed-on: https://gerrit.instructure.com/13360
Reviewed-by: Brian Palmer <3cb4e1df5ec4da2c7c4af7c52cec8cf340a55a10@instructure.com>
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>

--- app/coffeescripts/bundles/new_course_form.coffee
+++ app/coffeescripts/bundles/new_course_form.coffee
@@ -1,5 +1,6 @@
 require [
   'jquery'
+  'jquery.disableWhileLoading'
 ], ($) ->
   $ ->

@@ -10,4 +11,5 @@
         $('#course_code_wrapper').slideDown('fast')

     $nameInput = $('#new_course_form [name="course[name]"]')
-    $nameInput.bind changeEvents, showCourseCodeIfNeeded+    $nameInput.bind changeEvents, showCourseCodeIfNeeded
+    $('#new_course_form').submit -> $(this).disableWhileLoading($.Deferred())


Implement resizer that sets outer size of frame to inner control size

--- app/js/jail_iframe/classes/control_iframe.coffee
+++ app/js/jail_iframe/classes/control_iframe.coffee
@@ -19,6 +19,10 @@
     while bodyEl.firstChild
       bodyEl.removeChild(bodyEl.firstChild)
     bodyEl.appendChild(contentNode)
+  resizeFrame: ->
+    @el.style.width = @doc.body.clientWidth  + 'px'
+    @el.style.height = @doc.body.clientHeight + 'px'
+
   destroy: ->
     return unless @el
     @doc = null


Disabling required fields to make it easier to migrate

--- app/schemas/models/course_instance.schema.coffee
+++ app/schemas/models/course_instance.schema.coffee
@@ -2,9 +2,9 @@

 CourseInstanceSchema = c.object {
   title: 'Course Instance'
-  required: [
-    'courseID', 'classroomID', 'members', 'ownerID', 'aceConfig'
-  ]
+#  required: [
+#    'courseID', 'classroomID', 'members', 'ownerID', 'aceConfig'
+#  ]
 }

 _.extend CourseInstanceSchema.properties,


no status flag in admin auto complete

no status flag in admin auto complete

--- app/assets/javascripts/admin/admin_in_place_editor.js.coffee
+++ app/assets/javascripts/admin/admin_in_place_editor.js.coffee
@@ -62,7 +62,7 @@
         }, (data) =>
           labels = []
           $.each(data, (i, item) =>
-            labels.push(item.full_name + ' (' + item.name_status + ')')
+            labels.push(item.full_name)
           )
           return process(labels)
         )


Improved management for ticket changes.

Improved management for ticket changes.

--- app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
+++ app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
@@ -39,6 +39,7 @@
       url: @url()
       id:  @ticket_id
     if @ticket
+      @ticket = App.Ticket.retrieve( @ticket.id )
       meta.head  = @ticket.title
       meta.title = '#' + @ticket.number + ' - ' + @ticket.title
     meta
@@ -135,12 +136,9 @@
         nav:        @nav
         isCustomer: @isRole('Customer')
       )
-
-    # show frontend times
-    @frontendTimeUpdate()
-
-    @TicketTitle()
-    @Widgets()
+      @TicketTitle()
+      @Widgets()
+
     @TicketAction()
     @ArticleView()

@@ -220,6 +218,8 @@

   constructor: ->
     super
+
+    @ticket      = App.Ticket.retrieve( @ticket.id )
     @subscribeId = @ticket.subscribe(@render)
     @render(@ticket)

@@ -243,7 +243,6 @@

     # update title
     @ticket.title = title
-    @ticket.load( title: title )
     @ticket.save()

     # update taskbar with new meta data
@@ -257,7 +256,6 @@
     super

     @subscribeId = @ticket.subscribe(@render)
-
     @render(@ticket)

   render: (ticket) =>
@@ -282,7 +280,6 @@
 class Widgets extends App.Controller
   constructor: ->
     super
-
     @subscribeId = @ticket.subscribe(@render)
     @render(@ticket)

@@ -311,6 +308,9 @@
         object_type:  'Ticket'
         object:       ticket
       )
+
+    # show frontend times
+    @frontendTimeUpdate()

   release: =>
     App.Ticket.unsubscribe( @subscribeId )


Since a9e0102b1cc38da172f1607667095e48c6d4a9c5 it is not necessary to use Bootstrap javascript to remove job status alerts. When the job status object is removed from the scope, the alert goes away.

--- app/assets/javascripts/ng-services/ng-JobStatusSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-JobStatusSvc.js.coffee
@@ -69,9 +69,6 @@
     # from the database (it will not appear again).
     #---------------------------------------------
     hide_refresh_job_alert: (job_status)->
-      # Close alert
-      $("#refresh-status-alerts #refresh-status-#{job_status.id} .alert").alert 'close'
-
       # Remove job status from scope
       job_status = findSvc.find_refresh_feed_job job_status.id
       if job_status?


Fix socket preservation past parent movement

--- src/controller.coffee
+++ src/controller.coffee
@@ -1416,6 +1416,11 @@

       # Remove the block from the tree.
       rememberedSocketOffsets = @spliceRememberedSocketOffsets(@draggingBlock)
+
+      # TODO this is a hacky way of preserving locations
+      # across parenthesis insertion
+      hadTextToken = @draggingBlock.start.next.type is 'text'
+
       @spliceOut @draggingBlock

       @clearHighlightCanvas()
@@ -1437,12 +1442,21 @@
           if @lastHighlight.type is 'document'
             @spliceIn @draggingBlock, @lastHighlight.start

+      # TODO as above
+      hasTextToken = @draggingBlock.start.next.type is 'text'
+      if hadTextToken and not hasTextToken
+        rememberedSocketOffsets.forEach (x) ->
+          x.offset -= 1
+      else if hasTextToken and not hadTextToken
+        rememberedSocketOffsets.forEach (x) ->
+          x.offset += 1
+
       futureCursorLocation = @toCrossDocumentLocation @draggingBlock.start

       # Reparse the parent if we are
       # in a socket
       #
-      # TODO "reparseable" property, bubble up
+      # TODO "reparseable" property (or absent contexts), bubble up
       # TODO performance on large programs
       if @lastHighlight.type is 'socket'
         @reparse @draggingBlock.parent.parent


Fix units (px etc), for compatability

Fix units (px etc), for compatability

--- src/controller.coffee
+++ src/controller.coffee
@@ -246,15 +246,15 @@
       mainScroller.appendChild mainScrollerStuffing

       computeCanvasDimensions = =>
-        @main.height = @el.offsetHeight
-        @main.width = @el.offsetWidth * 2 - PALETTE_WIDTH
-
-        @palette.style.top = paletteScroller.style.top = @paletteHeader.style.height = @paletteHeaderHeight
-        @palette.height = @el.offsetHeight - @paletteHeaderHeight
-        @palette.width = PALETTE_WIDTH
-
-        drag.height = @el.offsetHeight
-        drag.width = @el.offsetWidth * 2 - PALETTE_WIDTH
+        @main.height = @el.offsetHeight; @main.style.height = "#{@el.offsetHeight}px"
+        @main.width = @el.offsetWidth - PALETTE_WIDTH; @main.style.width = "#{@el.offsetWidth - PALETTE_WIDTH}px"
+
+        @palette.style.top = paletteScroller.style.top = @paletteHeader.style.height = "#{@paletteHeaderHeight}px"
+        @palette.height = @el.offsetHeight - @paletteHeaderHeight; @palette.style.height = "#{@el.offsetHeight - @paletteHeaderHeight}px"
+        @palette.width = PALETTE_WIDTH; @palette.style.width = "#{PALETTE_WIDTH}px"
+
+        drag.height = @el.offsetHeight * 2; drag.style.height = "#{@el.offsetHeight * 2}px"
+        drag.width = @el.offsetWidth * 2 - PALETTE_WIDTH; drag.style.width = "#{@el.offsetWidth * 2 - PALETTE_WIDTH}px"

       # We need to resize the canvases any time the editor resizes.
       # We will bind this to the document resize function, but this function
@@ -2080,7 +2080,9 @@
         tick()
       ), 1

-      return true
+      return {
+        success: true
+      }

     _performFreezeAnimation: ->
       if @currentlyAnimating or @currentlyUsingBlocks then return


Setting .authorize in http.IncomingMessage doesn't seem to work, work around it for now

--- packages/shrub-passport/index.coffee
+++ packages/shrub-passport/index.coffee
@@ -32,28 +32,6 @@
     middleware: [

       (next) ->
-
-        {IncomingMessage} = require 'http'
-
-        # ## IncomingMessage::authorize
-        #
-        # * (string) `method` - The authorization method.
-        #
-        # * (response) `res` - The HTTP/socket response object.
-        #
-        # *Authorize a user instance.*
-        IncomingMessage::authorize = (method, res) ->
-          self = this
-
-          new Promise (resolve, reject) ->
-
-            self._passport.instance.authenticate(
-              method
-              (error, user, info) ->
-                return reject error if error?
-                resolve user, info
-
-            ) self, res

         # #### Invoke hook `shrubUserBeforeLoginMiddleware`.
         beforeLoginMiddleware = middleware.fromConfig(
@@ -169,6 +147,35 @@
   passport.initialize()
   passport.session()

+  (req, res, next) ->
+
+    # ## req.authorize
+    #
+    # * (string) `method` - The authorization method.
+    #
+    # * (response) `res` - The HTTP/socket response object.
+    #
+    # *Authorize a user instance.*
+    req.authorize = (method, options, res) ->
+      self = this
+
+      unless res?
+        res = options
+        options = {}
+
+      new Promise (resolve, reject) ->
+
+        self._passport.instance.authenticate(
+          method
+          options
+          (error, user, info) ->
+            return reject error if error?
+            resolve user, info
+
+        ) self, res
+
+    next()
+
   # Invoke after login middleware if a user already exists in the session.
   (req, res, next) ->
     return next() unless req.user?


forgot to put put process.env before the environment variables

--- web/app.coffee
+++ web/app.coffee
@@ -104,7 +104,8 @@
         if req.method is 'GET' or req.method is 'HEAD'
           next()
         else
-          heck.handler req, res, "Route '#{req.url}' not found, the '#{req.method}' method not allowed further.",
+          heck.handler req, res, "Route '#{req.url}' not found, \
+the '#{req.method}' method not allowed further.",
             405

   # Cloud9's vfs for static files
@@ -119,7 +120,8 @@
   util.puts "Uncaught Exception: #{err.toString()}"

 # Start the server.
-port = parseInt(process.env.PORT || 8001)
-host = process.env.IP || '127.0.0.1' # heroku config:add IP=0.0.0.0
+# heroku config:add IP=0.0.0.0 # for hosting with Heroku
+port = parseInt(process.env.OPENSHIFT_NODEJS_PORT || process.env.PORT || 8001)
+host = process.env.OPENSHIFT_NODEJS_IP || process.env.IP || '127.0.0.1'
 app.listen port, host
 console.log "Astrolin has started on http://#{host}:#{port}"


state change events

state change events

--- src/game_state.coffee
+++ src/game_state.coffee
@@ -57,7 +57,7 @@
     [I.objects, toRemove] = I.objects.partition (object) ->
       object.update()

-    I.objects.invoke
+    I.objects.invoke "trigger"

     toRemove.invoke "trigger", "remove"


Added submitter

Added submitter

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -160,7 +160,7 @@
     date: "Fecha"
     body: "Cuerpo"
     version: "Versión"
-#   submitter: "Submitter"
+    submitter: "Emisor" #O originador, que dicen?
     submitted: "Enviado"
     commit_msg: "Enviar mensaje"
     review: "Revisión"


Add @include snippet to replace default

Add @include snippet to replace default

--- .atom/snippets.cson
+++ .atom/snippets.cson
@@ -35,7 +35,7 @@
     'prefix': 'p'
     'body': '
$1
'
   'Span':
-  # Who adds a span without a class, right?
+  # Atom default span doesn't include a class.
     'prefix': 'span'
     'body': '$2'

@@ -52,3 +52,9 @@
   'Magento layout update handle':
     'prefix': 'update'
     'body': '<upated handle="${1:name}" />'
+
+'.source.css.scss':
+  'Sass include':
+  # Atom default includes closing semi-colon, which I usually don't use.
+    'prefix': 'include'
+    'body': '@include '


Slow down subject frame play, close #405

--- app/components/subject-viewer.cjsx
+++ app/components/subject-viewer.cjsx
@@ -10,7 +10,7 @@
 ROOT_STYLE = display: 'block'
 CONTAINER_STYLE = display: 'inline-block', position: 'relative'
 SUBJECT_STYLE = display: 'block'
-PLAYING_FRAME_DURATION = 333
+PLAYING_FRAME_DURATION = 667

 module.exports = React.createClass
   displayName: 'SubjectViewer'


Fix conflict

Fix conflict

--- app/js/controllers/transfer.coffee
+++ app/js/controllers/transfer.coffee
@@ -163,46 +163,6 @@
         $scope.gravatar_account_name = name

     $scope.accountSuggestions = (input) ->
-<<<<<<< HEAD
-        nItems=10
-        deferred = $q.defer()
-        ret = []
-        regHash={}
-        $scope.gravatar_account_name = ""
-        # Avoid scam names (look-alikes), NEVER auto-fill account names from the blockchain
-        angular.forEach Wallet.accounts, (val) ->
-            if val.name.substring(0, input.length) == input
-                if (regHash[val.name])
-                    ret.push {'name': val.name, 'is_favorite': val.is_favorite, 'approved': val.approved}
-                else
-                    ret.push {'name': val.name, 'is_favorite': val.is_favorite, 'approved': val.approved, 'unregistered': true}
-        ret.sort(compare)
-        deferred.resolve(ret)
-        return deferred.promise
-
-    compare = (a, b) ->
-        return -1  if a.name < b.name
-        return 1  if a.name > b.name
-        0
-
-    $scope.toggleVoteUpContact = (name) ->
-        newApproval=1
-        if ($scope.accounts[name] && $scope.accounts[name].approved>0)
-            newApproval=-1
-        if ($scope.accounts[name] && $scope.accounts[name].approved<0)
-            newApproval=0
-        Wallet.approve_account(name, newApproval).then (res)->
-            Wallet.refresh_account(name).then () ->
-                $scope.accounts=Wallet.accounts
-
-    $scope.toggleFavoriteContact = (name) ->
-        is_favorite=true
-        if (Wallet.accounts[name] && Wallet.accounts[name].is_favorite)
-            is_favorite=false
-        WalletAPI.account_set_favorite(name, is_favorite).then () ->
-            Wallet.refresh_account(name).then () ->
-                $scope.accounts=Wallet.accounts
-=======
         $filter('filter')(Object.keys(Wallet.favorites),input)

 #    $scope.toggleVoteUpContact = (name) ->
@@ -240,4 +200,3 @@
         $scope.add_to_address_book.message = null
         $scope.add_to_address_book.error = null
         my_transfer_form?.payto.error_message = null
->>>>>>> newlayout


update to support partial neighborhoods

update to support partial neighborhoods

--- source/javascripts/all.coffee
+++ source/javascripts/all.coffee
@@ -38,7 +38,7 @@
   </table>
   "

-d3.json 'data/neighborhoods-simplified.json', (pdx) ->
+d3.json 'data/neighborhoods.json', (pdx) ->
   neighborhoods = topojson.feature pdx, pdx.objects.neighborhoods
   projection.scale(1).translate [0, 0]

@@ -159,26 +159,24 @@
   nhoods = {}
   for hood in neighborhoods.features
     name = hood.properties.name
-    shared = hood.properties.shared
-
-    if !shared && !shouldExcludeNeighborhood(name)
-      current = nhoods[name] = {}
-      current[2010] = data[2010].filter((d) -> d.NEIGHBORHOOD == name)[0]
-      current[2000] = data[2000].filter((d) -> d.NEIGHBORHOOD == name)[0]
-
-      for key, ids of window.__mappings
-        try
-          from   = d3.sum(ids[0].map (id) -> current[2000][id])
-          to     = d3.sum(ids[1].map (id) -> current[2010][id])
-          change = to - from
-          growth = parseFloat ((change / from) * 100).toFixed(1)
-          growth = 100 if !isFinite growth
-          current[key] = [from, to, change, growth]
-        catch e
-          console.log "ERROR!!!! #{e} #{name} #{key}"
-
-      delete current[2000]
-      delete current[2010]
+
+    current = nhoods[name] = {}
+    current[2010] = data[2010].filter((d) -> d.NEIGHBORHOOD == name)[0]
+    current[2000] = data[2000].filter((d) -> d.NEIGHBORHOOD == name)[0]
+
+    for key, ids of window.__mappings
+      try
+        from   = d3.sum(ids[0].map (id) -> current[2000][id])
+        to     = d3.sum(ids[1].map (id) -> current[2010][id])
+        change = to - from
+        growth = parseFloat ((change / from) * 100).toFixed(1)
+        growth = 100 if !isFinite growth
+        current[key] = [from, to, change, growth]
+      catch e
+        console.log "ERROR!!!! #{e} #{name} #{key}"
+
+    delete current[2000]
+    delete current[2010]

   places = d3.entries(nhoods).filter (nh) -> !shouldExcludeNeighborhood(nh.key)
   highlight 'Total Population', 0
@@ -188,7 +186,7 @@
   out  = {}
   index = 0
   for year in years
-    d3.csv "data/portland-neighborhood-demographics-#{year}.csv", (data) ->
+    d3.csv "data/#{year}-census.csv", (data) ->
       data.map (row) ->
         for key, val of row
           row[key] = parseFloat val if key isnt 'NEIGHBORHOOD'


Add .pool functions

Add .pool functions

--- source/ninja-build-gen.coffee
+++ source/ninja-build-gen.coffee
@@ -72,6 +72,11 @@
     # Bind a variable to a temporary value for the edge.
     assign: (name, value) ->
         @assigns[name] = value
+        this
+
+
+    pool: (pool) ->
+        @_pool = pool
         this

     # Write the edge into a `stream`.
@@ -85,6 +90,7 @@
         for name, value of @assigns
             stream.write "\n  #{name} = #{value}"
         stream.write '\n'
+        stream.write "  pool = #{@_pool}\n" if @_pool?

 # Represent a Ninja rule, that is, a method to "how I build a file of type A
 # to type B".
@@ -117,12 +123,17 @@
         @isGenerator = isGenerator
         this

+    pool: (pool) ->
+        @_pool = pool
+        this
+
     # Write the rule into a `stream`.
     write: (stream) ->
         stream.write "rule #{@name}\n  command = #{@command}\n"
         stream.write "  description = #{@desc}\n" if @desc?
         stream.write "  restat = 1\n" if @doRestat
         stream.write "  generator = 1\n" if @isGenerator
+        stream.write "  pool = #{@_pool}\n" if @_pool?
         if @dependencyFile?
             stream.write "  depfile = #{@dependencyFile}\n"
             stream.write "  deps = gcc\n"


adding closure affected indentation

adding closure affected indentation

--- assets/js/teacher.coffee
+++ assets/js/teacher.coffee
@@ -45,7 +45,7 @@
             ((closed_id) ->
               $("#nick-#{closed_id}").toggle () ->
                 $("#text-container-#{closed_id}").show("explode", 1000);
-            , () ->
+              , () ->
                 $("#text-container-#{closed_id}").hide("explode", 1000);)(id);
     #also, delete any ids that are still client side but have disconnected from the server
      _.map $('.container').find('.student-box'),


do not use global eslint

do not use global eslint

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -14,8 +14,7 @@
   linter:
     errorPanelHeight: 176
     showErrorPanel: false
-  "linter-eslint":
-    useGlobalEslint: true
+  "linter-eslint": {}
   "markdown-preview":
     useGitHubStyle: true
   minimap: {}


atom config

atom config

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -63,8 +63,7 @@
     pullBeforePush: "pull --rebase"
   "go-plus":
     goPath: "/Users/holman/Code/go"
-  linter:
-    showErrorPanel: false
+  linter: {}
   "linter-elixirc":
     elixircPath: "/usr/local/bin/elixirc"
     mixPath: "/usr/local/bin/mix"


Fix the init.coffee workspaceView deprecation

--- atom.symlink/init.coffee
+++ atom.symlink/init.coffee
@@ -1,6 +1 @@
-path = require 'path'
-
-atom.workspaceView.eachEditorView (editorView) ->
-  editor = editorView.getEditor()
-  if path.extname(editor.getPath()) is '.md'
-    editor.setSoftWrap(true)
+# Add any auto-loaded Atom code on init here.


Add hideTooltip option to manually hide tooltip

--- core/KDView.coffee
+++ core/KDView.coffee
@@ -758,6 +758,10 @@
     o.title    or= ""
     @$(o.selector)[0].setAttribute "original-title", o.title

+  hideTooltip:(o = {})->
+    o.selector or= null
+    @$(o.selector).tipsy "hide"
+
   listenWindowResize:->

     @getSingleton('windowController').registerWindowResizeListener @


made bokejs and python max_zoom levels match

--- bokehjs/src/coffee/renderer/tile/mercator_tile_source.coffee
+++ bokehjs/src/coffee/renderer/tile/mercator_tile_source.coffee
@@ -8,7 +8,7 @@
   defaults: =>
     return _.extend {}, super(), {
       tile_size : 256
-      max_zoom : 25
+      max_zoom : 30
       min_zoom : 0
       extra_url_vars : {}
       full_extent : [-20037508.34, -20037508.34, 20037508.34, 20037508.34]


Add tests for Anon custom storage keys

Add tests for Anon custom storage keys

--- test/casperjs/kissmetrics-anon.coffee
+++ test/casperjs/kissmetrics-anon.coffee
@@ -41,6 +41,13 @@
 	@test.assertTruthy ID, 'ID is stored in localStorage'

 casper.then ->
+	ID = @evaluate ->
+		new window.AnonKissmetricsClient 'abc123', {storageKey: 'customKey'}
+		window.localStorage.getItem 'customKey'
+
+	@test.assertTruthy ID, 'ID is stored in localStorage with custom storage key'
+
+casper.then ->
 	data = @evaluate ->
 		km = new window.AnonKissmetricsClient 'abc123', {storage: 'cookie'}
 		{cookies: document.cookie, person: km.person}
@@ -49,6 +56,16 @@
 	@test.assertTruthy data.cookies.match matchString, 'Cookie is saved'

 casper.then ->
+	data = @evaluate ->
+		km = new window.AnonKissmetricsClient 'abc123',
+			storage: 'cookie'
+			storageKey: 'somethingCustom'
+		{cookies: document.cookie, person: km.person}
+
+	matchString = "somethingCustom=#{data.person}"
+	@test.assertTruthy data.cookies.match matchString, 'Cookie is saved with custom storage key'
+
+casper.then ->
 	originalPerson = @evaluate ->
 		km = new window.AnonKissmetricsClient 'abc123', {storage: 'cookie'}
 		km.person
@@ -61,6 +78,23 @@

 casper.then ->
 	originalPerson = @evaluate ->
+		km = new window.AnonKissmetricsClient 'abc123',
+			storage: 'cookie'
+			storageKey: 'somethingCustom'
+
+		km.person
+
+	newPerson = @evaluate ->
+		km = new window.AnonKissmetricsClient 'abc123',
+			storage: 'cookie'
+			storageKey: 'somethingCustom'
+
+		km.person
+
+	@test.assertEquals originalPerson, newPerson, 'Person is persistent in cookies with custom storage key'
+
+casper.then ->
+	originalPerson = @evaluate ->
 		km = new window.AnonKissmetricsClient 'abc123', {storage: 'localStorage'}
 		km.person

@@ -69,6 +103,21 @@
 		km.person

 	@test.assertEquals originalPerson, newPerson, 'Person is persistent in localStorage'
+
+casper.then ->
+	originalPerson = @evaluate ->
+		km = new window.AnonKissmetricsClient 'abc123',
+			storage: 'localStorage'
+			storageKey: 'customKey'
+		km.person
+
+	newPerson = @evaluate ->
+		km = new window.AnonKissmetricsClient 'abc123',
+			storage: 'localStorage'
+			storageKey: 'customKey'
+		km.person
+
+	@test.assertEquals originalPerson, newPerson, 'Person is persistent in localStorage with custom storage key'

 # Record


Fix spec to get all courses

Fix spec to get all courses

--- test/course-listing-store.spec.coffee
+++ test/course-listing-store.spec.coffee
@@ -12,7 +12,7 @@

   it 'should load courses', ->
     expect(CourseListingStore.isLoaded()).to.be.true
-    names = _.pluck(CourseListingStore.allValidCourses(), 'name')
+    names = _.pluck(CourseListingStore.allCourses(), 'name')
     expect(names).to.deep.equal(_.pluck(COURSES, 'name'))

   it 'should clear the store and the course store on reset', ->


chore: fixed decimal separator in tests for Estonia (#32)

--- test/parse-decimal-number-test.coffee
+++ test/parse-decimal-number-test.coffee
@@ -10,7 +10,7 @@
   {thousands:' ', decimal:','}  # 1 234,56   -- French
   {thousands:',', decimal:'/'}  # 1,234/56   -- Persian (Iran)
   {thousands:' ', decimal:'-'}  # 1 234-56   -- Kazakhstan
-  {thousands:' ', decimal:' '}  # 1 234.56   -- Estonia
+  {thousands:' ', decimal:'.'}  # 1 234.56   -- Estonia
 ]

 buildNumber = (max,doFraction,separators)->


Improved device platform test error messages

--- test/tests/DeviceComponentTest.coffee
+++ test/tests/DeviceComponentTest.coffee
@@ -158,15 +158,15 @@
 			device.deviceType = key
 			switch device.platform()
 				when "iOS"
-					assert(_.startsWith(key, "iphone") or _.startsWith(key, "ipad") or _.startsWith(key, "apple-iphone") or _.startsWith(key, "apple-ipad"))
+					assert(_.startsWith(key, "iphone") or _.startsWith(key, "ipad") or _.startsWith(key, "apple-iphone") or _.startsWith(key, "apple-ipad"), "#{key} should not have platform iOS")
 				when "watchOS"
-					assert(_.startsWith(key, "apple-watch") or _.startsWith(key, "applewatch"))
+					assert(_.startsWith(key, "apple-watch") or _.startsWith(key, "applewatch"), "#{key} should not have platform watchOS")
 				when "Windows"
-					assert(_.startsWith(key, "dell") or _.startsWith(key, "microsoft"))
+					assert(_.startsWith(key, "dell") or _.startsWith(key, "microsoft"), "#{key} should not have platform Windows")
 				when "Android"
-					assert(_.startsWith(key, "google") or _.startsWith(key, "nexus") or _.startsWith(key, "htc") or _.startsWith(key, "samsung"))
+					assert(_.startsWith(key, "google") or _.startsWith(key, "nexus") or _.startsWith(key, "htc") or _.startsWith(key, "samsung"), "#{key} should not have platform Android")
 				when "macOS"
-					assert(_.startsWith(key, "apple-macbook") or _.startsWith(key, "apple-imac") or _.startsWith(key, "desktop-safari"))
+					assert(_.startsWith(key, "apple-macbook") or _.startsWith(key, "apple-imac") or _.startsWith(key, "desktop-safari"), "#{key} should not have platform macOS")
 				else
 					# Exceptions
-					assert(key in ["fullscreen", "custom", "sony-w85Oc", "test"])
+					assert(key in ["fullscreen", "custom", "sony-w85Oc", "test"], "#{key} should have a platform specified")


Updating messageing and added courier logging to a few places

--- action.coffee
+++ action.coffee
@@ -31,8 +31,10 @@
     func = interpolate(courier, func)
     args = _.map(args, (s) -> interpolate(courier, s))
     func = eval("[#{func}][0]")
-    @echo "Applying '#{func}' within Casper."
+    message = "Applying '#{func}' within Casper."
+    @echo message
     func.apply @, ([courier, @]).concat(args)
+    courier.response(message, true, action)

 captureScreenShot = (action, courier, casper)->
   selector = getOrElse(action, "select", "body")
@@ -66,8 +68,8 @@
     message = "Clicking on \<#{selector}\>."
     @echo message
     @click selector
-    courier.response(message, true, action)
     @wait(1000)
+    courier.response(message, true, action)

 downloadFile = (action, courier, casper)->
   url = getOrElse(action, "url")
@@ -96,11 +98,16 @@
     key = interpolate(courier, key)
     func = interpolate(courier, func)
     args = _.map(args, (s) -> interpolate(courier, s))
-    @echo "Evaluating -> #{func}"
+    message = "Evaluating -> #{func}"
+    @echo message
     func = eval("[#{func}]")
+    courier.response(message, true, action)
     evaluated = @evaluate.apply @, func.concat(args)
-    @echo "Storing '#{key}':'#{evaluated}' from evaluating -> #{func}."
+    message = "Storing '#{key}':'#{evaluated}' from evaluating -> #{func}."
+    @echo message
     courier.store(key, evaluated)
+    courier.response(message, true, action)
+

 fillElement = (action, courier, casper) ->
   form = getOrElse(action, "form")
@@ -226,7 +233,7 @@
   casper.then ->
     selector = interpolate(courier, selector)
     text = interpolate(courier, text)
-    message = "Selecting /<#{selector}/> and Typing '#{text}' in \<#{selector}\>."
+    message = "Typing '#{text}' in \<#{selector}\>."
     @echo message
     @wait(500)
     @click selector


Characters: better users.coffee, #470

Characters: better users.coffee, #470

--- tests/user.coffee
+++ tests/user.coffee
@@ -25,24 +25,14 @@
 query = null

-queryOne = (str, values) ->
-	rows = query.all(str, values)
-	throw new Error('In query:\n' + query + '\nExpected one row, but got ' + rows.length) if rows.length isnt 1
-	rows[0]
-
-migrateTables = ->
-	args = (i for i in arguments)
-	mg.migrate.sync mg, conn, tables: args
-
-migrate = ->
-	mg.migrate.sync mg, conn
+clearTables = ->
+	query 'TRUNCATE ' + [].join.call(arguments, ', ')

 exports.setUp = (->
 	conn = anyDB.createConnection(config.DATABASE_URL_TEST)
 	query = queryUtils.getFor conn
-	query 'DROP TABLE IF EXISTS revision, uniusers, locations, characters, monsters, monster_prototypes'
-	query 'DROP TYPE IF EXISTS permission_kind'
+	mg.migrate.sync mg, conn
 ).async()

 exports.tearDown = (->
@@ -60,7 +50,7 @@
 #		'should fail on nonexistent table'
 #	)

-	migrateTables 'permission_kind', 'uniusers'
+	clearTables 'uniusers'
 	query 'INSERT INTO uniusers (username) VALUES ( $1 )', ['Sauron']

 	test.strictEqual userExists('Sauron'), true, 'should return true if user exists'
@@ -73,7 +63,7 @@

 exports.idExists = (test) ->
-	migrateTables 'permission_kind', 'uniusers'
+	clearTables 'uniusers'
 	query 'INSERT INTO uniusers (id) VALUES ( $1 )', [114]

 	test.strictEqual users.idExists.sync(null, conn, 114), true, 'should return true when user exists'
@@ -83,7 +73,7 @@

 exports.sessionExists =
 	testNoErrors: (test) ->
-		migrateTables 'permission_kind', 'uniusers'
+		clearTables 'uniusers'
 		query "INSERT INTO uniusers (sessid) VALUES ('someid')"

 		exists = users.sessionExists.sync(null, conn, 'someid')
@@ -93,15 +83,10 @@
 		test.strictEqual exists, false, 'should return false when sessid does not exist'
 		test.done()

-#	testErrors: (test) ->
-#		users.sessionExists conn, 'someid', (error, result) ->
-#			test.ok error, 'should return error without table'
-#			test.done()
-

 exports.sessionInfoRefreshing =
 	testNoErrors: (test) ->
-		migrateTables 'permission_kind', 'uniusers'
+		clearTables 'uniusers'
 		query "INSERT INTO uniusers " +
 			"(id, username, permissions, sessid, sess_time) " +
 			"VALUES (8, 'user0', 'admin', 'expiredid', NOW() - INTERVAL '3600 SECOND' )"
@@ -117,7 +102,7 @@

 		query "UPDATE uniusers SET sessid = 'someid'"

-		timeBefore = new Date(queryOne('SELECT sess_time FROM uniusers').sess_time)
+		timeBefore = new Date(query.val 'SELECT sess_time FROM uniusers')

 		res = users.sessionInfoRefreshing.sync null, conn, 'someid', 7200, false
 		test.deepEqual res, {
@@ -127,7 +112,7 @@
 			userid: 8
 		}, 'session should be active if not expired and user data should be returned'

-		timeAfter = new Date(queryOne('SELECT sess_time FROM uniusers').sess_time)
+		timeAfter = new Date(query.val 'SELECT sess_time FROM uniusers')
 		test.ok timeBefore < timeAfter, 'should update session timestamp'

 		res = users.sessionInfoRefreshing.sync null, conn, undefined, 7200, false
@@ -151,10 +136,10 @@
 			"(id, username, permissions, sessid, sess_time) " +
 			"VALUES (112, '112', 'admin', '123456', NOW() - INTERVAL '3600 SECOND' )"

-		timeBefore = new Date(queryOne('SELECT sess_time FROM uniusers WHERE id = 112').sess_time)
+		timeBefore = new Date(query.val('SELECT sess_time FROM uniusers WHERE id = 112').sess_time)
 		users.sessionInfoRefreshing.sync null, conn, '123456', 7200, true
 		sync.sleep 100
-		timeAfter = new Date(queryOne('SELECT sess_time FROM uniusers WHERE id = 112').sess_time)
+		timeAfter = new Date(query.val('SELECT sess_time FROM uniusers WHERE id = 112').sess_time)

 		#test.ok timeBefore < timeAfter, 'should update session timestamp with asyncUpdate'
 		test.done()
@@ -165,95 +150,50 @@
 #			test.done()

-exports.generateSessId =
-	testNoErrors: (test) ->
-		_createSalt = users.createSalt
-		i = 0
-		users.createSalt = (len) ->
-			'someid' + (++i)
-
-		async.series [
-			(callback) ->
-				mg.migrate conn, { tables: ['permission_kind', 'uniusers'] }, callback
-			(callback) ->
-				conn.query "INSERT INTO uniusers (sessid) VALUES ('someid1')", callback
-			(callback) ->
-				conn.query "INSERT INTO uniusers (sessid) VALUES ('someid2')", callback
-			(callback) ->
-				users.generateSessId conn, 16, callback
-			(callback) ->
-				conn.query 'DROP TABLE uniusers', [], callback
-		],
-		(error, result) ->
-			users.createSalt = _createSalt
-			test.ifError error
-			test.strictEqual result[3], 'someid3', 'sessid should be unique'
-			test.done()
-
-	testErrors: (test) ->
-		users.generateSessId conn, 16, (error, result) ->
-			test.ok error, 'should return error without table'
-			test.done()
+exports.generateSessId = (test) ->
+	_createSalt = users.createSalt
+	i = 0
+	users.createSalt = (len) ->
+		'someid' + (++i)
+
+	clearTables 'uniusers'
+	query "INSERT INTO uniusers (sessid) VALUES ('someid1')"
+	query "INSERT INTO uniusers (sessid) VALUES ('someid2')"
+	sessid = users.generateSessId.sync users, conn, 16
+
+	users.createSalt = _createSalt
+	test.strictEqual sessid, 'someid3', 'sessid should be unique'
+	test.done()

 exports.idBySession =
 	testNoErrors: (test) ->
-		async.series [
-			(callback) ->
-				mg.migrate conn, { tables: ['permission_kind', 'uniusers'] }, callback
-			(callback) ->
-				conn.query "INSERT INTO uniusers ( id, sessid ) VALUES ( 3, 'someid' )", callback
-			(callback) ->
-				users.idBySession conn, "someid", callback
-		],
-		(error, result) ->
-			test.ifError error
-			test.strictEqual result[2], 3, 'should return correct user id'
-			test.done()
+		clearTables 'uniusers'
+		query "INSERT INTO uniusers ( id, sessid ) VALUES ( 3, 'someid' )"
+		userid = users.idBySession.sync null, conn, "someid"
+		test.strictEqual userid, 3, 'should return correct user id'
+		test.done()

 	testWrongSessid: (test) ->
-		async.series [
-			(callback) ->
-				mg.migrate conn, { tables: ['permission_kind', 'uniusers'] }, callback
-			(callback) ->
-				users.idBySession conn, 'someid', callback
-			(callback) ->
-				conn.query 'DROP TABLE uniusers', [], callback
-		],
-		(error, result) ->
-			test.ok error, 'should return error on wrong sessid'
-			test.done()
-
-	testQueryError: (test) ->
-		users.idBySession conn, 'someid', (error, result) ->
-			test.ok error, 'should return error without table'
-			test.done()
-
-
-exports.closeSession =
-	testNoErrors: (test) ->
-		async.series [
-			(callback) -> #0
-				mg.migrate conn, { tables: ['permission_kind', 'uniusers'] }, callback
-			(callback) ->
-				conn.query "INSERT INTO uniusers ( sessid, sess_time ) VALUES ( 'someid', NOW() )", [], callback
-			(callback) ->
-				users.closeSession conn, 'someid', callback
-			(callback) ->
-				users.sessionInfoRefreshing conn, 'someid', 3600, callback
-			(callback) ->
-				users.closeSession conn, undefined, callback
-		],
-		(error, result) ->
-			test.ifError error
-			test.strictEqual result[3].sessionIsActive, false, 'session should have expired'
-			test.strictEqual result[4], 'Not closing: empty sessid', 'should not fail with empty sessid'
-			test.done()
-
-	testErrors: (test) ->
-		users.closeSession conn, 'someid', (error, result) ->
-			test.ok error, 'should return error without table'
-			test.done()
+		clearTables 'uniusers'
+		test.throws(
+			-> users.idBySession.sync null, conn, 'someid'
+			Error
+			'should return error on wrong sessid'
+		)
+		test.done()
+
+
+exports.closeSession = (test) ->
+	clearTables 'uniusers'
+	query "INSERT INTO uniusers ( sessid, sess_time ) VALUES ( 'someid', NOW() )"
+	users.closeSession.sync null, conn, 'someid'
+	refr = users.sessionInfoRefreshing.sync null, conn, 'someid', 3600
+	warn1 = users.closeSession.sync null, conn, undefined
+
+	test.strictEqual refr.sessionIsActive, false, 'session should have expired'
+	test.strictEqual warn1, 'Not closing: empty sessid', 'should not fail with empty sessid'
+	test.done()

 exports.createSalt = (test) ->
@@ -269,7 +209,7 @@

 exports.registerUser = (test) ->
-	migrateTables 'permission_kind', 'uniusers', 'locations', 'monsters', 'monster_prototypes', 'characters'
+	clearTables 'uniusers', 'locations', 'monsters', 'monster_prototypes', 'characters'
 	query 'INSERT INTO locations (id, initial) VALUES (2, 1)'

 	users.registerUser.sync null, conn, 'TheUser', 'password', 'admin'
@@ -294,81 +234,32 @@
 	test.done()

-exports.accessGranted =
-	testNoErrors: (test) ->
-		async.series [
-			(callback) ->
-				mg.migrate conn, { tables: ['permission_kind', 'uniusers', 'locations',
-					'monsters', 'monster_prototypes', 'characters'] }, callback
-			(callback) ->
-				conn.query 'INSERT INTO locations (id, initial) VALUES (2, 1)', callback
-			(callback) ->
-				users.registerUser conn, 'TheUser', 'password', 'user', callback
-			(callback) ->
-				users.accessGranted conn, 'TheUser', 'password', callback
-			(callback) ->
-				users.accessGranted conn, 'WrongUser', 'password', callback
-			(callback) ->
-				users.accessGranted conn, 'TheUser', 'wrongpass', callback
-			(callback) ->
-				users.accessGranted conn, 'THEUSER', 'password', callback
-			(callback) ->
-				users.accessGranted conn, 'theuser', 'password', callback
-		], (error, result) ->
-			test.ifError error
-			test.strictEqual result[3], true, 'should return true for valid data'
-			test.strictEqual result[4], false, 'should return false if user does not exist'
-			test.strictEqual result[5], false, 'should return false if password is wrong'
-			test.strictEqual result[6], true, 'should ignore capitalization'
-			test.strictEqual result[7], true, 'should ignore capitalization'
-			test.done()
-
-	testErrors: (test) ->
-		users.accessGranted conn, 'TheUser', 'password', (error, result) ->
-			test.ok error
-			test.done()
-
-
-exports.createSession =
-	testNoErrors: (test) ->
-		async.series [
-			(callback) -> #0
-				mg.migrate conn, { tables: ['permission_kind', 'uniusers', 'locations',
-					'monsters', 'monster_prototypes', 'characters'] }, callback
-			(callback) ->
-				conn.query 'INSERT INTO locations (id, initial) VALUES (2, 1)', callback
-			(callback) ->
-				users.registerUser conn, 'Мохнатый Ангел', 'password', 'user', callback
-			(callback) ->
-				conn.query 'SELECT sessid FROM uniusers', [], callback
-			(callback) ->
-				users.createSession conn, 'МОХНАТЫЙ ангел', callback
-			(callback) -> #5
-				conn.query 'SELECT sessid, sess_time FROM uniusers', [], callback
-		],
-		(error, result) ->
-			test.ifError error
-			test.ok result[3].rows[0].sessid isnt result[5].rows[0].sessid,
-				'should change sessid'
-			test.ok result[5].rows[0].sess_time.getTime() > new Date().getTime() - 60000,
-				'should update session timestamp'
-			test.done()
-
-#	testErrors: (test) ->
-#		users.createSession conn, 10101, (error, result) ->
-#			test.ok error, 'should crash on wrong sessid'
-#			test.done()
-
-fixTest = (obj) ->
-	for attr of obj
-		if attr is 'setUp' or attr is 'tearDown'
-			continue
-
-		if obj[attr] instanceof Function
-			obj[attr] = ((origTestFunc, t) -> (test) ->
-					console.log(t)
-					origTestFunc(test)
-				)(obj[attr], attr)
-		else
-			fixTest(obj[attr])
-fixTest exports
+exports.accessGranted = (test) ->
+	clearTables 'uniusers', 'characters', 'locations'
+	query 'INSERT INTO locations (id, initial) VALUES (2, 1)'
+	users.registerUser.sync null, conn, 'TheUser', 'password', 'user'
+
+	[
+		{name:'TheUser',   pass:'password',  ok:true,  msg:'should return true for valid data'}
+		{name:'WrongUser', pass:'password',  ok:false, msg:'should return false if user does not exist'}
+		{name:'WrongUser', pass:'wrongpass', ok:false, msg:'should return false if password is wrong'}
+		{name:'THEUSER',   pass:'password',  ok:true,  msg:'should ignore capitalization'}
+		{name:'theuser',   pass:'password',  ok:true,  msg:'should ignore capitalization (2)'}
+	].forEach (t) ->
+		granted = users.accessGranted.sync null, conn, t.name, t.pass
+		test.strictEqual granted, t.ok, t.msg
+	test.done()
+
+
+exports.createSession = (test) ->
+	clearTables 'uniusers', 'locations', 'characters'
+	query 'INSERT INTO locations (id, initial) VALUES (2, 1)'
+	users.registerUser.sync null, conn, 'Мохнатый Ангел', 'password', 'user'
+	user0 = query.row 'SELECT sessid FROM uniusers'
+	users.createSession.sync null, conn, 'МОХНАТЫЙ ангел'
+	user1 = query.row 'SELECT sessid, sess_time FROM uniusers'
+
+	test.ok user0.sessid isnt user1.sessid, 'should change sessid'
+	test.ok user1.sess_time.getTime() > Date.now() - 60000, 'should update session timestamp'
+	test.done()
+


more gitpages fiddling

more gitpages fiddling

--- core/layout.coffee
+++ core/layout.coffee
@@ -320,7 +320,7 @@
         #preload skin
         unless fileloaded['skin']
           fileloaded['skin'] = true
-          loadInclude("/classes/skin.dre")
+          loadInclude(DREEM_ROOT + "/classes/skin.dre")

       # load includes
         for url, el of findIncludeURLs()
@@ -344,7 +344,7 @@
           # load missing classes
           for name, el of findMissingClasses()
             fileloaded[name] = true
-            loadInclude("/classes/" + name.split(tagPackageSeparator).join('/') + ".dre", el) if name
+            loadInclude(DREEM_ROOT + "/classes/" + name.split(tagPackageSeparator).join('/') + ".dre", el) if name
             # console.log 'loading dre', name, url, el

           # console.log(filerequests, fileloaded, inlineclasses)


fit latest server-side api

fit latest server-side api

--- src/lib/screen.coffee
+++ src/lib/screen.coffee
@@ -45,7 +45,7 @@
     _saveLast: (callback) =>
         DB.run 'INSERT OR REPLACE INTO META (key, value) VALUES ($key, $value)',
             $key: 'LAST_SCREENID'
-            $value: @data.id
+            $value: @data.actid
         , callback

     # Connect to remote
@@ -67,22 +67,22 @@

             (callback) =>
                 # update preference URI
-                @preference.URI = "#{CONFIG.Client.Service}/#{@data.id}"
+                @preference.URI = "#{CONFIG.Client.Service}/#{@data.actid}"

                 # create (if not exists) assets directory
-                Assets.createDirectory @data.id, callback
+                Assets.createDirectory @data.actid, callback

             (callback) =>
                 # update database
                 DB.run 'INSERT OR REPLACE INTO INFO (SID, data) VALUES ($sid, $data)',
-                    $sid: @data.id
+                    $sid: @data.actid
                     $data: JSON.stringify(@data)
                 , callback

             (callback) =>
                 # query preference
                 DB.all 'SELECT * FROM PREFERENCE WHERE SID = $sid LIMIT 1',
-                    $sid: @data.id
+                    $sid: @data.actid
                 , (err, data) =>
                     @preference = JSON.parse(data[0].DATA) if data? and data.length > 0
                     callback()
@@ -154,22 +154,22 @@
     # Reveal assets directory
     revealAssets: (callback) =>
         throw new Error('Please connect to a screen') if not @connected
-        Assets.openDirectory @data.id
+        Assets.openDirectory @data.actid
         callback && callback()

     # Scan assets directory and add missing assets
     scanAssets: (callback) =>
         throw new Error('Please connect to a screen') if not @connected
-        Assets.scan @data.id, callback
+        Assets.scan @data.actid, callback

     # Update the description of an asset and return all assets
     updateAssetDesc: (hash, description, callback) =>
         throw new Error('Please connect to a screen') if not @connected
-        Assets.updateDescription @data.id, hash, description, callback
+        Assets.updateDescription @data.actid, hash, description, callback

     # Update walls' background
     switchToAsset: (hash, callback) =>
-        Assets.get @data.id, hash, (err, asset) =>
+        Assets.get @data.actid, hash, (err, asset) =>
             return callback && callback err if err
             return callback && callback new Error('Asset not found') if not asset
             SERVER.io.to('wall').emit 'switchTo', asset
@@ -203,7 +203,7 @@
     # Update preference into database
     _updatePreference: (callback) =>
         DB.run 'INSERT OR REPLACE INTO PREFERENCE (SID, data) VALUES ($sid, $data)',
-            $sid: @data.id
+            $sid: @data.actid
             $data: JSON.stringify(@preference)
         , callback


bug fix

bug fix

--- src/lib/scutil.coffee
+++ src/lib/scutil.coffee
@@ -192,14 +192,14 @@

     getLatLngRegion: (points) ->

-        box = scutil.getBoundary()
-
-            NorthEast:
-                Lat: box.x_max
-                Lng: box.y_max
-            SouthWest:
-                Lat: box.x_min
-                Lng: box.y_min
+        box = scutil.getBoundary(points)
+
+        NorthEast:
+            Lat: box.x_max
+            Lng: box.y_max
+        SouthWest:
+            Lat: box.x_min
+            Lng: box.y_min

     discretize: (points) ->


fixup

fixup

--- src/remcoaudio.coffee
+++ src/remcoaudio.coffee
@@ -98,7 +98,7 @@

   triggerOneShot = ->
     if preset.get 'oneshotstate'
-      a = attack.vaue
+      a = attack.value
       d = decay.value
       amp.gain.setValueAtTime 0, audio.currentTime
       amp.gain.linearRampToValueAtTime 1, audio.currentTime + a
@@ -120,7 +120,5 @@
   # return a collection of exposed parameters
   {
     triggerOneShot: triggerOneShot
-    getLength: ->
-      console.log attack.value + decay.value
-      attack.value + decay.value
+    getLength: -> attack.value + decay.value
   }


intro: fix SourceMap path

intro: fix SourceMap path

--- projects.coffee
+++ projects.coffee
@@ -32,7 +32,7 @@
     files     : "client/Intro/includes.coffee"
     style     : "website/a/css/introapp.#{KODING_VERSION}.css"
     script    : "website/a/js/introapp.#{KODING_VERSION}.js"
-    sourceMapRoot : "introapp/"
+    sourceMapRoot : "Intro/"

   Landing     :
     files     : "client/landing/includes.coffee"


Removed debug setting on GitHub object.

Removed debug setting on GitHub object.

--- prviewer.coffee
+++ prviewer.coffee
@@ -73,7 +73,7 @@
 CACHE_MS = 5 * 60 * 1000

 getGitHubHelper = (token) ->
-  github = new GitHubAPI(version: '3.0.0', debug: true)
+  github = new GitHubAPI(version: '3.0.0')
   github.authenticate type: 'oauth', token: token
   return github
 getGitHubHelper = memoize getGitHubHelper # Cache forever.


Client: Activity: EmptySearchText renamed to noResultText

--- client/activity/lib/components/sidebarmodallist/index.coffee
+++ client/activity/lib/components/sidebarmodallist/index.coffee
@@ -21,7 +21,7 @@

     super

-    @state = { threads: @props.sidebarModalChannels.followed, emptySearchText: no, value: '', isSearching: no }
+    @state = { threads: @props.sidebarModalChannels.followed, noResultText: no, value: '', isSearching: no }

   onThresholdReached: ->
@@ -80,7 +80,7 @@

   getNoResultClassNames: -> classnames
     'ChannelList-emptySearch': yes
-    'hidden' : not @state.emptySearchText
+    'hidden' : not @state.noResultText

   renderChildren: ->


MachineSettingsSnapshotsView: Code styling.

--- client/app/lib/providers/machinesettingssnapshotsview.coffee
+++ client/app/lib/providers/machinesettingssnapshotsview.coffee
@@ -1,18 +1,15 @@
-Encoder                   = require 'htmlencode'
-
 kd                        = require 'kd'
 remote                    = require('app/remote').getInstance()
-
+Encoder                   = require 'htmlencode'
+SnapshotListItem          = require './snapshotlistitem'
+snapshotHelpers           = require './snapshothelpers'
 ComputeErrorUsageModal    = require './computeerrorusagemodal'
 MachineSettingsCommonView = require './machinesettingscommonview'
-SnapshotListItem          = require './snapshotlistitem'
-snapshotHelpers           = require './snapshothelpers'
-

 module.exports = class MachineSettingsSnapshotsView extends MachineSettingsCommonView

-  constructor:(options = {}, data) ->
+  constructor: (options = {}, data) ->

     options.cssClass             = kd.utils.curry options.cssClass, 'snapshots'
     options.headerTitle          = 'Snapshots'
@@ -23,8 +20,7 @@
       cssClass : 'no-item'
       partial  : 'You do not have any snapshots created'

-    # Trigger the snapshotsLimits fetch, so that we can cache it ahead
-    # of time.
+    # Trigger the snapshotsLimits fetch, so that we can cache it ahead of time.
     @snapshotsLimit()

     super options, data


derive config by removing '-' not '_'

derive config by removing '-' not '_'

--- code/linode.coffee
+++ code/linode.coffee
@@ -205,7 +205,7 @@
 # into an instance of the Linode class.
 _convert_to_instance = (l) =>
   o = {name: l['LABEL'].toString(), state: l['STATUS'], id: l['LINODEID']}
-  # The config is derived from the name, by removing the "_1"
+  # The config is derived from the name, by removing the "-1"
   # bit at the end.
-  config = o.name.replace /_\d+$/, ''
+  config = o.name.replace /-\d+$/, ''
   new Linode config, o.id, o.name, o.state


Resolved conflicts from main.coffee and committing

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

           if !@is_section_break
-            $('.readonly').find('input, textarea, select').attr('readonly', 'readonly') if @model.get("field_options").state is "readonly"
+            @$el.addClass('readonly') if @model.get("field_options").state is "readonly"
+            $('.readonly').find('input, textarea, select').attr('readonly', 'readonly');
             @$el.addClass('response-field-'+ @field_type + ' '+ @model.getCid())
               .data('cid', cid)
               .html(Formbuilder.templates["view/base#{base_templ_suff}"]({


Add prophet get use item (2)

Add prophet get use item (2)

--- parts/bottom-alert.cjsx
+++ parts/bottom-alert.cjsx
@@ -4,8 +4,11 @@
     <div>
       {
         if @props.getShip? or @props.getItem?
+          prepend = ""
+          if @props.getItem?.api_useitem_id is 68
+            prepend = "摸鱼成功！ "
           <Panel>
-            {"#{@props.getShip.api_ship_type} 「#{@props.getShip.api_ship_name}」 #{@props.joinFleet}"}
+            {prepend + "#{@props.getShip.api_ship_type} 「#{@props.getShip.api_ship_name}」 #{@props.joinFleet}"}
           </Panel>
         else if @props.formationNum != 0
           <Panel>


Basic index of apt-crab packages.

Basic index of apt-crab packages.

--- indexer.coffee
+++ indexer.coffee
@@ -95,6 +95,27 @@

 			callback(null, python_packages)

+	getAptCranPackages: () ->
+		child_process.execSync ' sudo apt-get update '
+		apt_packages = child_process.execSync ' apt-cache search "r-cran-.*" | grep "^r-cran.*$"'
+		lines = apt_packages.toString().split('\n')
+		packages = lines.map (line) ->
+			console.log line
+			match = line.match /^(.*) - (.*)$/
+			if match
+				name = match[1]
+				summary = match[2]
+				return {
+					name: name
+					summary: summary
+					description: null
+					url: null
+					command: "sudo apt-get install #{name}"
+				}
+		return _.without(packages, undefined)
+
+
+
 	build: (callback) ->
 		Indexer.buildPythonIndex (err, python_index) ->
 			if err?


Restore missing page/book toggle for the attribution tab

--- src/scripts/modules/media/footer/attribution/attribution.coffee
+++ src/scripts/modules/media/footer/attribution/attribution.coffee
@@ -5,5 +5,7 @@

   return class AttributionView extends FooterTabView
     template: template
-    templateHelpers:
-      id: () -> @model.getVersionedId() # Ensure the id has the version attached
+    templateHelpers: () ->
+      model = super()
+      model.id = @model.getVersionedId() # Ensure the id has the version attached
+      return model


Fix facebook login on mobile

Fix facebook login on mobile

--- src/mobile/apps/auth/client/signup.coffee
+++ src/mobile/apps/auth/client/signup.coffee
@@ -71,7 +71,8 @@

     if $checkbox.is(':checked')
       $('.gdpr-signup__form__checkbox__accept-terms').removeClass('tos-error')
-      window.location = SIGNUP_URLS[signupMethod] + "&#{redirectTo}"
+      redirectStr = if signupMethod is 'facebook' then "?#{redirectTo}" else "&#{redirectTo}"
+      window.location = SIGNUP_URLS[signupMethod] + redirectStr
     else
       $('.gdpr-signup__form__checkbox__accept-terms').addClass('tos-error')


less array access

less array access

--- translator.coffee
+++ translator.coffee
@@ -179,15 +179,15 @@
 # ###################################################################################################

 trans.translator_hash['block_regexp'] = translate:(ctx, node)->
-  # Assuming that the token starts with "///" and therefore parts[0] is an empty string.
-  parts = node.value_view.split "///"
-  parts[1] = parts[1].replace /\s#.*/g, ''
-  parts[1] = parts[1].replace /\s/g, ''
-  if parts[1] == ""
-    parts[1] = "(?:)"
+  # Assuming that the token starts with "///" and therefore _skip is an empty string.
+  [_skip, body, flags] = node.value_view.split "///"
+  body = body.replace /\s#.*/g, ''
+  body = body.replace /\s/g, ''
+  if body == ""
+    body = "(?:)"
   else
-    parts[1] = parts[1].replace /\//g, '\\/'
-  '/' + parts[1] + '/' + parts[2]
+    body = body.replace /\//g, '\\/'
+  '/' + body + '/' + flags

 trans.translator_hash['block_regexp_start'] = translate:(ctx, node)->
   ret = node.value_view[3...-2]


Remove prototyped roy code.

Remove prototyped roy code.

--- src/testrunner.coffee
+++ src/testrunner.coffee
@@ -14,14 +14,6 @@
     coffee.compile content
   else if extension is '.js'
     content
-
-let directorySpecs directory specs file =
-  let filePath = path.join directory file
-  if fs.statSync(filePath).isDirectory()
-    fs.readdirSync(filePath).reduce getDirectorySpecs(filePath), specs
-  else
-    specs.push compileSpecFile filePath
-  specs

 getDirectorySpecs = (directory) -> (specs, file) ->
   filePath = path.join directory, file


improve response; initial queueing

improve response; initial queueing

--- src/virustotal.coffee
+++ src/virustotal.coffee
@@ -18,6 +18,57 @@

 moment = require 'moment'
 virustotal = require 'virustotal.js'
+
+maxRequestsPerInterval = 4
+requestWindow = 1000
+defaultwaitms = 1000
+
+# send queue
+sq = []
+
+queueUrlReport = (robot, msg, url) ->
+  ms = Date.now()
+  sq.push { whenqueued: ms, sent: false, robot: robot, msg: msg, url: url }
+
+servicequeue = ->
+  ms = Date.now()
+  cutoffms = ms - (requestWindow * maxRequestsPerInterval)
+
+  # remove stale
+  q = sq
+  for o in q when o.whenqueued < cutoffms and o.sent
+    sq.splice(sq.indexOf(o), 1)
+
+  # count already sent objects, within the request window
+  credit = maxRequestsPerInterval
+  credit-- for o in sq when o.sent
+
+  # send as many as we have credit for
+  for o in sq when o.sent isnt true
+    break if credit < 1
+    getUrlReport o.robot, o.msg, o.url
+    o.sent = true
+    credit--
+
+  # restart timer
+  setTimeout servicequeue, defaultwaitms
+
+getUrlReport = (robot, msg, url) ->
+  virustotal.getUrlReport url, (err, res) ->
+    if err
+      msgout = "Virus total: error: `#{err.json.verbose_msg}`."
+      robot.logger.info "#{msgout} [#{msg.envelope.user.name}]"
+      return robot.send {room: msg.envelope.user.name}, msgout
+
+    r = { clean: 0, unclean: 0 }
+    for scanner, obj of res.scans
+      r.clean++ unless obj.detected
+      r.unclean++ if obj.detected
+
+    msgout = "Virus total: `#{res.resource.replace('http:\/\/','')}` rated clean by #{r.clean}, unclean by #{r.unclean}.  More info: #{res.permalink}"
+    robot.logger.info "#{msgout} [#{msg.envelope.user.name}]"
+    return robot.send {room: msg.envelope.user.name}, msgout
+

 module.exports = (robot) ->

@@ -42,22 +93,19 @@
     url = msg.match[1]

     robot.logger.info "Virus total: url report request: #{url} [#{msg.envelope.user.name}]"
-
+
     virustotal.getUrlReport url, (err, res) ->
       if err
         msgout = "Virus total: error: `#{err.json.verbose_msg}`."
         robot.logger.info "#{msgout} [#{msg.envelope.user.name}]"
         return robot.send {room: msg.envelope.user.name}, msgout

-      clean = []
+      r = { clean: 0, unclean: 0 }
       for scanner, obj of res.scans
-        clean.push "`#{scanner}`" unless obj.detected
+        r.clean++ unless obj.detected
+        r.unclean++ if obj.detected

-      unclean = []
-      for scanner, obj of res.scans
-        unclean.push "`#{scanner}` (#{obj.result})" if obj.detected
-
-      msgout = "Virus total: `#{res.resource.replace('http:\/\/','')}` rated clean by #{clean.length}, unclean by #{unclean.length}.  More info: #{res.permalink}"
+      msgout = "Virus total: `#{res.resource.replace('http:\/\/','')}` rated clean by #{r.clean}, unclean by #{r.unclean}.  More info: #{res.permalink}"
       robot.logger.info "#{msgout} [#{msg.envelope.user.name}]"
       return robot.send {room: msg.envelope.user.name}, msgout


perftest: using Pump.copy() instead of directly writing buffer

--- perftest/pump10m.coffee
+++ perftest/pump10m.coffee
@@ -26,7 +26,7 @@
         pumped++
         if !(pumped % 1000000)
           console.log 'Pumped ' + (pumped / 1000000) + ' million items'
-        @buffer().writeAsync data
+        @copy data

     pump.on 'end', ->
       if pumped == limit && created == limit


Course: Clarify student upgrade description

--- src/smc-webapp/course/settings_panel.cjsx
+++ src/smc-webapp/course/settings_panel.cjsx
@@ -564,7 +564,7 @@
             {if @state.upgrade_quotas then @render_upgrade_quotas() else @render_upgrade_quotas_button()}

-
Add additional quota upgrades to all of the projects in this course, augmenting what is provided for free and what students may have purchased.  Your contributions will be split evenly between student projects.

+
Add additional quota upgrades to all of the projects in this course, augmenting what is provided for free and what students may have purchased. You will need sufficient upgrades to contribute the above amount to each student.

If you add new students, currently you must re-open the quota panel and re-allocate quota so that newly added projects get additional upgrades; alternatively, you may open any project directly and edit its quotas in project settings.


format json bodys better

format json bodys better

--- cURLCodeGenerator.coffee
+++ cURLCodeGenerator.coffee
@@ -92,6 +92,14 @@
                 } for name, value of multipart_body)
             }

+        json_body = request.jsonBody
+        if json_body?
+            return {
+                "has_raw_body_with_tabs_or_new_lines": true
+                "has_raw_body_without_tabs_or_new_lines": false
+                "raw_body": addslashes_single_quotes(JSON.stringify(json_body, null, 2))
+            }
+
         raw_body = request.body
         if raw_body
             if raw_body.length < 5000


Removed another unnecessary preference set.

--- spec/fuzzy-finder-spec.coffee
+++ spec/fuzzy-finder-spec.coffee
@@ -404,9 +404,6 @@
           dispatchCommand('toggle-buffer-finder')

       describe "when the active pane has an item for the selected path", ->
-        beforeEach ->
-          atom.config.set("fuzzy-finder.searchAllPanes", false)
-
         it "switches to the item for the selected path", ->
           expectedPath = atom.project.getDirectories()[0].resolve('sample.txt')
           bufferView.confirmed({filePath: expectedPath})


activity: initial options of `ActivityLikeView` class

--- client/Social/Activity/views/likecount.coffee
+++ client/Social/Activity/views/likecount.coffee
@@ -1,4 +1,11 @@
 class ActivityLikeCount extends CustomLinkView
+
+  constructor: (options = {}, data) ->
+
+    options.cssClass = KD.utils.curry "like-count", options.cssClass
+
+    super options, data
+

   click: (event) ->


accountpopup: listen for paypal changes too

--- client/app/lib/avatararea/accountpopup.coffee
+++ client/app/lib/avatararea/accountpopup.coffee
@@ -67,7 +67,14 @@

     @updatePaymentTitle()

-    kd.singletons.paymentController.on 'UserPlanUpdated', @bound 'updatePaymentTitle'
+    { paymentController } = kd.singletons
+
+    paymentFinishedEvents = [
+      'UserPlanUpdated'
+      'PaypalRequestFinished'
+    ]
+
+    paymentController.on paymentFinishedEvents, @bound 'updatePaymentTitle'

   updatePaymentTitle: ->
@@ -133,4 +140,4 @@

     kd.utils.stopDOMEvent event
     kd.singletons.router.handleRoute "/Admin"
-    @hide()+    @hide()


Client: RemoteExtensions: override ::init to keep caches for instances on client side

--- client/app/lib/remote-extensions/index.coffee
+++ client/app/lib/remote-extensions/index.coffee
@@ -1,7 +1,25 @@
+globals = require 'globals'
+globals.__remoteCache = {}

-module.exports =
+module.exports = RemoteExtensions =

-  initialize   : (remote) ->
+  getInstance: (instanceId)     -> globals.__remoteCache[instanceId]
+
+  setInstance: (data, instance) -> globals.__remoteCache[data._id] = instance
+
+  initialize: (remote) ->
+
+    (Object.keys remote.api).forEach (model) ->
+      remote.api[model]::init = (data) ->
+        super
+
+        instance = RemoteExtensions.getInstance data._id
+
+        unless instance
+          RemoteExtensions.setInstance data, this
+        else
+          for own key of data when instance[key]?
+            instance[key] = data[key]

     remote.api.JComputeStack = require './computestack'
     remote.api.JMachine      = require './machine'


Client: remote-extensions: updateInstance: return if there is no data

--- client/app/lib/remote-extensions/index.coffee
+++ client/app/lib/remote-extensions/index.coffee
@@ -72,6 +72,8 @@

   updateInstance: (data) ->

+    return  unless data
+
     { id: instanceId, change, timestamp } = data

     return  if (instances = @getInstances instanceId).length is 0


This spec is also no longer relevant

This spec is also no longer relevant

--- spec/themes-panel-spec.coffee
+++ spec/themes-panel-spec.coffee
@@ -65,27 +65,3 @@
       runs ->
         expect(panel.uiMenu.val()).toBe 'atom-light-ui'
         expect(panel.syntaxMenu.val()).toBe 'atom-light-syntax'
-
-  describe "when a theme fails to install", ->
-    it "displays an error", ->
-      expect(panel.syntaxMenu.find('option[value=a-theme]').length).toBe 0
-
-      themeView = null
-      waitsFor ->
-        themeView = panel.find('.available-package-view').view()
-        themeView?
-
-      runs ->
-        spyOn(console, 'error')
-        spyOn(packageManager, 'runCommand').andCallFake (args, callback) ->
-          process.nextTick -> callback(-1, 'failed', 'failed')
-        themeView.installButton.click()
-        expect(themeView.installButton.prop('disabled')).toBe true
-
-      waitsFor ->
-        themeView.status.hasClass('icon-alert')
-
-      runs ->
-        expect(console.error).toHaveBeenCalled()
-        expect(themeView.installButton.prop('disabled')).toBe false
-        expect(panel.syntaxMenu.find('option[value=a-theme]').length).toBe 0


delete {

delete {

--- BackofficeBundle/Resources/public/coffee/table/TableviewCollectionView.coffee
+++ BackofficeBundle/Resources/public/coffee/table/TableviewCollectionView.coffee
@@ -45,18 +45,17 @@
     table = $('#tableviewCollectionTable').dataTable(
       searching: true
       ordering: true
-      ajax : {
+      ajax :
         url : @options.url
         dataSrc: (json) ->
           collectionName = json.collection_name
           return json[collectionName]
-      },
-      columnDefs: [{
+      columnDefs: [
         targets: -1,
         data: 'links',
         createdCell : (td, cellData, rowData, row, col) ->
           viewContext.renderColumnActions(viewContext, td, cellData, rowData, row, col)
-      }]
+      ]
       initComplete: (settings, json) ->
           viewContext.renderAddButton(viewContext, json.links)
       columns: columns


Update Atom packages.cson sync file

Update Atom packages.cson sync file

--- .atom/packages.cson
+++ .atom/packages.cson
@@ -7,8 +7,11 @@
   "atom-material-ui"
   "atom-pair"
   "atom-ternjs"
+  "autocomplete-go"
   "autocomplete-modules"
   "autocomplete-paths"
+  "ava"
+  "builder-go"
   "color-picker"
   "command-logger"
   "docblockr"
@@ -20,11 +23,19 @@
   "git-log"
   "git-time-machine"
   "glowing-cursor"
+  "go-config"
+  "go-debug"
+  "go-get"
   "go-plus"
+  "godoc"
+  "gofmt"
+  "gometalinter-linter"
+  "gorename"
   "highlight-selected"
   "hyperclick"
   "js-hyperclick"
   "language-asciidoc"
+  "language-docker"
   "language-powershell"
   "linter"
   "linter-bootlint"
@@ -47,8 +58,8 @@
   "minimap-selection"
   "minimap-split-diff"
   "monokai"
+  "navigator-go"
   "oceanic-next"
-  "oceanic-next-color-scheme"
   "package-sync"
   "pigments"
   "react"
@@ -59,6 +70,7 @@
   "stylefmt"
   "symbols-tree-view"
   "tabs-to-spaces"
+  "tester-go"
   "travis-ci-status"
   "visual-bell"
   "Zen"


renderif binding test improvements

renderif binding test improvements

--- tests/batman/view/renderif_binding_test.coffee
+++ tests/batman/view/renderif_binding_test.coffee
@@ -2,7 +2,7 @@

 QUnit.module "Batman.View: data-render-if bindings"

-asyncTest 'it should not render the inner nodes until the keypath is truthy', 4, ->
+asyncTest 'should not render inner nodes until keypath is truthy', 4, ->
   context = proceed: false

   source = '
unrendered
'
@@ -19,7 +19,18 @@

     QUnit.start()

-asyncTest 'it should render the inner nodes in the same context as the node was in when it deferred rendering', 2, ->
+asyncTest 'should render inner nodes and remove attribute if keypath is truthy', 2, ->
+  context = { proceed: true, deferred: 'inner value' }
+
+  source = '
unrendered
'
+
+  helpers.render source, context, (node, view) ->
+    equal $('span', node).html(), 'inner value'
+    equal $('.foo', node).attr('data-renderif'), undefined
+
+    QUnit.start()
+
+asyncTest 'should render inner nodes in same context as node was in when it deferred rendering', 2, ->
   context =
     proceed: false
     foo: Batman
@@ -33,7 +44,7 @@
     equal $('span', node).html(), 'bar'
     QUnit.start()

-asyncTest 'it should continue rendering on the node it stopped rendering', 2, ->
+asyncTest 'should continue rendering on node it stopped rendering', 2, ->
   context = proceed: false, foo: "bar"

   source = '
unrendered
'
@@ -44,7 +55,7 @@
     equal node.html(), 'bar'
     QUnit.start()

-asyncTest 'it should only render the inner nodes once', 3, ->
+asyncTest 'should only render inner nodes once', 3, ->
   context = proceed: false, deferred: spy = createSpy().whichReturns('inner value')

   source = '
unrendered
'


:lipstick:

:lipstick:

--- grammars/jsoniq.cson
+++ grammars/jsoniq.cson
@@ -92,12 +92,12 @@
         'name': 'constant.numeric.jsoniq'
     }
     {
-        'match': '(?<!\\.)\\b(NaN|after|allowing|ancestor|ancestor-or-self|and|append|array|as|ascending|at|attribute|base-uri|before|boundary-space|break|by|case|cast|castable|catch|child|collation|comment|constraint|construction|contains|context|continue|copy|copy-namespaces|count|decimal-format|decimal-separator|declare|default|delete|descendant|descendant-or-self|descending|digit|div|document|document-node|element|else|empty|empty-sequence|encoding|end|eq|every|except|exit|external|false|first|following|following-sibling|for|from|ft-option|function|ge|greatest|group|grouping-separator|gt|idiv|if|import|in|index|infinity|insert|instance|integrity|intersect|into|is|item|json|json-item|jsoniq|last|lax|le|least|let|loop|lt|minus-sign|mod|modify|module|namespace|namespace-node|ne|next|node|nodes|not|null|object|of|only|option|or|order|ordered|ordering|paragraphs|parent|pattern-separator|per-mille|percent|preceding|preceding-sibling|previous|processing-instruction|rename|replace|return|returning|revalidation|satisfies|schema|schema-attribute|schema-element|score|select|self|sentences|sliding|some|stable|start|strict|switch|text|then|times|to|treat|true|try|tumbling|type|typeswitch|union|unordered|updating|validate|value|variable|version|when|where|while|window|with|words|xquery|zero-digit)(?!(:|\\-))\\b'
+        'match': '(?<!\\.)\\b(NaN|after|allowing|ancestor|ancestor-or-self|and|append|array|as|ascending|at|attribute|base-uri|before|boundary-space|break|by|case|cast|castable|catch|child|collation|comment|constraint|construction|contains|context|continue|copy|copy-namespaces|count|decimal-format|decimal-separator|declare|default|delete|descendant|descendant-or-self|descending|digit|div|document|document-node|element|else|empty|empty-sequence|encoding|end|eq|every|except|exit|external|false|first|following|following-sibling|for|from|ft-option|function|ge|greatest|group|grouping-separator|gt|idiv|if|import|in|index|infinity|insert|instance|integrity|intersect|into|is|item|json|json-item|jsoniq|last|lax|le|least|let|loop|lt|minus-sign|mod|modify|module|namespace|namespace-node|ne|next|node|nodes|not|null|object|of|only|option|or|order|ordered|ordering|paragraphs|parent|pattern-separator|per-mille|percent|preceding|preceding-sibling|previous|processing-instruction|rename|replace|return|returning|revalidation|satisfies|schema|schema-attribute|schema-element|score|select|self|sentences|sliding|some|stable|start|strict|switch|text|then|times|to|treat|true|try|tumbling|type|typeswitch|union|unordered|updating|validate|value|variable|version|when|where|while|window|with|words|xquery|zero-digit)(?!(:|\\-|\\s*\\())\\b'
         'name': 'keyword.jsoniq'
     }
     {
         'comment': 'EQName'
-        'match': '([\\-_a-zA-Z0-9][\\-\\._a-zA-Z0-9]*:)?([\\-_a-zA-Z0-9][\\-_a-zA-Z0-9]*)(?=\s*\\()'
+        'match': '([\\-_a-zA-Z0-9][\\-\\._a-zA-Z0-9]*:)?([\\-_a-zA-Z0-9][\\-_a-zA-Z0-9]*)(?=\\s*\\()'
         'name': 'support.function.jsoniq'
     }
     {


fix parsing inside lists

fix parsing inside lists

--- grammars/schelp.cson
+++ grammars/schelp.cson
@@ -161,7 +161,7 @@
             'include': '#modal_tags'
         }
         {
-            'include': 'lists_tables'
+            'include': '#lists_tables'
         }
     ]
   }
@@ -180,6 +180,10 @@
         {
             'include': '#modal_tags'
         }
+        {
+            'include': '#lists_tables'
+        }
+
     ]
   }

@@ -252,11 +256,11 @@
                 'name': 'entity.name.tag.schelp'
         'patterns': [
             {
-                'match': '##|\\|\\|.*$'
+                'include': '#modal_tags'
+            }
+            {
+                'match': '(##|\\|\\|)'
                 'name': 'markup.list.unnumbered'
-            }
-            {
-                'include': '#modal_tags'
             }
         ]
       }
@@ -272,7 +276,7 @@
                 'name': 'entity.name.tag.schelp'
         'patterns': [
             {
-                'match': '##.*$'
+                'match': '##'
                 'name': 'markup.list.unnumbered'
             }
             {
@@ -292,7 +296,7 @@
                 'name': 'entity.name.tag.schelp'
         'patterns': [
             {
-                'match': '##.*$'
+                'match': '##'
                 'name': 'markup.list.numbered'
             }
             {
@@ -312,7 +316,7 @@
                 'name': 'entity.name.tag.schelp'
         'patterns': [
             {
-                'match': '##.*$'
+                'match': '##'
                 'name': 'markup.list.unnumbered'
             }
             {


#577 - hide unselectable questions from list

--- dkobo/koboform/static/js/xlform/XLF.SkipLogic.helpers.coffee
+++ dkobo/koboform/static/js/xlform/XLF.SkipLogic.helpers.coffee
@@ -134,7 +134,7 @@
     @view_factory.create_operator_picker operators

   build_question_view: () ->
-    @view_factory.create_question_picker @questions
+    @view_factory.create_question_picker @questions()

   build_response_view: (question, question_type, operator_type) ->
     responses = null
@@ -178,14 +178,20 @@

     new XLF.SkipLogicPresenter(criterion_model, criterion_view, @)

+  questions: () ->
+    questions = []
+    limit = false
+
+    non_selectable = ['datetime', 'time', 'note', 'calculate']
+
+    @survey.forEachRow (question) =>
+      limit = limit || question is @current_question
+      if !limit && question.getType() not in non_selectable
+        questions.push question
+
+    questions
+
   constructor: (@model_factory, @view_factory, @survey, @current_question) ->
-    @questions = []
-    limit = false
-
-    @survey.forEachRow (question) =>
-      limit = limit || question is current_question
-      if !limit
-        @questions.push question

 question_types =


Don't prep collections on login

Don't prep collections on login

--- www/js/app.coffee
+++ www/js/app.coffee
@@ -13,6 +13,10 @@
   # models in the app (including using Human Models for Backbone) in the future.
   prepCollections = ->
     d = $.Deferred()
+
+    unless window.GLOBALS.TOKEN
+      d.resolve()
+      return d.promise()

     # I hate myself and want to die.
     Checkins.fetch


inject Modernizr into rootScope

inject Modernizr into rootScope

--- www/js/app.coffee
+++ www/js/app.coffee
@@ -132,6 +132,7 @@
 	.run ($rootScope) ->
 		document.addEventListener "deviceready", ->
 			$rootScope.device = device
+			$rootScope.Modernizr = Modernizr

 	.run ($rootScope, $state, $stateParams) ->
 		_.extend $rootScope,


fix list of theaters

fix list of theaters

--- src/theater/states/list/controller.coffee
+++ src/theater/states/list/controller.coffee
@@ -1,6 +1,8 @@
 angular.module 'Cinesponsable.theater'
-.controller 'TheaterListCtrl', ($scope, $state, theaters) ->
-  $scope.theaters = theaters
+.controller 'TheaterListCtrl', ($scope, $state, Theater) ->
+  Theater.query().$promise.then (theaters) ->
+    $scope.theaters = theaters

   $scope.showtime = (theater) ->
-    $state.go 'base.showtime', {theaterId: theater.code}
+    console.log theater
+    $state.go 'base.showtimeByTheater', {theaterId: theater.id}


change 'lowerCaseColumns' to 'camelCaseColumns'

--- src/token/colmetadata-token-parser.coffee
+++ src/token/colmetadata-token-parser.coffee
@@ -21,7 +21,7 @@

     colName = buffer.readBVarchar()

-    if options.lowerCaseColumns
+    if options.camelCaseColumns
       colName = colName.replace /^[A-Z]/, (s) -> s.toLowerCase()


reset button working

reset button working

--- platform/src/app.coffee
+++ platform/src/app.coffee
@@ -3,6 +3,7 @@
 globals = @

 CODE_CHANGE_TIMEOUT = 1000
+MODEL_FORMATTING_INDENTATION = 2

 MessageType = GE.makeConstantSet("Error", "Info")

@@ -30,7 +31,7 @@

 spinner = new Spinner(SPINNER_OPTS)

-lastModel = new GE.Model()
+currentModel = new GE.Model()
 currentFrame = 0
 currentModelData = null
 currentAssets = null
@@ -122,6 +123,16 @@
         icons:
           primary: "ui-icon-play"

+  $("#resetButton").on "click", ->
+    currentFrame = 0
+    currentModel = currentModel.atVersion(0)
+
+    editors.modelEditor.setValue(JSON.stringify(currentModel.data, null, MODEL_FORMATTING_INDENTATION))
+    # The new contect will be selected by default
+    editors.modelEditor.selection.clearSelection()
+
+
+
 setupEditor = (id) ->
   editor = ace.edit(id)
   editor.getSession().setMode("ace/mode/javascript")
@@ -150,6 +161,8 @@
   catch error
     return showMessage(MessageType.Error, "Model error. #{error}")

+  currentModel.atVersion(currentFrame).data = currentModelData
+
   try
     currentActions = eval(editors.actionsEditor.getValue())
   catch error
@@ -171,7 +184,6 @@

 # returns a new model
 executeCode = ->
-  currentModel = new GE.Model(currentModelData, lastModel)
   [result, patches] = GE.runStep(currentModel, currentLoadedAssets, currentActions, currentLayout)
   return currentModel.applyPatches(patches)

@@ -196,9 +208,10 @@
 handleAnimation = ->
   if not isPlaying then return false

-  lastModel = executeCode()
-
-  editors.modelEditor.setValue(JSON.stringify(lastModel.data, null, 4))
+  currentModel = executeCode()
+  currentFrame++
+
+  editors.modelEditor.setValue(JSON.stringify(currentModel.data, null, MODEL_FORMATTING_INDENTATION))
   # The new contect will be selected by default
   editors.modelEditor.selection.clearSelection()


show download errors, specify encoding when downloading

--- metal/install_manager.coffee
+++ metal/install_manager.coffee
@@ -166,6 +166,7 @@

     console.log "Downloading with headers #{JSON.stringify headers}, flags = #{flags}"
     r = progress request.get({
+      encoding: null # binary (otherwise defaults to utf-8)
       url: @url
       headers
     }), throttle: 25
@@ -173,6 +174,9 @@
       console.log "Got status code: #{response.statusCode}"
       content_length = response.headers['content-length']
       console.log "Downloading #{Humanize.fileSize content_length} for #{@game.title}"
+
+    r.on 'error', (err) =>
+      console.log "Download error: #{JSON.stringify err}"

     r.on 'progress', (state) =>
       @progress = 0.01 * state.percent


bitcoin use coinbase exchange api

bitcoin use coinbase exchange api

--- plugin/bitcoin/bitcoin.coffee
+++ plugin/bitcoin/bitcoin.coffee
@@ -24,12 +24,12 @@
     key: 'bitcoin.getExchangeRate'
     currency: currency
   , (SETEX) ->
-    request 'https://blockchain.info/ticker', (err, res, body) ->
+    request 'https://api.coinbase.com/v1/currencies/exchange_rates', (err, res, body) ->
       logger.error if err

       body = JSON.parse body
-      rate = 1 / parseFloat(body[currency]['15m'])
+      rate = parseFloat body["#{currency.toLowerCase()}_to_btc"]

-      SETEX rate, 60
+      SETEX rate, 600

   , callback


profile: wip slug

profile: wip slug

--- workers/social/lib/social/models/account/render-homepage.coffee
+++ workers/social/lib/social/models/account/render-homepage.coffee
@@ -125,8 +125,8 @@
   posts = ""
   for blog,i in blogPosts

-    console.log blog.slug
-    href = ("/#{blog.slug}" if 'string' is typeof slug) or "/#{blog.slug.group}/#{blog.slug.slug}" or '/#'
+    slug = blog.slug
+    href = ("/#{slug}" if 'string' is typeof slug) or "/#{slug.group}/#{slug.slug}" or '/#'

     if blog.tags?.length
       tags = ""


KodingProvider: add Plan details/limits for feature reference.

--- workers/social/lib/social/models/computeproviders/koding.coffee
+++ workers/social/lib/social/models/computeproviders/koding.coffee
@@ -6,6 +6,33 @@
 KONFIG            = require('koding-config-manager').load("main.#{argv.c}")

 module.exports = class Koding extends ProviderInterface
+
+  PLANS                =
+    free               :
+      total            : 1
+      alwaysOn         : 0
+      storage          : 3
+      allowedInstances : ['t2.micro']
+    hobbyist           :
+      total            : 1
+      alwaysOn         : 1
+      storage          : 10
+      allowedInstances : ['t2.micro']
+    developer          :
+      total            : 3
+      alwaysOn         : 1
+      storage          : 25
+      allowedInstances : ['t2.micro']
+    professional       :
+      total            : 5
+      alwaysOn         : 2
+      storage          : 50
+      allowedInstances : ['t2.micro']
+    super              :
+      total            : 10
+      alwaysOn         : 5
+      storage          : 100
+      allowedInstances : ['t2.micro']

   @ping = (client, options, callback)->


ProviderKoding: now supports create, remove and fetch on top of JVM

--- workers/social/lib/social/models/computeproviders/koding.coffee
+++ workers/social/lib/social/models/computeproviders/koding.coffee
@@ -1,4 +1,7 @@
+
 ProviderInterface = require './providerinterface'
+KodingError       = require '../../error'
+JVM               = require '../vm'

 module.exports = class Koding extends ProviderInterface

@@ -9,8 +12,25 @@

   @fetchExisting = (client, options, callback)->

-    JVM = require '../vm'
     JVM.fetchVmsByContext client, options, callback
+
+
+  @create = (client, options, callback)->
+
+    {nonce, stackId} = options
+    JVM.createVmByNonce client, nonce, stackId, callback
+
+
+  @remove = (client, options, callback)->
+
+    {hostnameAlias} = options
+    JVM.removeByHostname client, hostnameAlias, callback
+
+
+  @update = (client, options, callback)->
+
+    callback new KodingError \
+      "Update not supported for Koding VMs", "NotSupported"

   @fetchAvailable = (client, options, callback)->


async

async

--- chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
+++ chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
@@ -442,7 +442,7 @@
   for own name, endpoint of @endpoints
     url = "/better-bibtex/#{name}"
     ep = Zotero.Server.Endpoints[url] = ->
-    ep.prototype = endpoint
+    ep:: = endpoint

   @loadTranslators()
   @extensionConflicts()
@@ -452,7 +452,7 @@
     return ->
       header = @header.split(/\r?\n/)

-      req = header[0]?.exec(/^([A-Z]+) (.*)/)
+      req = header[0]?.match(/^([A-Z]+) (.+)/)

       return original.apply(@, arguments) unless req?[1]?.indexOf('/better-bibtex/') == 0


Mount machine owner's home directory

Mount machine owner's home directory

--- client/finder/lib/filetree/controllers/nfindercontroller.coffee
+++ client/finder/lib/filetree/controllers/nfindercontroller.coffee
@@ -120,7 +120,11 @@

     { uid } = machine
     mRoots  = (@appStorage.getValue 'machineRoots') or {}
-    path    = options.mountPath or mRoots[uid] or "/home/#{nick()}"
+    path    = options.mountPath or mRoots[uid]
+
+    path ?= if owner = machine.getOwner()
+    then "/home/#{owner}"
+    else '/'

     if @getMachineNode uid
       return kd.warn "Machine #{machine.getName()} is already mounted!"


Make delegateEvents override take optional events hash

--- src/chaplin/views/view.coffee
+++ src/chaplin/views/view.coffee
@@ -158,8 +158,11 @@

   # Override Backbones method to combine the events
   # of the parent view if it exists.
-  delegateEvents: ->
+  delegateEvents: (events) ->
     @undelegateEvents()
+    if events
+      @_delegateEvents events
+      return
     for events in utils.getAllPropertyVersions this, 'events'
       @_delegateEvents events
     return


content

content

--- project/coffee/AppView.coffee
+++ project/coffee/AppView.coffee
@@ -81,7 +81,7 @@
         @onResize = _.debounce @onResize, 300
         @$window.on 'resize orientationchange', @onResize

-        # @$window.on 'click', @showModalTest
+        @$window.on 'click', @showModalTest

         return


Workspace: Handle workspace missing case

--- client/ide/routes.coffee
+++ client/ide/routes.coffee
@@ -218,4 +218,6 @@

         params.username = KD.nick()

-        loadWorkspace params, workspace
+        if workspace
+        then loadWorkspace params, workspace
+        else routeToLatestWorkspace()


Routes: Validate shared workspace immediately

--- client/ide/routes.coffee
+++ client/ide/routes.coffee
@@ -185,23 +185,21 @@

       try

-        return routeToLatestWorkspace()  unless KD.userWorkspaces.length
-
-        KD.userWorkspaces.forEach (workspace, index) =>
-
-          return  if workspace.channelId isnt channel.id
-
-          machine = (KD.userMachines.filter (m) -> m.uid is workspace.machineUId)[0]
-          query   = socialApiId: channel.creatorId
-
-          KD.remote.api.JAccount.some query, {}, (err, account) =>
-
-            return throw new Error err  if err
-
-            username  = account.first.profile.nickname
-            channelId = channel.id
-
-            return loadIDE { machine, workspace, username, channelId }
+        [workspace] = KD.userWorkspaces.filter (w) -> w.channelId is channel.id
+
+        return routeToLatestWorkspace()  unless workspace
+
+        [machine] = KD.userMachines.filter (m) -> m.uid is workspace.machineUId
+
+        query = socialApiId: channel.creatorId
+        KD.remote.api.JAccount.some query, {}, (err, account) =>
+
+          return throw new Error err  if err
+
+          username  = account.first.profile.nickname
+          channelId = channel.id
+
+          return loadIDE { machine, workspace, username, channelId }

       catch e


Rework loader to load css with Firefox

Rework loader to load css with Firefox

--- client/lib/loader.coffee
+++ client/lib/loader.coffee
@@ -1,3 +1,7 @@
+# inspiration from https://github.com/ramitos/lazyload/blob/master/src/lazyload.js
+#
+GECKO = /Gecko\//.test(navigator.userAgent)
+
 class Loader

     constructor: (urls,cb)->
@@ -36,10 +40,12 @@
         )
         return fn(url, true)  if exists
         called = false
-        node = _.el("link",
-          type: "text/css"
-          href: url
-          rel: "stylesheet"
+        tag = "link" #if GECKO then "style" else "link"
+        node = _.el( tag,
+           type: "text/css"
+           href: url
+           media: "screen"
+           rel: "stylesheet"
         )

         onloaded=(success,err)->
@@ -48,15 +54,14 @@
             fn(url, success, err)
         node.onload  = ()->    onloaded(true,false)
         node.onerror = (err)-> onloaded(false,err)
-
-
         node.onreadystatechange = ->
             return if not (/loaded|complete/.test(node.readyState)) or called
             called=true
             fn(url, true)
+        @head.appendChild node
+        # GECKO cannot query the node immediatly after it's created
+        if GECKO then _.delay(poll,250) else poll()

-        @head.appendChild node
-        poll()

     loadJS: (url, fn) ->
@@ -81,7 +86,7 @@
         this.body.appendChild(node)

-Lanes.lib.Loader = (urls...)->
+Lanes.lib.Request = (urls...)->
     urls = urls[0] if urls.length == 1 && _.isArray(urls[0])
     new _.Promise(  (resolve,reject)->
         new Loader(urls, (completed)->


refactor from $ appends to heredocs

refactor from $ appends to heredocs

--- client/lib/plugin.coffee
+++ client/lib/plugin.coffee
@@ -58,9 +58,15 @@
     emit: (div, item) ->
       div.append ul = $('
').append if localStorage.length then $('<input type="button" value="discard all" />').css('margin-top','10px') else $('
empty
')
       for i in [0...localStorage.length]
-        key = localStorage.key(i)
-        a = $('').append(JSON.parse(localStorage[key]).title).data('pageName', key)
-        ul.prepend($('
').append(a))
+        slug = localStorage.key(i)
+        wikiPage = JSON.parse(localStorage[slug])
+        ul.prepend """
+

+
+              #{wikiPage.title} <button>X</button>
+
+

+        """
     bind: (div, item) ->
       div.find('input').click ->
         localStorage.clear()


Proper error handling in spec.

Proper error handling in spec.

--- test/integration/price-import.spec.coffee
+++ test/integration/price-import.spec.coffee
@@ -116,4 +116,6 @@
         expect(product.variants[1].prices[0].value.centAmount).toBe 9
         expect(product.variants[1].prices[0].value.currencyCode).toBe 'GBP'
         done()
+      .catch (err) ->
+        done(_.prettify err)
   , 30000


функция

функция

--- js/main.coffee
+++ js/main.coffee
@@ -1,7 +1,4 @@
 do ->
-  qwer=if asd>2
-    asdd()
-  else
-    qwe()
+  func = (x,y) -> ( x + y )

-
+  console.log(func 2, 3)


re-added option for special indent inside methods

--- client/prefs-code.coffee
+++ client/prefs-code.coffee
@@ -8,7 +8,7 @@
 @init = ($e) ->
   $e.html '''

<input id=code-ai   type=checkbox>Auto-indent <input id=code-sw  size=1> spaces
-
+
<input id=code-aim  type=checkbox>in methods: <input id=code-swm size=1> spaces

<input id=code-mb   type=checkbox>Highlight matching brackets: <tt>()[]{}</tt>

<input id=code-acbr type=checkbox>Auto-close brackets

<input id=code-acbl type=checkbox>Auto-close blocks: <tt>:If :For ...</tt>


use hipchat for @dat

use hipchat for @dat

--- scripts/name-expander.coffee
+++ scripts/name-expander.coffee
@@ -2,37 +2,29 @@
 #
 # merlin: <your message>

-josiah = "Jkiehl"
-cliff = "cdickerson"
-ivey = "ivey"
-chong = "Chong Yan"
-jesse = "Jesse Howarth"
-kyle = "kallan"
-andrew = "Andrew Garson"
+josiah = "@jk"
+cliff = "@cliff"
+ivey = "@ivey"
+chong = "@ChongYan"
+kyle = "@kallan"
 trotter = "Trotter Cashion"
 afeng = "Aaron Feng"
-bennett = "bsprugel"
 akwan = "Alan Kwan"
 marksoho = "Mark Soho"
 nmathrani = "Neeraj Mathrani"
+steve = "@srude"
+fei = "@fei"

 noc_members = ["Carter1", "Cames1", "jdowns", "jdowns1", "jtramullas", "nstuhring", "ctrytten1", "ctrytten", "akim", "nsanches", "jkim", "ebednik", "nhooper"]

-dert_mermbers = ["CDERCKERSERN", "ERVER", "JKERL", "CHIN YERN", "KERLERN", "ERNDRERW GERSERN"]
-
 build_members = ["mvuong", "mstewart", "jromigh", "zeffron", "ilinkletter", "eyap"]

-merlin_remote_members = [
-  cliff,
+dat_members = [
   ivey,
-]
-
-merlin_lax_members = [
   josiah,
-  chong,
+  steve,
   kyle,
-  andrew,
-  bennett
+  fei
 ]

 chef_repo_mergers = [
@@ -45,8 +37,6 @@
   trotter,
   afeng
 ]
-
-merlin_members = merlin_remote_members.concat merlin_lax_members

 devinf_members = [
   cliff,
@@ -68,17 +58,8 @@

 module.exports = (robot) ->
-  robot.hear /^(merlern|dert)( team| dudes| guys)*:(.+)/i, (msg) ->
-    msg.send dert_mermbers.join(", ") + ": ^^"
-
-  robot.hear /^(merlin|dat)( team| dudes| guys)*:(.+)/i, (msg) ->
-    msg.send merlin_members.join(", ") + ": ^^"
-
-  robot.hear /^(merlin|dat)-lax( team| dudes| guys)*:(.+)/i, (msg) ->
-    msg.send merlin_lax_members.join(", ") + ": ^^"
-
-  robot.hear /^(merlin|dat)-remote( team| dudes| guys)*:(.+)/i, (msg) ->
-    msg.send merlin_remote_members.join(", ") + ": ^^"
+  robot.hear /^@dat:? (.+)/i, (msg) ->
+    msg.send dat_members.join(", ") + ": ^^"

   robot.hear /^(mashion)( team| dudes| guys)*:(.+)/i, (msg) ->
     msg.send mashion_devs.join(", ") + ": ^^"


Disable selectWorkspace tests for now since we've disabled them in team context cc/ @fatihacet

--- client/ide/test/routes.test.coffee
+++ client/ide/test/routes.test.coffee
@@ -165,7 +165,7 @@
       expect(call).toBe no  for call in calls

-    it 'should call activitySidebar.selectWorkspace', ->
+    it.skip 'should call activitySidebar.selectWorkspace', ->

       data                     = { machine: mockMachine, workspace: mockWorkspace }
       { activitySidebar }      = kd.singletons.mainView


Test/Team fix coffeescript error

Test/Team fix coffeescript error

--- client/test/lib/teams/teams.coffee
+++ client/test/lib/teams/teams.coffee
@@ -61,6 +61,7 @@
       browser.url url
       browser.waitForElementVisible '.content-page.Team section a.previous-team', 20000

+
   checkAllLinkInTheFooter: (browser) ->
     url = helpers.getUrl(yes)
     browser.maximizeWindow()


Test/Myteam fix error in seePreviouslyVisitedTeams

--- client/test/lib/teams/teams.coffee
+++ client/test/lib/teams/teams.coffee
@@ -9,7 +9,7 @@

 module.exports =

-  # Team Creation
+  # # Team Creation
   createTeamWithInvalidEmail: (browser) ->
     teamsHelpers.createTeam(browser, user, '', 'InvalidEmail')

@@ -56,10 +56,10 @@

   seePreviouslyVisitedTeams: (browser) ->
     url = helpers.getUrl()
-    teamsHelpers.loginTeam(browser, user, no)
-    teamsHelpers.logoutTeamfromUrl browser, (result) ->
+    teamsHelpers.loginTeam browser, user, no, '', ->
+      teamsHelpers.logoutTeamfromUrl browser, (result) ->
       browser.url url
-      browser.waitForElementVisible '.content-page.Team section a.previous-team', 20000
+      browser.waitForElementVisible '.content-page.Team section a.previous-team', 40000

   checkAllLinkInTheFooter: (browser) ->


add uid field to box schema

add uid field to box schema

--- server/code/model/box.coffee
+++ server/code/model/box.coffee
@@ -16,6 +16,9 @@
     index: unique: true
   server: String
   boxJSON: Schema.Types.Mixed
+  uid:
+    type: Number
+    index: unique: true

 zDbBox = mongoose.model 'Box', boxSchema


wrap the original model method so it can modify api request

--- shared/src/api/action-adapter.coffee
+++ shared/src/api/action-adapter.coffee
@@ -8,6 +8,7 @@
 reject    = require 'lodash/reject'
 pick      = require 'lodash/pick'
 merge     = require 'lodash/merge'
+wrap      = require 'lodash/wrap'
 invert    = require 'lodash/invert'
 partition = require 'lodash/partition'
 defaults  = require 'lodash/defaults'
@@ -112,14 +113,17 @@
   actionOptions = merge(method: METHODS[action], ACTIONS[action] or {})
   connectHandler(apiHandler, Actions, actionOptions, allOptions...)

+emptyFn = (config) -> {}
+
 connectModelAction = (action, apiHandler, klass, method, options) ->
   actionOptions = defaults(options, { method: METHODS[action] })
   handlers = makeRequestHandlers(klass, options)
-  handler = (reqOptions) ->
+  handler = (originalMethod, reqOptions) ->
     requestConfig = pick(options, 'url', 'method', 'data', 'params', 'handledErrors')
     requestConfig.url ?= interpolate(options.pattern, defaults({}, reqOptions, this))
+    merge(requestConfig, originalMethod(reqOptions, requestConfig))
     apiHandler.send(requestConfig, options, reqOptions)
-  klass.prototype[method] = handler
+  klass.prototype[method] = wrap(klass.prototype[method] or emptyFn, handler)

 adaptHandler = (apiHandler) ->