Some improvements

Some improvements

--- src/tunnel.coffee
+++ src/tunnel.coffee
@@ -9,6 +9,8 @@
         port: 8080
         host: '0.0.0.0'
         timeout: 2000
+        protocol: 'ciph:'
+        tunnel_name: 'tunnel'

     connections: {}

@@ -33,9 +35,9 @@
     message: (cn, data)->
         mes = null
         try mes = url.parse data, true
-        return if not mes or mes.protocol isnt 'ciph:'
+        return if not mes or mes.protocol isnt @options.protocol

-        if mes.host is 'tunnel'
+        if mes.host is @options.tunnel_name
             switch mes.pathname
                 when '/connect'
                     @connect cn, mes.query unless cn._tunnelled
@@ -43,7 +45,7 @@
                 when '/rename'
                     @rename cn, mes.query if cn._tunnelled

-        else
+        else if cn._tunnelled and mes.auth
             @tunnelMessage cn, mes, data

     connect: (cn, q)->
@@ -59,7 +61,7 @@
         cn._tunnelled = clientId

         @disconnect cn, clientId
-        @response cn, null, 'tunnel', '/connected', client: clientId, exists: exists
+        @response cn, null, @options.tunnel_name, '/connected', client: clientId, exists: exists

     rename: (cn, q)->
         # Do nothing if no client parameter, or already exists or equal with current
@@ -71,17 +73,18 @@
         cn._tunnelled = clientId

         @disconnect cn, clientId
-        @response cn, null, 'tunnel', '/connected', client: clientId, exists: false
+        @response cn, null, @options.tunnel_name, '/connected', client: clientId, exists: false

     tunnelMessage: (cn, mes, data)->
         return if not mes.host or not @connections[mes.host]
+        return if mes.auth isnt cn._tunnelled
         @connections[mes.host].send data

     response: (cn, c, h, m, q)->
         return unless h

         res = url.format
-            protocol: 'ciph:'
+            protocol: @options.protocol
             slashes: true
             auth: c or null
             host: h
@@ -91,7 +94,7 @@
         cn?.send res

     getUID: (prefix='')->
-        hash = crypto.createHash('md5')
+        hash = crypto.createHash('sha1')
         hash.update _.uniqueId prefix + new Date().getTime()
         return hash.digest('hex').slice(0, 8)


Ask for forgiveness rather than permission

--- src/unlink.coffee
+++ src/unlink.coffee
@@ -39,7 +39,7 @@
   unlinkPath: (pathToUnlink) ->
     try
       process.stdout.write "Unlinking #{pathToUnlink} "
-      fs.unlinkSync(pathToUnlink) if fs.isSymbolicLinkSync(pathToUnlink)
+      fs.unlinkSync(pathToUnlink)
       @logSuccess()
     catch error
       @logFailure()


Null-guard hook calls when supporting legacy attach hooks

--- src/space-pen.coffee
+++ src/space-pen.coffee
@@ -137,8 +137,8 @@

     if @useLegacyAttachHooks
       @element.setAttribute('callAttachHooks', true)
-      @afterAttach = (onDOM) -> @attached() if onDOM
-      @beforeRemove = -> @detached()
+      @afterAttach = (onDOM) -> @attached?() if onDOM
+      @beforeRemove = -> @detached?()
     else if useCustomElementAttachHooks
       @element.attached = => @attached?()
       @element.detached = => @detached?()


payment: listen "change" event of coupon option radio button group view

--- client/Main/payment/planupgradeconfirmform.coffee
+++ client/Main/payment/planupgradeconfirmform.coffee
@@ -53,17 +53,18 @@
     giftWrapper.addSubView new KDLabelView
       title: "Select your gift"
       cssClass: "select-gift"
+
     giftWrapper.addSubView couponOptions = new KDInputRadioGroup
       name       : "coupon-options"
       radios     : [
         title    : "#{@coupons.discount.name}"
         value    : "discount"
-        callback : => @changeCouponOption "discount"
       ,
         title    : "#{@coupons.vm.name}"
         value    : "vm"
-        callback : => @changeCouponOption "vm"
       ]
+
+    couponOptions.on "change", @bound "changeCouponOption"

     @plan.addSubView totalWrapper = new KDCustomHTMLView cssClass: "total-wrapper"


client: fixed multiple delete modal issue

--- client/account/lib/views/deleteaccountview.coffee
+++ client/account/lib/views/deleteaccountview.coffee
@@ -29,7 +29,10 @@
               return
           @toggleClass 'escape'
           times++
-      callback   : -> new DeleteModalView
+      callback   : ->
+        @disable()
+        deleteModalView = new DeleteModalView
+        deleteModalView.on 'KDModalViewDestroyed', @bound 'enable'

   pistachio:->
     """


Collapse highlight meta menu after deletion. Fixes #390

--- client/lib/annotator/canvas-text-highlight.coffee
+++ client/lib/annotator/canvas-text-highlight.coffee
@@ -124,6 +124,9 @@
     highlightsControl = Meteor.render =>
       highlight = Highlight.documents.findOne @annotation?._id
       Template.highlightsControl highlight if highlight
+
+    # Workaround for https://github.com/peerlibrary/peerlibrary/issues/390
+    $control.wrap('<div/>').unwrap()

     $control.find('.meta-content').empty().append(highlightsControl)
     $control.show()


Updated tag input. Syntax cleanup.

Updated tag input. Syntax cleanup.

--- src/tag_input.coffee
+++ src/tag_input.coffee
@@ -55,25 +55,24 @@
           get: ->
             getTagsList $scope.$parent

-        $scope.$on "updateTagInput", ->
+        updateTagInput = ->
           chosenElement.trigger "liszt:updated"
+
+        $scope.$on "update-tag-input", -> updateTagInput()

         # Update tag input after adding new option DOM element
         setTimeout (->
-          chosenElement.trigger "liszt:updated"
+          updateTagInput()
         ), 0

         chosenElement.change (event, object)->
           $scope.$apply ->
-            list = $scope.selected
             if object.selected?
-              list.push object.selected
+              $scope.selected.push object.selected
             else if object.deselected?
               # Find the element that need to be removed
               index = list.indexOf object.deselected
               return if index is -1

-              list[index..index] = []
-              $scope.selected    = list
-
+              $scope.selected[index..index] = []
 ]


Fix: Tableau Desktop 9.1Beta2 bug of failing when the lastRecordToken contains quote characters.

Thanks to Samm Desmond at Tableau Software for finding it.

--- coffee/twitter_connector/twitter_connector.coffee
+++ coffee/twitter_connector/twitter_connector.coffee
@@ -100,14 +100,13 @@
       wdc_base.set_connection_data( data )
       tableau.submit()

-  rows: (connection_data, lastRecordToken)->
+  rows: (connection_data, lastRecordTokenBase64)->
+    # De-base64 the last record token to get around a Tableau Desktop issue
+    # with quotes in the lastRecordToken
+    lastRecordToken = atob(lastRecordTokenBase64)
+
     tableau.log "Starting to fetch a batch -- lastRecordToken:'#{lastRecordToken}'"
     tableau.abortWithError("No search terms provided") unless connection_data.q
-
-    ## The pagination data
-    #if lastRecordToken != ""
-      #tableau.abortWithError("Error while parsing pagination token:#{JSON.stringify lastRecordToken}")
-      #return

     pagination = {
       url: "/search?q=#{encodeURIComponent(connection_data.q)}"
@@ -136,8 +135,11 @@

         have_more = (next_page.pages > 0)

-        #tableau.abortWithError("stringify: #{JSON.stringify(next_page)} have_more:#{ have_more}")
-        tableau.dataCallback(tableau_data, JSON.stringify(next_page), have_more )
+        # Re-encode the new lastRecordToken with base64 to get around
+        # the Tableau Desktop lastRecordToken issue.
+        reencodedLastRecordToken = btoa(JSON.stringify(next_page))
+
+        tableau.dataCallback(tableau_data, reencodedLastRecordToken, have_more )

       error: (xhr, ajaxOptions, thrownError)->
         console.error("Error during search request", thrownError)


Scrolling to revealed active file in the treeview

--- src/tree-view.coffee
+++ src/tree-view.coffee
@@ -163,6 +163,7 @@
         entry.expand()
       else
         @selectEntry(entry)
+        @scrollToEntry(entry)

   entryForPath: (path) ->
     fn = (bestMatchEntry, element) ->


Multi-level composite key parsing WORKS!

--- test/jolokiasrv.coffee
+++ test/jolokiasrv.coffee
@@ -285,6 +285,7 @@
         k = resp['java.lang:name=ConcurrentMarkSweep,type=GarbageCollector']
         Object.keys(k).should.include 'CollectionCount'
         k['CollectionCount'].value.should.equal 46060
+        Object.keys(k['CollectionCount'].graph).should.have.length 4
         srv.close()
         done()

@@ -355,102 +356,111 @@
     srv = http.createServer(app)
     srv.listen(47432, post_data)

-  # it "should be able to query a multilevel composite jolokia mbean", (done) =>
-  #   post_data = () =>
-  #     url_href = 'http://localhost:47432/jolokia/'
-  #     js.add_client 'test', url_href
-  #     , [
-  #       { mbean: 'java.lang:name=ParNew,type=GarbageCollector',
-  #       attributes: [
-  #         { name: 'LastGcInfo'
-  #         composites: [
-  #           { name: 'memoryUsageBeforeGC|Code Cache|init',
-  #           graph:
-  #             name: "MemoryUsage_Before_GC_Code_Cache_Init"
-  #             description: "Code Cache (Init) Memory Usage Before GC"
-  #             units: "bytes"
-  #             type: "int32" }
-  #         ] }
-  #       ] }
-  #     ]
-
-  #     js.query_jolokia 'test', (err, resp) =>
-  #       resp.memoryUsageAfterGc['Code Cache'].init.should.equal 2555904
-  #       srv.close()
-  #       done()
-
-  #   app = express()
-  #   app.use express.bodyParser()
-  #   app.post '/jolokia', (req, res, next) =>
-  #     return_package =
-  #       status: 200
-  #       timestamp: 1356657870
-  #       duration: 10
-  #       request:
-  #         attribute: 'LastGcInfo'
-  #         type: 'read'
-  #         mbean: 'java.lang:name=ParNew,type=GarbageCollector'
-  #       value:
-  #         id: 118583
-  #         memoryUsageBeforeGc:
-  #           'Code Cache':
-  #             init: 2555904
-  #             committed: 9764864
-  #             used: 9589824
-  #             max: 50331648
-  #           'CMS Perm Gen':
-  #             init: 21757952
-  #             committed: 75091968
-  #             used: 44995904
-  #             max: 268435456
-  #           'CMS Old Gen':
-  #             init: 262406144
-  #             committed: 1973878784
-  #             used: 1257080824
-  #             max: 1973878784
-  #           'Par Eden Space':
-  #             init: 104988672
-  #             committed: 558432256
-  #             used: 558432256
-  #             max: 558432256
-  #           'Par Survivor Space':
-  #             init: 13107200
-  #             committed: 69730304
-  #             used: 6333344
-  #             max: 69730304
-  #         GcThreadCount: 11,
-  #         endTime: 521639692
-  #         startTime: 521639682
-  #         memoryUsageAfterGc:
-  #           'Code Cache':
-  #             init: 2555904
-  #             committed: 9764864
-  #             used: 9589824
-  #             max: 50331648
-  #           'CMS Perm Gen':
-  #             init: 21757952
-  #             committed: 75091968
-  #             used: 44995904
-  #             max: 268435456
-  #           'CMS Old Gen':
-  #             init: 262406144
-  #             committed: 1973878784
-  #             used: 1257559928
-  #             max: 1973878784
-  #           'Par Eden Space':
-  #             init: 104988672
-  #             committed: 558432256
-  #             used: 0
-  #             max: 558432256
-  #           'Par Survivor Space':
-  #             init: 13107200
-  #             committed: 69730304
-  #             used: 8012272
-  #             max: 69730304
-  #     res.json 200, return_package
-
-  #   srv = http.createServer(app)
-  #   srv.listen(47432, post_data)
+  it "should be able to query a multilevel composite jolokia mbean", (done) =>
+    post_data = () =>
+      url_href = 'http://localhost:47432/jolokia/'
+      js.add_client 'test', url_href
+      , [
+        { mbean: 'java.lang:name=ParNew,type=GarbageCollector',
+        attributes: [
+          { name: 'LastGcInfo'
+          composites: [
+            { name: 'memoryUsageBeforeGc|Code Cache|init',
+            graph:
+              name: "MemoryUsage_Before_GC_Code_Cache_Init"
+              description: "Code Cache (Init) Memory Usage Before GC"
+              units: "bytes"
+              type: "int32" }
+          ] }
+        ] }
+      ]
+
+      js.query_jolokia 'test', (err, resp) =>
+        Object.keys(resp).should
+        .include 'java.lang:name=ParNew,type=GarbageCollector'
+        k = resp['java.lang:name=ParNew,type=GarbageCollector']
+        Object.keys(k).should.include 'LastGcInfo'
+        Object.keys(k['LastGcInfo']).should
+        .include 'memoryUsageBeforeGc|Code Cache|init'
+        k2 = k['LastGcInfo']['memoryUsageBeforeGc|Code Cache|init']
+        k2.value.should.equal 2555904
+        Object.keys(k2.graph).should.have.length 4
+        srv.close()
+        done()
+
+    app = express()
+    app.use express.bodyParser()
+    app.post '/jolokia', (req, res, next) =>
+      return_package = [
+        { status: 200
+        timestamp: 1356657870
+        duration: 10
+        request:
+          attribute: 'LastGcInfo'
+          type: 'read'
+          mbean: 'java.lang:name=ParNew,type=GarbageCollector'
+        value:
+          id: 118583
+          memoryUsageBeforeGc:
+            'Code Cache':
+              init: 2555904
+              committed: 9764864
+              used: 9589824
+              max: 50331648
+            'CMS Perm Gen':
+              init: 21757952
+              committed: 75091968
+              used: 44995904
+              max: 268435456
+            'CMS Old Gen':
+              init: 262406144
+              committed: 1973878784
+              used: 1257080824
+              max: 1973878784
+            'Par Eden Space':
+              init: 104988672
+              committed: 558432256
+              used: 558432256
+              max: 558432256
+            'Par Survivor Space':
+              init: 13107200
+              committed: 69730304
+              used: 6333344
+              max: 69730304
+          GcThreadCount: 11,
+          endTime: 521639692
+          startTime: 521639682
+          memoryUsageAfterGc:
+            'Code Cache':
+              init: 2555904
+              committed: 9764864
+              used: 9589824
+              max: 50331648
+            'CMS Perm Gen':
+              init: 21757952
+              committed: 75091968
+              used: 44995904
+              max: 268435456
+            'CMS Old Gen':
+              init: 262406144
+              committed: 1973878784
+              used: 1257559928
+              max: 1973878784
+            'Par Eden Space':
+              init: 104988672
+              committed: 558432256
+              used: 0
+              max: 558432256
+            'Par Survivor Space':
+              init: 13107200
+              committed: 69730304
+              used: 8012272
+              max: 69730304 }
+      ]
+      res.json 200, return_package
+
+    srv = http.createServer(app)
+    srv.listen(47432, post_data)

   # it "should support templating for metric aggregation"


Ha, the human client was using the position constants

--- web/assets/js/hearts/controllers.coffee
+++ web/assets/js/hearts/controllers.coffee
@@ -12,10 +12,10 @@
     game.playCard()

   positions = [
-    AgentVsAgent.Position.NORTH
-    AgentVsAgent.Position.EAST
-    AgentVsAgent.Position.SOUTH
-    AgentVsAgent.Position.WEST
+    "north"
+    "east"
+    "south"
+    "west"
   ]

   $scope.Positions = AgentVsAgent.Position


404 early

404 early

--- controllers/people.coffee
+++ controllers/people.coffee
@@ -8,10 +8,10 @@
   next()

 loadPerson = (req, res, next) ->
-  if uid = req.param('id')
-    Person.findById uid, (err, person) ->
+  if id = req.param('id')
+    Person.findById id, (err, person) ->
       return next err if err
-      return next() unless person
+      return res.send 404 unless person
       req.person = person
       next()
   else


Handle EINVAL errors on open

Handle EINVAL errors on open

Closes atom/tree-view#408

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -442,8 +442,8 @@
         when 'EACCES'
           atom.notifications.addWarning("Permission denied '#{error.path}'")
           return Q()
-        when 'EPERM', 'EBUSY', 'ENXIO', 'EIO', 'ENOTCONN', 'UNKNOWN', 'ECONNRESET'
-          atom.notifications.addWarning("Unable to open '#{error.path}'", detail: error.message)
+        when 'EPERM', 'EBUSY', 'ENXIO', 'EIO', 'ENOTCONN', 'UNKNOWN', 'ECONNRESET', 'EINVAL'
+          atom.notifications.addWarning("Unable to open '#{error.path ? uri}'", detail: error.message)
           return Q()
         else
           throw error


AuthWorker: Make rejecting in silence

AuthWorker: Make rejecting in silence

--- workers/auth/lib/auth/authworker.coffee
+++ workers/auth/lib/auth/authworker.coffee
@@ -129,7 +129,7 @@
     clientsByExchange.push client

   rejectClient:(routingKey, message)->
-    console.log 'rejecting', routingKey
+    # console.log 'rejecting', routingKey
     return console.trace()  unless routingKey?
     @bongo.respondToClient routingKey,
       method    : 'error'


Export our Request class and add a missing require.

--- application/chrome/content/wesabe/ofx/Request.coffee
+++ application/chrome/content/wesabe/ofx/Request.coffee
@@ -1,5 +1,6 @@
 date    = require 'lang/date'
 func    = require 'lang/func'
+type    = require 'lang/type'
 xhr     = require 'io/xhr'
 privacy = require 'util/privacy'
 {uuid}  = require 'ofx/UUID'
@@ -272,3 +273,6 @@
     "</INVSTMTRQ>\r\n" +
     "</INVSTMTTRNRQ>\r\n" +
     "</INVSTMTMSGSRQV1>\r\n"
+
+
+module.exports = Request


Ensure that vhost is set if no .rabbitvhost exists.

Otherwise node-amqp craps out.

--- config/dev.coffee
+++ config/dev.coffee
@@ -10,7 +10,9 @@

 projectRoot = nodePath.join __dirname, '..'

-rabbitVhost = try fs.readFileSync nodePath.join(projectRoot, '.rabbitvhost'), 'utf8'
+rabbitVhost =\
+  try fs.readFileSync nodePath.join(projectRoot, '.rabbitvhost'), 'utf8'
+  catch e then "/"

 module.exports = deepFreeze
   projectRoot   : projectRoot


app-win-portable.zip -> app-win32-portable.zip

--- tasks/pack.coffee
+++ tasks/pack.coffee
@@ -22,7 +22,7 @@
 # Create the win32 portable zip
 gulp.task 'pack:win32:portable', ['build:win32', 'sign:win32'], (done) ->
   gulp.src './build/win32'
-    .pipe zip manifest.name + '-win-portable.zip'
+    .pipe zip manifest.name + '-win32-portable.zip'
     .pipe gulp.dest './dist'

 # Pack for all the platforms


More debugging...

More debugging...

--- tasks/pack.coffee
+++ tasks/pack.coffee
@@ -429,19 +429,19 @@

     (callback) ->
       try
-        fs.readdirSync './'
+        console.log fs.readdirSync './'
       catch error
         console.log 'err fs.readdirSync \'./\'', error
       try
-        fs.readdirSync './dist'
+        console.log fs.readdirSync './dist'
       catch error
         console.log 'err fs.readdirSync \'./dist\'', error
       try
-        fs.readdirSync '../'
+        console.log fs.readdirSync '../'
       catch error
         console.log 'err fs.readdirSync \'../\'', error
       try
-        fs.readdirSync '../dist'
+        console.log fs.readdirSync '../dist'
       catch error
         console.log 'err fs.readdirSync \'../dist\'', error
       callback()


remove cruft

remove cruft

--- client/general.coffee
+++ client/general.coffee
@@ -21,7 +21,6 @@
 # --------------------------------------------
 # session variables
 editing_name = new ReactiveVar(null)
-Session.setDefault "all_tags", []
 Session.setDefault "NeuronCatalogSpecialization",[]

 window.modal_save_func = null
@@ -297,13 +296,6 @@
 Template.registerHelper "binary_data_cursor", ->
   BinaryData.find {}

-Template.registerHelper "get_all_tags", ->
-  return Session.get("all_tags")
-
-Template.registerHelper "dumpthis", ->
-  console.log "dumpthis:", this
-  "this: "+this
-
 setTitle = () ->
   cfg = NeuronCatalogConfig.findOne {}
   if cfg?


Clean up user completion

Clean up user completion

--- client/message.coffee
+++ client/message.coffee
@@ -751,7 +751,7 @@
               ch: 0
             , cursor
             /@[^\s@]*$/.exec(text)[0]
-          users = usernames = usersSearch = null
+          users = null
           editor.on 'keyup', (cm, e) ->
             if e.key.length == 1 and  ## regular typing
                (word = lastWord cm).startsWith '@'
@@ -759,26 +759,18 @@
               cm.showHint
                 completeSingle: false
                 hint: (cm) -> new Promise (callback) ->
-                  unless users?
-                    users = []
-                    usernames = []
-                    usersSearch = []
-                    (Meteor.users.find {}, fields:
-                      username: 1
-                      "profile.fullname": 1
-                    ).forEach (user) ->
-                      usernames.push user.username
-                      if user.profile.fullname
-                        users.push "#{user.username} (#{user.profile.fullname})"
-                      else
-                        users.push user.username
-                      usersSearch.push \
-                        users[users.length-1].toLowerCase().replace /\s/g, ''
-                  matches =
-                    for search, i in usersSearch \
-                        when 0 <= search.indexOf word.toLowerCase()
-                      text: usernames[i] + ' '
-                      displayText: users[i]
+                  users ?= (Meteor.users.find {}, fields:
+                    username: 1
+                    "profile.fullname": 1
+                  ).map (user) ->
+                    display = user.username
+                    if user.profile.fullname
+                      display += " (#{user.profile.fullname})"
+                    text: user.username + ' '
+                    displayText: display
+                    search: display.toLowerCase().replace /\s/g, ''
+                  matches = (user for user in users \
+                    when user.search.includes word.toLowerCase())
                   cursor = cm.getCursor()
                   callback
                     list: matches


StackOverflow link

StackOverflow link

--- client/message.coffee
+++ client/message.coffee
@@ -482,6 +482,7 @@
       return unless image.width  ## wait for load
       if settings.rotate
         radians = settings.rotate * Math.PI / 180
+        ## Computation based on https://stackoverflow.com/a/3231438
         width = Math.abs(Math.sin radians) * image.height + Math.abs(Math.cos radians) * image.width
         height = Math.abs(Math.sin radians) * image.width + Math.abs(Math.cos radians) * image.height
         scale = image.width / width


Remove old helper code (historify, tabindex)

--- client/message.coffee
+++ client/message.coffee
@@ -431,20 +431,6 @@
       messageFolded.set ancestor._id, false
   ## Also unfold message itself, because you probably want to see it.
   messageFolded.set id, false
-
-historify = (x, post) -> () ->
-  history = messageHistory.get @_id
-  value =
-    if history?
-      history[x]
-    else
-      @[x]
-  if post?
-    value = post value
-  value
-
-tabindex = (i) ->
-  1 + 20 * Template.instance().count + parseInt(i ? 0)

 routeHere = (id) ->
   id? and Router.current().route?.getName() == 'message' and


Documenting Router class

Documenting Router class

--- www/src/theoricus/core/router.coffee
+++ www/src/theoricus/core/router.coffee
@@ -55,10 +55,10 @@
     , 1

   ###*
-    Creates and store a route within `routes` array.
+    Create and store a route within `routes` array.
     @method map
-    @param route {String}
-    @param to {String} Controller and action to which the route will be sent.
+    @param route {String} Url state.
+    @param to {String} Controller '/' action to which the route will be sent.
     @param at {String} Route to be called as a dependency.
     @param el {String} CSS selector to define where the template will be rendered.
   ###
@@ -67,9 +67,9 @@
     return route

   ###*
-    Handle the url route.
+    Handle the url state.
     @method route
-    @param state {Object} HTML5 pushstate object
+    @param state {Object} HTML5 pushstate state
   ###
   route:( state )->

@@ -121,10 +121,10 @@
     @trigger = true

   ###*
-    Change the url route.
+    Change the url state.

     @method navigate
-    @param url {String} Route to be updated.
+    @param url {String} New url state.
     @param [trigger=true] {String} If false,
     @param [replace=false] {String} If true, pushes a new state to the browser.
   ###
@@ -139,8 +139,8 @@
     Set the url if the browser doesn't support HTML5 pushstate.

     @method run
-    @param url {String} Route to be updated.
-    @param [trigger=true] {String} If false, doesn't handle the url route.
+    @param url {String} New url state.
+    @param [trigger=true] {String} If false, doesn't handle the url's state.
   ###
   run:( url, trigger = true )=>
     ( url = url.replace @the.base_path, '' ) if @the.base_path?


bugfix number adjustment

bugfix number adjustment

--- ui/javascripts/main.coffee
+++ ui/javascripts/main.coffee
@@ -34,14 +34,14 @@
   numberIntersections()

 start_mcts = (board)->
-  move = mcts(board, 100)
+  move = mcts(board, 20)
   play_stone(move, board)
   set_move_on_ui_board(move)

 set_move_on_ui_board = (move)->
-  $target_cell = $("table.go-board tr:nth-child(#{move.y}) td:nth-child(#{move.x})")
+  $target_cell = $("table.go-board tr:nth-child(#{move.y - 1}) td:nth-child(#{move.x - 1})")
   stone = $(GO_STONE)
   stone.addClass(COLOR_TO_CLASS[move.color])
   $target_cell.append(stone)


change ES startup checks to handle multiple ES URLs

--- server/api/lib/elasticsearch.coffee
+++ server/api/lib/elasticsearch.coffee
@@ -13,10 +13,11 @@
   console.log 'ES WARNING - ELASTICSEARCH SEEMS TO BE REQUIRED BUT SETTINGS HAVE NOT BEEN PROVIDED.'
 else
   try
-    s = HTTP.call 'GET', API.settings.es.url
+    API.settings.es.url = [API.settings.es.url] if typeof API.settings.es.url is 'string'
+    for url in API.settings.es.url
+      s = HTTP.call 'GET', url
     if API.settings.log?.level is 'debug'
       console.log 'ES confirmed ' + API.settings.es.url + ' is reachable'
-      console.log s.data
   catch err
     console.log 'ES FAILURE - INSTANCE AT ' + API.settings.es.url + ' APPEARS TO BE UNREACHABLE.'
     console.log err


Xcode 4.5.2 fix: s/iPhone/iOS/ for simulator name

--- uiautomation-runner.coffee
+++ uiautomation-runner.coffee
@@ -100,8 +100,10 @@

 quit_simulator = (settings, callback=(->)) ->
-  script = 'tell application "iPhone Simulator" to quit'
-  spawn_with_output 'osascript', ['-e', script], callback
+  get_xcode_version (xcode_version) ->
+    name = if xcode_version >= '4.5.2' then "iOS Simulator" else "iPhone Simulator"
+    script = "tell application \"#{name}\" to quit"
+    spawn_with_output 'osascript', ['-e', script], callback

 bring_simulator_to_front = (settings, callback=(->)) ->
@@ -109,6 +111,15 @@
   spawn_with_output 'osascript', ['-e', script], callback

+get_xcode_version = (c) ->
+  path = "/Applications/Xcode.app/Contents/Info.plist"
+  exec "defaults read '#{path}' CFBundleShortVersionString", (e, out, err) ->
+    return c null if e
+    m = out.match(/([^ \n]+)/)
+    return c null if not m
+    c m[1]
+
+
 module.exports = {
   build_and_test
   mkdirp, spawn_with_output


Fix userKey

Fix userKey

--- server/models/registeredUser.coffee
+++ server/models/registeredUser.coffee
@@ -13,8 +13,8 @@

 registeredUserSchema.methods.userKey = () ->
-    @informaticsUsername
-
+    @informaticsId
+
 registeredUserSchema.plugin(passportLocalMongoose);

 RegisteredUser = mongoose.model('registeredUser', registeredUserSchema);


[IMPAC-809] - Show the dialog when the widget there is no data. It avoid the error because the user cannot use the widget.

--- src/components/widgets/accounts-cash-projection/accounts-cash-projection.directive.coffee
+++ src/components/widgets/accounts-cash-projection/accounts-cash-projection.directive.coffee
@@ -176,6 +176,7 @@
   # == Widget =====================================================================================
   # Executed after the widget content is retrieved from the API
   w.initContext = ->
+
     # Hide Cash Flow series returned by the API
     cashFlowSerie = _.find w.content.chart.series, (serie) ->
       serie.name == "Cash flow"
@@ -189,7 +190,13 @@
       w.metadata.bolt_path,
       'companies',
       { metadata: _.pick(w.metadata, 'organization_ids') }
-    ).then((response) -> $scope.firstCompanyId = response.data.data[0].id)
+    ).then((response) ->
+      if _.isEmpty(response.data.data)
+        w.demoData = true
+      else
+        w.demoData = false
+        $scope.firstCompanyId = response.data.data[0].id
+    )

   # Executed after the widget and its settings are initialised and ready
   w.format = ->


fixes #1976: minor REPL tab completion bug fixes


監視対象のファイルを最適化

監視対象のファイルを最適化

--- grunt/esteWatch.coffee
+++ grunt/esteWatch.coffee
@@ -1,12 +1,14 @@
 module.exports = (grunt) ->
 	options:
 		dirs: [
-			"<%= DEV_PC_PATH %>**/"
-			"!<%= DEV_PC_PATH %>fla/**/"
-			"!<%= DEV_PC_PATH %><%= SCSS_DIR %>parts/"
-			"<%= DEV_SP_PATH %>**/"
-			"!<%= DEV_SP_PATH %><%= SCSS_DIR %>parts/"
-			"<%= DEV_FP_PATH %>**/"
+			"<%= DEV_PC_PATH %>_includes/"
+			"<%= DEV_PC_PATH %>_layouts/"
+			"<%= DEV_PC_PATH %>coffee/**"
+			"<%= DEV_PC_PATH %>css/"
+			"<%= DEV_PC_PATH %>data/"
+			"<%= DEV_PC_PATH %>js/"
+			"<%= DEV_PC_PATH %>sass/**"
+			"<%= DEV_PC_PATH %>"
 		]
 		livereload:
 			enabled: true


fix: ordering poll options

fix: ordering poll options

--- vue/src/shared/models/poll_model.coffee
+++ vue/src/shared/models/poll_model.coffee
@@ -55,10 +55,10 @@
     @belongsTo 'discussion'
     @belongsTo 'group'
     @belongsTo 'guestGroup', from: 'groups'
-    @hasMany   'pollOptions'
-    @hasMany   'stances', sortBy: 'createdAt', sortDesc: true
+    @hasMany   'pollOptions', orderBy: 'priority'
+    @hasMany   'stances'
     @hasMany   'pollDidNotVotes'
-    @hasMany   'versions', sortBy: 'createdAt'
+    @hasMany   'versions'

   authorName: ->
     @author().nameWithTitle(@)


guard null exception in user.nameWithTitle

--- vue/src/shared/models/user_model.coffee
+++ vue/src/shared/models/user_model.coffee
@@ -90,5 +90,6 @@
     @avatarUrl[size]

   nameWithTitle: (group) ->
+    return unless group
     titles = @titles || {}
     compact([@name, (titles[group.id] || titles[group.parentId])]).join(' · ')


bugfix: initialize body earlier

bugfix: initialize body earlier


update config file with new version of deps

--- client/config.coffee
+++ client/config.coffee
@@ -24,13 +24,13 @@
                 # Files in `vendor` directories are compiled before other files
                 # even if they aren't specified in order.
                 before: [
-                    'vendor/scripts/jquery-1.9.1.js'
+                    'vendor/scripts/jquery-2.1.1.js'
                     'vendor/scripts/jquery-ui-1.10.3.custom.js'
                     'vendor/scripts/bootstrap.js'
                     'vendor/scripts/bootstrap-datepicker.js'
                     'vendor/scripts/bootstrap-timepicker.js'
                     'vendor/scripts/underscore-1.4.4.js'
-                    'vendor/scripts/backbone-1.0.0.js'
+                    'vendor/scripts/backbone-1.1.2.js'
                     'vendor/scripts/spin.js'
                     'vendor/scripts/fullcalendar.js'
                     'vendor/scripts/rrule.js'


improved "toggle localisation" (ctrl-up)

--- client/editor.coffee
+++ client/editor.coffee
@@ -54,9 +54,15 @@
         r = '[A-Z_a-zÀ-ÖØ-Ýß-öø-üþ∆⍙Ⓐ-Ⓩ0-9]*' # regex fragment to match identifiers
         name = ((///⎕?#{r}$///.exec(s[...c.ch])?[0] or '') + (///^#{r}///.exec(s[c.ch..])?[0] or '')).replace /^\d+/, ''
         if name
-          l = cm.getLine 0; [head, tail...] = l.split ';'
-          i = tail.indexOf name; if i < 0 then tail.push name else tail.splice i, 1
-          cm.replaceRange [head].concat(tail.sort()).join(';'), {line: 0, ch: 0}, {line: 0, ch: l.length}, 'Dyalog'
+          # search backwards for a line that looks like a tradfn header (though in theory it might be a dfns's recursive call)
+          l = c.line; while l >= 0 && !/^\s*∇\s*\S/.test cm.getLine l then l--
+          if l < 0 and !/\{\s*$/.test cm.getLine(0).replace /⍝.*/, '' then l = 0
+          if l >= 0 && l != c.line
+            [_, s, comment] = /([^⍝]*)(.*)/.exec cm.getLine l
+            [head, tail...] = s.split ';'; head = head.replace /\s+$/, ''; tail = tail.map (x) -> x.replace /\s+/g, ''
+            i = tail.indexOf name; if i < 0 then tail.push name else tail.splice i, 1
+            s = [head].concat(tail.sort()).join(';') + if comment then ' ' + comment else ''
+            cm.replaceRange s, {line: l, ch: 0}, {line: l, ch: cm.getLine(l).length}, 'Dyalog'
         return

   createBreakpointElement = -> $('
●
')[0]


render tooltip even if no data

render tooltip even if no data

--- client/ironTable.coffee
+++ client/ironTable.coffee
@@ -11,11 +11,11 @@
 #    console.log("ironTable created")

-#Template.ironTable.rendered = ->
+Template.ironTable.rendered = ->
+    $('[rel="tooltip"]').tooltip()
     #console.log("ironTable rendered")
     #getCurrentIronTableController().reset()
     #getCurrentIronTableController().fetchRecordCount()
-    #$('[rel="tooltip"]').tooltip()
     #$('[rel="popover"]').popover()


removing point extend with instance methods

--- src/point.coffee
+++ src/point.coffee
@@ -8,22 +8,18 @@
   @constructor
   ###
   Point = (x, y) ->
-    $.extend {
-      ###*
-      The x coordinate of this point.
-      @name x
-      @fieldOf Point#
-      ###
-      x: x || 0
-      ###*
-      The y coordinate of this point.
-      @name y
-      @fieldOf Point#
-      ###
-      y: y || 0
-    }, Point.instanceMethods
-
-  Point.instanceMethods =
+    ###*
+    The x coordinate of this point.
+    @name x
+    @fieldOf Point#
+    ###
+    x: x || 0
+    ###*
+    The y coordinate of this point.
+    @name y
+    @fieldOf Point#
+    ###
+    y: y || 0
     ###*
     Creates a copy of this point.


:racehorse: V8 can’t optimize the splat

:racehorse: V8 can’t optimize the splat

--- src/range.coffee
+++ src/range.coffee
@@ -33,7 +33,7 @@
   # Returns: A {Range} based on the given object.
   @fromObject: (object, copy) ->
     if Array.isArray(object)
-      new this(object...)
+      new this(object[0], object[1])
     else if object instanceof this
       if copy then object.copy() else object
     else
@@ -85,7 +85,10 @@
   #
   # * `array` {array} of params to pass to the {::constructor}
   @deserialize: (array) ->
-    new this(array...)
+    if Array.isArray(array)
+      new this(array[0], array[1])
+    else
+      new this()

   ###
   Section: Construction


add code for better font resizing

add code for better font resizing

--- home/.atom/init.coffee
+++ home/.atom/init.coffee
@@ -18,3 +18,14 @@

 atom.commands.add selector, 'blur', ->
   atom.commands.dispatch(this, toNormalMode)
+
+######################################################
+# Allow Ctrl +/- to change the font size of everything
+# (Not just the panes, but the trees and tabs, too)
+######################################################
+# set initial value
+document.documentElement.style.fontSize = atom.config.get('editor.fontSize') + 'px'
+
+# update value
+atom.config.onDidChange 'editor.fontSize', ({newValue}) ->
+  document.documentElement.style.fontSize = newValue + 'px'


attribution fix

attribution fix

--- app/assets/javascripts/tag_building.coffee
+++ app/assets/javascripts/tag_building.coffee
@@ -42,7 +42,7 @@

 		L.control.attribution(
 			position: 'bottomright'
-			prefix: "From: NYPL Digital Collections | Warper"
+			prefix: ""
 		).addTo(@map)

 		L.control.zoom(


somewhat proper level count / this level needs improvement

--- app/assets/javascripts/tag_building.coffee
+++ app/assets/javascripts/tag_building.coffee
@@ -56,6 +56,7 @@
 		mapScore = Math.round(100 * ((@mapPolygons - @mapPolygonsSession) / @mapPolygons))

 		level = Math.floor(Math.log(@allPolygonsSession) / Math.LN2)
+		level = 0 if @allPolygonsSession == 0
 		maximumLevel = 16
 		levelScore = Math.round(100 * (level / maximumLevel)) #Math.round(100 * (@allPolygonsSession / @allPolygons))
 		levelScore = 100 if levelScore > 100


Minor fixes in 'Add memberships' lightbox

--- app/coffee/modules/admin/lightboxes.coffee
+++ app/coffee/modules/admin/lightboxes.coffee
@@ -23,6 +23,7 @@

 module = angular.module("taigaKanban")

+MAX_MEMBERSHIP_FIELDSETS = 6

 #############################################################################
 ## Create Members Lightbox Directive
@@ -71,19 +72,26 @@
             fieldSet = target.parent()

             fieldSet.remove()
-            $el.find('.add-fieldset').show()
+
+            lastActionButton = $el.find("fieldset:last > a")
+            if lastActionButton.hasClass("icon-delete delete-fieldset")
+                lastActionButton.removeClass("icon-delete delete-fieldset")
+                                .addClass("icon-plus add-fieldset")

         $el.on "click", ".add-fieldset", (event) ->
             event.preventDefault()
             target = angular.element(event.currentTarget)
             fieldSet = target.parent()

-            target.removeClass("icon-plus add-fieldset").addClass("icon-delete delete-fieldset")
+            target.removeClass("icon-plus add-fieldset")
+                  .addClass("icon-delete delete-fieldset")
+
             newFieldSet = createFieldSet()
             fieldSet.after(newFieldSet)

-            if $el.find('fieldset').length == 6
-                $el.find('.add-fieldset').hide()
+            if $el.find("fieldset").length == MAX_MEMBERSHIP_FIELDSETS
+                $el.find("fieldset:last > a").removeClass("icon-plus add-fieldset")
+                                             .addClass("icon-delete delete-fieldset")

         $el.on "click", ".button-green", (event) ->
             event.preventDefault()


Refactor UTC transform

Refactor UTC transform

--- app/coffee/modules/taskboard/charts.coffee
+++ app/coffee/modules/taskboard/charts.coffee
@@ -42,7 +42,7 @@
         width = element.width()
         element.height(240)

-        days = _.map(dataToDraw, (x) -> moment(new Date(x.day).getTime()))
+        days = _.map(dataToDraw, (x) -> moment.utc(x.day))

         data = []
         data.unshift({


expand recent activity in .links fixes #3275

test plan:
1. on the dashboard, click the whitespace next to
   the course links in the header
   - it should expand

Change-Id: Ie3cfeeb1c40217b5d92755421d54c1fc1265865b
Reviewed-on: https://gerrit.instructure.com/17104
Reviewed-by: Joe Tanner <16a9a54ddf4259952e3c118c763138e83693d7fd@instructure.com>
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
QA-Review: Cam Theriault <b4133c36b6d893d0872cc09e87b536d16aa2d241@instructure.com>

--- app/coffeescripts/bundles/dashboard.coffee
+++ app/coffeescripts/bundles/dashboard.coffee
@@ -11,7 +11,7 @@

     events:
       'click .stream_header': 'expandDetails'
-      'click .stream_header .links': 'stopPropagation'
+      'click .stream_header .links a': 'stopPropagation'
       'click .stream-details': 'handleDetailsClick'
       'beforeremove': 'updateCategoryCounts' # ujsLinks event


restore cursor position done right

restore cursor position done right


Execute the callback function associated with the specialActionVars only once and remove unused parts

--- src/application.coffee
+++ src/application.coffee
@@ -292,15 +292,10 @@
 		err.message or err
 	"#{failures.length} error#{s}: #{messages.join(' - ')}"

-# Example callback function to enable/disable vpn
-# enableVpn = (val) ->
-# 	if val == '1' then console.log('Enabling VPN') else console.log('Disabling VPN')
-
-specialActionEnvVars = {
+specialActionEnvVars =
 	'RESIN_OVERRIDE_LOCK': null
-	# @Praneeth: maybe add your vars here with a callback as value? e.g:
-	# 'RESIN_ENABLE_VPN': enableVpn
-}
+
+executedSpecialActionEnvVars = {}

 UPDATE_IDLE = 0
 UPDATE_UPDATING = 1
@@ -361,8 +356,7 @@
 					if app.environment_variable?
 						_.extend(env, app.environment_variable)
 					remoteAppEnvs[app.id] = env
-					env = _.pick env, (val, key) ->
-						!_.includes(key, _.keys(specialActionEnvVars))
+					env = _.omit(env, _.keys(specialActionEnvVars))
 					return {
 						appId: '' + app.id
 						commit: app.commit
@@ -373,12 +367,17 @@
 				remoteApps = _.indexBy(remoteApps, 'appId')
 				remoteAppIds = _.keys(remoteApps)

-				# @Praneeth: looks like this might be a good place to run the special functions,
-				# like turning vpn on/off if remoteAppEnvs has the corresponding variable.
+				# Run special functions against variables if remoteAppEnvs has the corresponding variable function mapping.
 				_.map specialActionEnvVars, (specialActionCallback, key) ->
 					_.map remoteAppIds, (appId) ->
 						if remoteAppEnvs[appId][key]? && specialActionCallback?
-							specialActionCallback(remoteAppEnvs[appId][key])
+							# This makes the Special Action Envs only trigger their functions once.
+							if executedSpecialActionEnvVars[key]?
+								if executedSpecialActionEnvVars[key] != remoteAppEnvs[appId][key]
+									specialActionCallback(remoteAppEnvs[appId][key])
+							else
+								specialActionCallback(remoteAppEnvs[appId][key])
+						executedSpecialActionEnvVars[key] = remoteAppEnvs[appId][key]

 				apps = _.indexBy(apps, 'appId')
 				localApps = _.mapValues apps, (app) ->


Set properties by enumeration

Set properties by enumeration

--- src/shape.coffee
+++ src/shape.coffee
@@ -4,13 +4,19 @@
 class Shape extends GameObject
   ###
   @description Shape constructor
-  @param {Object} args Object representing shape options
-  ###
-  constructor: (args = {}) ->
-    super(args)
+  @param {Object} options Object representing shape options
+  ###
+  constructor: (options = {}) ->
+    super(options)

     # Internal drawing cache
     @canvas = document.createElement('canvas')
+
+    # Trigger initial cache draw
+    @dirty = true
+
+    # Data structure to hold animations
+    @tweens = []

     # Default graphical options; changing these requires using setters/getters,
     # since the cache would need to be redrawn
@@ -18,26 +24,19 @@
     @_border   = { width: 0, color: '#fff' }
     @_shadow   = { x: 0, y: 0, blur: 0, color: '#fff' }
     @_vertices = 4
-    @_size     = { width: 10, height: 10 }
-
-    # Trigger initial cache draw
-    @dirty = true
-
-    @velocity = args.velocity || { x: 0, y: 0 }
-    @acceleration = args.acceleration || { x: 0, y: 0 }
-    @speed = args.speed       || 1
-    @debug = args.debug       || false
-    @fixed = args.fixed       || false # By default, moves with camera
-    @angularVelocity = args.angularVelocity || 0
-
-    @color    = args.color if args.hasOwnProperty('color')
-    @border   = args.border if args.hasOwnProperty('border')
-    @shadow   = args.shadow if args.hasOwnProperty('shadow')
-    @vertices = args.vertices if args.hasOwnProperty('vertices')
-    @size     = args.size if args.hasOwnProperty('size')
-    @path     = args.path if args.hasOwnProperty('path')  # Custom drawing function
-    @anchor   = { x: @size.width / 2, y: @size.height / 2 }
-    @tweens   = []
+    @_size = { width: 10, height: 10 }
+    @speed = 1
+    @velocity = { x: 0, y: 0 }
+    @angularVelocity = 0
+    @acceleration = { x: 0, y: 0 }
+    @fixed = false # By default, moves with camera
+    @debug = false
+
+    # Pass through any property of the options object
+    for property of options
+      @[property] = options[property] if options.hasOwnProperty(property)
+
+    @anchor = { x: @size.width / 2, y: @size.height / 2 }

   ###
   @description Getter/setter for color


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/state.coffee
+++ src/state.coffee
@@ -1,9 +1,4 @@
-StateMachine = (I, self) ->
-  I ||= {}
-
-  transition: (state) ->
-
-
+StateMachine = (name, object, options, block) ->
   return Machine(name, object, options, block)

 Callback = (options, machine, block) ->


only flush to stdout once per table render

--- src/table.coffee
+++ src/table.coffee
@@ -19,14 +19,17 @@

     Table.renderRows()

-  renderRows: ->
+  renderRows: (clear = false) ->
+    str = ""
+    str += "#{csi}#{rowCount}F" if clear
     for key,text of rows
-      Logger.log "#{csi}2K#{Table.padName(key)}  #{text}"
+      str += "#{csi}2K#{Table.padName(key)}  #{text}\n"
+
+    Logger.write str

   render: (name, message) ->
     rows[name] = message
-    clear()
-    Table.renderRows()
+    Table.renderRows true

   renderOk: (name) ->
     Table.render name, "#{rows[name]} #{csi}32m✔#{csi}0m"
@@ -36,7 +39,3 @@
     return "#{prefix}#{name}#{suffix}#{Array(pad).join(" ")}"

 module.exports = Table
-
-clear = ->
-  offset = rowCount
-  Logger.write "#{csi}#{offset}F"


Delete dead code

Delete dead code

--- src/atom/editor.coffee
+++ src/atom/editor.coffee
@@ -111,7 +111,6 @@
       @pre class: 'line', =>
         if tokens.length
           for token in tokens
-            classes = token.type.split('.').map((c) -> "ace_#{c}").join(' ')
             @span { class: token.type.replace('.', ' ') }, token.value
         else
           @raw ' '


Make output prettier.

Make output prettier.

--- js2py.coffee
+++ js2py.coffee
@@ -411,6 +411,13 @@
   p = new LinePrinter
   isElse = false

+  maybeParens = (expr) ->
+    s = walk expr
+    if expr.type not in ['BinaryExpression', 'UnaryExpression', 'LogicalExpression']
+      s
+    else
+      "(#{s})"
+
   walk = (c) ->
     return if c is null
     switch c.type
@@ -430,10 +437,11 @@
         p.indent--
       when 'FunctionDeclaration'
         p.addLine "def #{c.id.name}(#{c.params.map((p) -> (walk p) + '=None').join ', '}):"
-        p.indent++
-        for k, v of c.globalVars
-          p.addLine "global #{k}"
-        p.indent--
+        globals = Object.keys c.globalVars
+        if globals.length > 0
+          p.indent++
+          p.addLine "global #{globals.join ", "}"
+          p.indent--
         walk c.body
         p.addLine ''
       when 'IfStatement'
@@ -450,7 +458,7 @@
       when 'WhileStatement'
         if c.prelude
           c.prelude.map(walk)
-        p.addLine "while (#{walk c.test}):"
+        p.addLine "while #{walk c.test}:"
         walk c.body
       when 'VariableDeclaration'
         for d in c.declarations
@@ -468,7 +476,9 @@
         p.addLine "return #{if c.argument isnt null then walk c.argument else ''}"
       when 'ExpressionStatement'
         ex = walk c.expression
-        if ex then p.addLine "#{ex}"
+        if c.expression and \
+           c.expression.type not in ['Literal', 'Identifier', 'AssignmentExpression']
+          p.addLine "#{ex}"
       when 'CallExpression', 'NewExpression'
         "#{walk c.callee}(#{c.arguments.map(walk).join ', '})"
       when 'ThrowStatement'
@@ -486,7 +496,7 @@
       when 'PySlice'
         "#{walk c.callee}[(#{walk c.arguments[0]}):(#{walk c.arguments[1]})]"
       when 'ConditionalExpression'
-        "#{walk c.consequent} if #{walk c.test} else #{walk c.alternate}"
+        "(#{walk c.consequent} if #{walk c.test} else #{walk c.alternate})"
       when 'MemberExpression'
         if c.computed
           "#{walk c.object}[#{walk c.property}]"
@@ -508,14 +518,14 @@
           when '!==' then '!='
           when '==', '!=' then throw new Error('Unsupported')
           else c.operator
-        "(#{walk c.left}) #{op} (#{walk c.right})"
+        "#{maybeParens c.left} #{op} #{maybeParens c.right}"
       when 'UnaryExpression'
         if c.operator is '!'
-          "not (#{walk c.argument})"
+          "not #{maybeParens c.argument}"
         else if c.operator is 'delete'
-          "del (#{walk c.argument})"
+          "del #{maybeParens c.argument}"
         else if c.operator is '-'
-          "-(#{walk c.argument})"
+          "-#{maybeParens c.argument}"
         else if c.operator is 'typeof'
           v = walk c.argument
           if c.argument.type is 'Identifier'
@@ -536,9 +546,9 @@
       when 'ArrayExpression'
         "[#{c.elements.map(walk).join ', '}]"
       when 'ObjectExpression'
-        rv = 'jsdict({'
+        rv = 'jsdict({\n'
         for prop in c.properties
-          rv += "\"#{prop.key.name ? prop.key.value}\": (#{walk prop.value}), "
+          rv += "\"#{prop.key.name ? prop.key.value}\": #{walk prop.value},\n"
         rv += '})'
       when 'Literal'
         if c.value is null


CoffeeScript 0.5.5, with string interpolation and internal documentation

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -18,7 +18,7 @@
   this.exports: this.CoffeeScript: {}

 # The current CoffeeScript version number.
-exports.VERSION: '0.5.4'
+exports.VERSION: '0.5.5'

 # Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison
 # compiler.


Four space tabs to two

Four space tabs to two

--- coffee/select.coffee
+++ coffee/select.coffee
@@ -10,11 +10,11 @@
 clickEvent = if touchDevice then 'touchstart' else 'click'

 isRepeatedChar = (str) ->
-    Array::reduce.call str, (a, b) ->
-        if a is b then b else false
+  Array::reduce.call str, (a, b) ->
+    if a is b then b else false

 getFocusedSelect = ->
-    document.querySelector('.select-target-focused,.select-target.select-open')?.selectInstance
+  document.querySelector('.select-target-focused,.select-target.select-open')?.selectInstance

 searchText = ''
 searchTextTimeout = undefined
@@ -22,326 +22,326 @@
 lastCharacter = undefined

 document.addEventListener 'keypress', (e) ->
-    return unless select = getFocusedSelect()
-    return if e.charCode is 0
-
-    if e.keyCode is SPACE
-        e.preventDefault()
-
-    clearTimeout searchTextTimeout
-    searchTextTimeout = setTimeout ->
-        searchText = ''
-    , 500
-
-    searchText += String.fromCharCode e.charCode
-
-    options = select.findOptionsByPrefix(searchText)
-
-    if options.length is 1
-        # We have an exact match, choose it
-        select.selectOption options[0]
+  return unless select = getFocusedSelect()
+  return if e.charCode is 0
+
+  if e.keyCode is SPACE
+    e.preventDefault()
+
+  clearTimeout searchTextTimeout
+  searchTextTimeout = setTimeout ->
+    searchText = ''
+  , 500
+
+  searchText += String.fromCharCode e.charCode
+
+  options = select.findOptionsByPrefix(searchText)
+
+  if options.length is 1
+    # We have an exact match, choose it
+    select.selectOption options[0]
+    return
+
+  if searchText.length > 1 and isRepeatedChar(searchText)
+    # They hit the same char over and over, maybe they want to cycle through
+    # the options that start with that char
+    repeatedOptions = select.findOptionsByPrefix(searchText[0])
+
+    if repeatedOptions.length
+      selected = repeatedOptions.indexOf select.getChosen()
+
+      # Pick the next thing (if something with this prefix wasen't selected we'll end up with the first option)
+      selected += 1
+      selected = selected % repeatedOptions.length
+
+      select.selectOption repeatedOptions[selected]
+      return
+
+  if options.length
+    # We have multiple things that start with this prefix.  Based on the behavior of native select,
+    # this is considered after the repeated case.
+    select.selectOption options[0]
+    return
+
+  # No match at all, do nothing
+
+document.addEventListener 'keydown', (e) ->
+  # We consider this independently of the keypress handler so we can intercept keys that have
+  # built-in functions.
+  return unless select = getFocusedSelect()
+
+  if e.keyCode in [UP, DOWN, ESCAPE]
+    e.preventDefault()
+
+  if select.isOpen()
+    switch e.keyCode
+      when UP, DOWN
+        select.moveHighlight e.keyCode
+      when ENTER
+        select.selectHighlightedOption()
+      when ESCAPE
+        select.close()
+        select.target.focus()
+  else
+    if e.keyCode in [UP, DOWN, SPACE]
+      select.open()
+
+class Select
+
+  @defaults:
+    alignToHighlighed: 'auto'
+    className: 'select-theme-default'
+
+  constructor: (@options) ->
+    @options = extend {}, Select.defaults, @options
+    @select = @options.el
+
+    @setupTarget()
+    @renderTarget()
+
+    @setupDrop()
+    @renderDrop()
+
+    @setupSelect()
+
+    @setupTether()
+    @bindClick()
+
+  setupTarget: ->
+    @target = document.createElement 'a'
+    @target.href = 'javascript:;'
+
+    addClass @target, 'select-target'
+    if @options.className
+      addClass @target, @options.className
+
+    @target.selectInstance = @
+
+    @target.addEventListener 'click', =>
+      if not @isOpen()
+        @target.focus()
+      else
+        @target.blur()
+
+    @target.addEventListener 'focus', =>
+      addClass @target, 'select-target-focused'
+
+    @target.addEventListener 'blur', (e) =>
+      if @isOpen()
+        if e.relatedTarget and not @drop.contains(e.relatedTarget)
+          @close()
+      else
+        removeClass @target, 'select-target-focused'
+
+    @select.parentNode.insertBefore(@target, @select.nextSibling)
+
+    # TODO: Do with a class?
+    @select.style.display = 'none'
+
+  setupDrop: ->
+    @drop = document.createElement 'div'
+    addClass @drop, 'select'
+
+    if @options.className
+      addClass @drop, @options.className
+
+    document.body.appendChild @drop
+
+    @drop.addEventListener 'click', (e) =>
+      if hasClass(e.target, 'select-option')
+        @pickOption e.target
+
+    @drop.addEventListener 'mousemove', (e) =>
+      if hasClass(e.target, 'select-option')
+        @highlightOption e.target
+
+    @content = document.createElement 'div'
+    addClass @content, 'select-content'
+    @drop.appendChild @content
+
+  open: ->
+    addClass @drop, 'select-open'
+    addClass @target, 'select-open'
+
+    setTimeout =>
+      @tether.enable()
+
+    selectedOption = @drop.querySelector('.select-option-selected')
+
+    return unless selectedOption
+
+    @highlightOption selectedOption
+    @scrollDropContentToOption selectedOption
+
+    positionSelectStyle = =>
+      if hasClass(@drop, 'tether-abutted-left') or hasClass(@drop, 'tether-abutted-bottom')
+        dropBounds = getBounds @drop
+        optionBounds = getBounds selectedOption
+
+        offset = dropBounds.top - (optionBounds.top + optionBounds.height)
+
+        @drop.style.top = (parseFloat(@drop.style.top) or 0) + offset + 'px'
+
+    if @options.alignToHighlighted is 'always' or (@options.alignToHighlighted is 'auto' and @content.scrollHeight <= @content.clientHeight)
+      setTimeout positionSelectStyle
+
+  close: ->
+    @tether.disable()
+
+    removeClass @drop, 'select-open'
+    removeClass @target, 'select-open'
+    removeClass @target, 'select-target-focused'
+
+  toggle: ->
+    if @isOpen()
+      @close()
+    else
+      @open()
+
+  isOpen: ->
+    hasClass @drop, 'select-open'
+
+  bindClick: ->
+    @target.addEventListener clickEvent, => @toggle()
+
+    document.addEventListener clickEvent, (event) =>
+      return unless @isOpen()
+
+      # Clicking inside dropdown
+      if event.target is @drop or @drop.contains(event.target)
         return

-    if searchText.length > 1 and isRepeatedChar(searchText)
-        # They hit the same char over and over, maybe they want to cycle through
-        # the options that start with that char
-        repeatedOptions = select.findOptionsByPrefix(searchText[0])
-
-        if repeatedOptions.length
-            selected = repeatedOptions.indexOf select.getChosen()
-
-            # Pick the next thing (if something with this prefix wasen't selected we'll end up with the first option)
-            selected += 1
-            selected = selected % repeatedOptions.length
-
-            select.selectOption repeatedOptions[selected]
-            return
-
-    if options.length
-        # We have multiple things that start with this prefix.  Based on the behavior of native select,
-        # this is considered after the repeated case.
-        select.selectOption options[0]
+      # Clicking target
+      if event.target is @target or @target.contains(event.target)
         return

-    # No match at all, do nothing
-
-document.addEventListener 'keydown', (e) ->
-    # We consider this independently of the keypress handler so we can intercept keys that have
-    # built-in functions.
-    return unless select = getFocusedSelect()
-
-    if e.keyCode in [UP, DOWN, ESCAPE]
-        e.preventDefault()
-
-    if select.isOpen()
-        switch e.keyCode
-            when UP, DOWN
-                select.moveHighlight e.keyCode
-            when ENTER
-                select.selectHighlightedOption()
-            when ESCAPE
-                select.close()
-                select.target.focus()
+      @close()
+
+  setupTether: ->
+    @tether = new Tether
+      element: @drop
+      target: @target
+      attachment: 'top left'
+      targetAttachment: 'bottom left'
+      classPrefix: 'select'
+      constraints: [
+        to: 'window'
+        pin: true
+        attachment: 'together'
+      ]
+
+  renderTarget: ->
+    @target.innerHTML = ''
+
+    for option in @select.querySelectorAll('option')
+      if option.selected
+        @target.innerHTML = option.innerHTML
+        break
+
+    @target.appendChild document.createElement 'b'
+
+  renderDrop: ->
+    optionList = document.createElement 'ul'
+    addClass optionList, 'select-options'
+
+    for el in @select.querySelectorAll('option')
+      option = document.createElement 'li'
+      addClass option, 'select-option'
+
+      option.setAttribute 'data-value', el.value
+      option.innerHTML = el.innerHTML
+
+      if el.selected
+        addClass option, 'select-option-selected'
+
+      optionList.appendChild option
+
+    @content.innerHTML = ''
+    @content.appendChild optionList
+
+  setupSelect: ->
+    @select.selectInstance = @
+
+    @select.addEventListener 'change', =>
+      @renderDrop()
+      @renderTarget()
+
+  findOptionsByPrefix: (text) ->
+    options = @drop.querySelectorAll('.select-option')
+
+    text = text.toLowerCase()
+
+    Array::filter.call options, (option) ->
+      option.innerHTML.toLowerCase().substr(0, text.length) is text
+
+  getChosen: ->
+    if @isOpen()
+      @drop.querySelector('.select-option-highlight')
     else
-        if e.keyCode in [UP, DOWN, SPACE]
-            select.open()
-
-class Select
-
-    @defaults:
-        alignToHighlighed: 'auto'
-        className: 'select-theme-default'
-
-    constructor: (@options) ->
-        @options = extend {}, Select.defaults, @options
-        @select = @options.el
-
-        @setupTarget()
-        @renderTarget()
-
-        @setupDrop()
-        @renderDrop()
-
-        @setupSelect()
-
-        @setupTether()
-        @bindClick()
-
-    setupTarget: ->
-        @target = document.createElement 'a'
-        @target.href = 'javascript:;'
-
-        addClass @target, 'select-target'
-        if @options.className
-            addClass @target, @options.className
-
-        @target.selectInstance = @
-
-        @target.addEventListener 'click', =>
-            if not @isOpen()
-                @target.focus()
-            else
-                @target.blur()
-
-        @target.addEventListener 'focus', =>
-            addClass @target, 'select-target-focused'
-
-        @target.addEventListener 'blur', (e) =>
-            if @isOpen()
-                if e.relatedTarget and not @drop.contains(e.relatedTarget)
-                    @close()
-            else
-                removeClass @target, 'select-target-focused'
-
-        @select.parentNode.insertBefore(@target, @select.nextSibling)
-
-        # TODO: Do with a class?
-        @select.style.display = 'none'
-
-    setupDrop: ->
-        @drop = document.createElement 'div'
-        addClass @drop, 'select'
-
-        if @options.className
-            addClass @drop, @options.className
-
-        document.body.appendChild @drop
-
-        @drop.addEventListener 'click', (e) =>
-            if hasClass(e.target, 'select-option')
-                @pickOption e.target
-
-        @drop.addEventListener 'mousemove', (e) =>
-            if hasClass(e.target, 'select-option')
-                @highlightOption e.target
-
-        @content = document.createElement 'div'
-        addClass @content, 'select-content'
-        @drop.appendChild @content
-
-    open: ->
-        addClass @drop, 'select-open'
-        addClass @target, 'select-open'
-
-        setTimeout =>
-            @tether.enable()
-
-        selectedOption = @drop.querySelector('.select-option-selected')
-
-        return unless selectedOption
-
-        @highlightOption selectedOption
-        @scrollDropContentToOption selectedOption
-
-        positionSelectStyle = =>
-            if hasClass(@drop, 'tether-abutted-left') or hasClass(@drop, 'tether-abutted-bottom')
-                dropBounds = getBounds @drop
-                optionBounds = getBounds selectedOption
-
-                offset = dropBounds.top - (optionBounds.top + optionBounds.height)
-
-                @drop.style.top = (parseFloat(@drop.style.top) or 0) + offset + 'px'
-
-        if @options.alignToHighlighted is 'always' or (@options.alignToHighlighted is 'auto' and @content.scrollHeight <= @content.clientHeight)
-            setTimeout positionSelectStyle
-
-    close: ->
-        @tether.disable()
-
-        removeClass @drop, 'select-open'
-        removeClass @target, 'select-open'
-        removeClass @target, 'select-target-focused'
-
-    toggle: ->
-        if @isOpen()
-            @close()
-        else
-            @open()
-
-    isOpen: ->
-        hasClass @drop, 'select-open'
-
-    bindClick: ->
-        @target.addEventListener clickEvent, => @toggle()
-
-        document.addEventListener clickEvent, (event) =>
-            return unless @isOpen()
-
-            # Clicking inside dropdown
-            if event.target is @drop or @drop.contains(event.target)
-                return
-
-            # Clicking target
-            if event.target is @target or @target.contains(event.target)
-                return
-
-            @close()
-
-    setupTether: ->
-        @tether = new Tether
-            element: @drop
-            target: @target
-            attachment: 'top left'
-            targetAttachment: 'bottom left'
-            classPrefix: 'select'
-            constraints: [
-                to: 'window'
-                pin: true
-                attachment: 'together'
-            ]
-
-    renderTarget: ->
-        @target.innerHTML = ''
-
-        for option in @select.querySelectorAll('option')
-            if option.selected
-                @target.innerHTML = option.innerHTML
-                break
-
-        @target.appendChild document.createElement 'b'
-
-    renderDrop: ->
-        optionList = document.createElement 'ul'
-        addClass optionList, 'select-options'
-
-        for el in @select.querySelectorAll('option')
-            option = document.createElement 'li'
-            addClass option, 'select-option'
-
-            option.setAttribute 'data-value', el.value
-            option.innerHTML = el.innerHTML
-
-            if el.selected
-                addClass option, 'select-option-selected'
-
-            optionList.appendChild option
-
-        @content.innerHTML = ''
-        @content.appendChild optionList
-
-    setupSelect: ->
-        @select.selectInstance = @
-
-        @select.addEventListener 'change', =>
-            @renderDrop()
-            @renderTarget()
-
-    findOptionsByPrefix: (text) ->
-        options = @drop.querySelectorAll('.select-option')
-
-        text = text.toLowerCase()
-
-        Array::filter.call options, (option) ->
-            option.innerHTML.toLowerCase().substr(0, text.length) is text
-
-    getChosen: ->
-        if @isOpen()
-            @drop.querySelector('.select-option-highlight')
-        else
-            @drop.querySelector('.select-option-selected')
-
-    selectOption: (option) ->
-        if @isOpen()
-            @highlightOption option
-            @scrollDropContentToOption option
-        else
-            @pickOption option, false
-
-    resetSelection: ->
-        @selectOption @drop.querySelector('.select-option')
-
-    highlightOption: (option) ->
-        highlighted = @drop.querySelector('.select-option-highlight')
-        if highlighted?
-            removeClass highlighted, 'select-option-highlight'
-
-        addClass option, 'select-option-highlight'
-
-    moveHighlight: (directionKeyCode) ->
-        unless highlighted = @drop.querySelector('.select-option-highlight')
-            @highlightOption @drop.querySelector('.select-option')
-            return
-
-        options = @drop.querySelectorAll('.select-option')
-
-        highlightedIndex = Array::indexOf.call options, highlighted
-        return unless highlightedIndex >= 0
-
-        if directionKeyCode is UP
-            highlightedIndex -= 1
-        else
-            highlightedIndex += 1
-
-        if highlightedIndex < 0 or highlightedIndex >= options.length
-            return
-
-        newHighlight = options[highlightedIndex]
-
-        @highlightOption newHighlight
-        @scrollDropContentToOption newHighlight
-
-    scrollDropContentToOption: (option) ->
-        if @content.scrollHeight > @content.clientHeight
-            contentBounds = getBounds @content
-            optionBounds = getBounds option
-
-            @content.scrollTop = optionBounds.top - (contentBounds.top - @content.scrollTop)
-
-    selectHighlightedOption: ->
-        @pickOption @drop.querySelector('.select-option-highlight')
-
-    pickOption: (option, close=true) ->
-        @select.value = option.getAttribute 'data-value'
-        @triggerChange()
-
-        if close
-            setTimeout =>
-                @close()
-                @target.focus()
-
-    triggerChange: ->
-        event = document.createEvent("HTMLEvents")
-        event.initEvent("change", true, false)
-        @select.dispatchEvent event
+      @drop.querySelector('.select-option-selected')
+
+  selectOption: (option) ->
+    if @isOpen()
+      @highlightOption option
+      @scrollDropContentToOption option
+    else
+      @pickOption option, false
+
+  resetSelection: ->
+    @selectOption @drop.querySelector('.select-option')
+
+  highlightOption: (option) ->
+    highlighted = @drop.querySelector('.select-option-highlight')
+    if highlighted?
+      removeClass highlighted, 'select-option-highlight'
+
+    addClass option, 'select-option-highlight'
+
+  moveHighlight: (directionKeyCode) ->
+    unless highlighted = @drop.querySelector('.select-option-highlight')
+      @highlightOption @drop.querySelector('.select-option')
+      return
+
+    options = @drop.querySelectorAll('.select-option')
+
+    highlightedIndex = Array::indexOf.call options, highlighted
+    return unless highlightedIndex >= 0
+
+    if directionKeyCode is UP
+      highlightedIndex -= 1
+    else
+      highlightedIndex += 1
+
+    if highlightedIndex < 0 or highlightedIndex >= options.length
+      return
+
+    newHighlight = options[highlightedIndex]
+
+    @highlightOption newHighlight
+    @scrollDropContentToOption newHighlight
+
+  scrollDropContentToOption: (option) ->
+    if @content.scrollHeight > @content.clientHeight
+      contentBounds = getBounds @content
+      optionBounds = getBounds option
+
+      @content.scrollTop = optionBounds.top - (contentBounds.top - @content.scrollTop)
+
+  selectHighlightedOption: ->
+    @pickOption @drop.querySelector('.select-option-highlight')
+
+  pickOption: (option, close=true) ->
+    @select.value = option.getAttribute 'data-value'
+    @triggerChange()
+
+    if close
+      setTimeout =>
+        @close()
+        @target.focus()
+
+  triggerChange: ->
+    event = document.createEvent("HTMLEvents")
+    event.initEvent("change", true, false)
+    @select.dispatchEvent event

 window.Select = Select


Add title as Card prop.

Add title as Card prop.

--- components/Card.cjsx
+++ components/Card.cjsx
@@ -15,6 +15,7 @@
             ]).isRequired
         link        : React.PropTypes.string
         id          : React.PropTypes.string
+        title       : React.PropTypes.string
         className   : React.PropTypes.oneOfType([
                 React.PropTypes.string
                 React.PropTypes.object
@@ -32,6 +33,7 @@
             id          = @props.id
             className   = "Card #{ @props.className or '' } #{ variants }"
             href        = @props.link
+            title       = @props.title
         >
             {@props.children}
         </tag>


page header - post views - update API path

--- _assets/javascripts/shared/shared.coffee
+++ _assets/javascripts/shared/shared.coffee
@@ -127,7 +127,7 @@
   registerView: ->
     post_token = String($('.page-header').data('credit-post-token'))
     if post_token != 'undefined'
-      path = "/api/v2/post_views/?post_tokens=#{post_token}"
+      path = "/api/v2/post_views/?kind=wtf_promo&post_tokens=#{post_token}"

       $.ajax
         type: "GET"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color.coffee
+++ test/color.coffee
@@ -5,3 +5,8 @@
   equals color.channels.b, 0, "default blue channel is 0"
   equals color.channels.a, 0, "default alpha channel is 0"

+test "Color should parse rgb", ->
+  color = Color('rgb(34, 56, 74')
+  equals color.channels.r, 34, 'red channel equals input red value'
+  equals color.channels.g, 56, 'green channel equals input green value'
+  equals color.channels.b, 74, 'blue channel equals input blue value'


Adds paddings to the grid

Adds paddings to the grid

--- coffee/game/grid.coffee
+++ coffee/game/grid.coffee
@@ -8,8 +8,8 @@
     @blockSize = blockSize
     @streetSize = streetSize

-    @gridWidth = @source.outerWidth()
-    @gridHeight = @source.outerHeight()
+    @gridWidth = @source.width() - (@source.outerWidth() % @blockSize)
+    @gridHeight = @source.height() - (@source.outerHeight() % @blockSize)
     @verticalStreetNumber = Math.floor(@gridWidth / @blockSize) + 1
     @horizontalStreetNumber = Math.floor(@gridHeight / @blockSize) + 1


Less confusing syntax?

Less confusing syntax?

--- resources/assets/coffee/react/profile-page.coffee
+++ resources/assets/coffee/react/profile-page.coffee
@@ -15,17 +15,17 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with osu!web.  If not, see <http://www.gnu.org/licenses/>.
 ###
-el = React.createElement
-
 user = osu.parseJson('json-user-info').data

-ReactDOM.render \
-  el(ProfilePage.Main,
-    user: user
-    userPage: osu.parseJson('json-user-page').page
-    allStats: osu.parseJson('json-user-stats')
-    withEdit: user.id == window.currentUser.id
-    recentAchievements: osu.parseJson('json-user-recent-achievements').data
-    recentActivities: osu.parseJson('json-user-recent-activities').data
-    recentlyReceivedKudosu: osu.parseJson('json-user-recently-received-kudosu').data
-  ), document.getElementsByClassName('js-content')[0]
+element = React.createElement ProfilePage.Main,
+  user: user
+  userPage: osu.parseJson('json-user-page').page
+  allStats: osu.parseJson('json-user-stats')
+  withEdit: user.id == window.currentUser.id
+  recentAchievements: osu.parseJson('json-user-recent-achievements').data
+  recentActivities: osu.parseJson('json-user-recent-activities').data
+  recentlyReceivedKudosu: osu.parseJson('json-user-recently-received-kudosu').data
+
+target = document.getElementsByClassName('js-content')[0]
+
+ReactDOM.render element, target


Fixed sharepopup position and placement

Fixed sharepopup position and placement

--- client/activity/lib/views/activityactionsview.coffee
+++ client/activity/lib/views/activityactionsview.coffee
@@ -42,12 +42,12 @@

         {scrollX, scrollY} = global

-        new KDContextMenu
+        @append sharePopup = new KDContextMenu
           cssClass    : "activity-share-popup"
           type        : "activity-share"
           delegate    : this
-          x           : @shareLink.getX() + scrollX + 25
-          y           : @shareLink.getY() + scrollY - 7
+          x           : scrollX + 140
+          y           : scrollY - 12
           menuMaxWidth: 400
           menuMinWidth: 192
           lazyLoad    : yes


Use default invite code in register form for testing

--- client/landing/app/MainApp/login/registerform.coffee
+++ client/landing/app/MainApp/login/registerform.coffee
@@ -188,7 +188,7 @@
         name          : "inviteCode"
         forceCase     : "lowercase"
         placeholder   : "your code..."
-        # defaultValue  : "111"
+        defaultValue  : "newkoding"
         validate      :
           container   : this
           event       : "blur"


Frontend: removing old version of api fetcher

- is not needed anymore

--- smsgw/static/src/api/fetcher.coffee
+++ smsgw/static/src/api/fetcher.coffee
@@ -1,73 +0,0 @@
-###
-See for more information
-http://arcturo.github.io/library/coffeescript/07_the_bad_parts.html
-###
-"use strict"
-
-Promise = require 'bluebird'
-request = require 'superagent'
-localStorage = require 'localStorage'
-
-class ApiError extends Error
-    # bad request url
-    @url = null
-    # request method
-    @method = null
-    # request response code
-    @status = null
-    # message of error, taken from meta or error message
-    @message = null
-    # data content from response body
-    @data = null
-
-standartize = (req, token = null, query = {}) ->
-
-    req = req
-        .accept 'application/json'
-        .type 'application/json'
-    req = req.set("Authorization", "Token #{token}") if token?
-    req
-
-handler = (resolve, reject) ->
-    (res) ->
-        body = res.body
-        if res.ok is yes
-            resolve body
-        else
-            error = new ApiError()
-            error.message = res.error.message
-            error.status = res.status
-            error.url = res.req.url
-
-            if body
-                if 'data' of body
-                    error.data = body.data
-                if 'meta' of body and 'message' of body.meta
-                    error.message = body.meta.message
-
-            reject error
-
-module.exports =
-
-    get: (url, token, query = {}) ->
-        new Promise (resolve, reject) ->
-            standartize request.get(url), token, query
-                .end handler resolve, reject
-
-    post: (url, token, data = {}, query = {}) ->
-        new Promise (resolve, reject) ->
-            standartize request.post(url), token, query
-                .send data
-                .end handler resolve, reject
-
-    put: (url, token, data = {}, query = {}) ->
-        new Promise (resolve, reject) ->
-            standartize request.put(url), token, query
-                .send data
-                .end handler resolve, reject
-
-    delete: (url, token, query = {}) ->
-        new Promise (resolve, reject) ->
-            standartize request.del(url), token, query
-                .end handler resolve, reject
-


modified server host path

modified server host path

--- web/src/cs/dreamAssetLoader.coffee
+++ web/src/cs/dreamAssetLoader.coffee
@@ -61,7 +61,7 @@
 				substrPrefixLength = 8
 				if image.url.indexOf('https') isnt 0
 					substrPrefixLength = 7
-				image.url = 'http://localhost:5100/' + image.url.substr(substrPrefixLength)
+				image.url = 'http://' + document.location.hostname + ':5100/' + image.url.substr(substrPrefixLength)
 			@textureLoader.load(image.url, imageLoaded, imageProgressHandler, @imagesErrorHandler)
 		)

@@ -76,7 +76,7 @@
 			console.log image.url
 			if image.url.indexOf('https') isnt 0
 				substrPrefixLength = 7
-			image.url = 'http://localhost:5100/' + image.url.substr(substrPrefixLength)
+			image.url = 'http://' + document.location.hostname + ':5100/' + image.url.substr(substrPrefixLength)
 		imageLoaded = (texture) =>
 			if @checkTextureSize(image, texture, false)
 				return
@@ -139,7 +139,7 @@
 				if image.originalURL.indexOf('https') isnt 0
 					substrPrefixLength = 7

-				resizeURL = 'http://localhost:3000/?w=' + ~~resizedDimensions.w +
+				resizeURL = 'http://' + document.location.hostname + ':3000/?w=' + ~~resizedDimensions.w +
 					"&h=" + ~~resizedDimensions.h + "&u=http://" + image.originalURL.substr(substrPrefixLength)
 				console.log resizeURL
 				image.url = resizeURL


When editor is loaded, place cursor at top so things work immediately.

--- scripts/views/workspace/content/aloha-edit.coffee
+++ scripts/views/workspace/content/aloha-edit.coffee
@@ -94,4 +94,8 @@
           # reenable everything
           @$el.removeClass('disabled')

+          # Focus the editor
+          alohaEditable = Aloha.getEditableById @$el.attr('id')
+          alohaEditable.activate()
+
       @initalRender.resolve()


Use process.env.PORT to find the proroduction port

Signed-off-by: Ian Macalinao <08370e385f4778cad8fe504ec5445edd3d45bd9a@gmail.com>

--- config/config.coffee
+++ config/config.coffee
@@ -3,4 +3,4 @@
     port: 3000

   production:
-    port: 80
+    port: process.env.PORT || 80


Toggling hint now calls hint api endpoint.

--- web/coffee/problems.coffee
+++ web/coffee/problems.coffee
@@ -39,7 +39,7 @@

 addProblemReview = (e) ->
   e.preventDefault()
-
+
   feedback = {
     metrics: {}
     comment: ""
@@ -65,6 +65,7 @@

 toggleHint = (e) ->
   pid = $(e.target).data("pid")
+  apiCall "GET", "/api/problems/hint", {"pid": pid, "source": "basic"}
   $("#"+pid+"-hint").toggle("fast")

 loadProblems = ->
@@ -84,7 +85,7 @@
             renderProblemReview: renderProblemReview,
             sanitizeMetricName: sanitizeMetricName
           })
-
+
           #Should solved problem descriptions still be able to be viewed?
           #$("li.disabled>a").removeAttr "href"


Fixes issue with triggering selected item vs search results

--- components/main_layout/header/search_bar/view.coffee
+++ components/main_layout/header/search_bar/view.coffee
@@ -23,7 +23,8 @@
     'keyup input': 'checkSubmission'

   checkSubmission: (e) ->
-    window.location = "/search?q=#{@$input.val()}" if e.which is 13
+    return if !(e.which is 13) or @selected
+    window.location = "/search?q=#{@$input.val()}"

   indicateLoading: ->
     @$el.addClass 'is-loading'
@@ -67,6 +68,7 @@
           @$('img').error -> $(this).hide()

   selectResult: (e, model) ->
+    @selected = true
     window.location = model.get 'location'

   remove: ->


remove log message in activity

remove log message in activity

--- assets/coffeescript/recent_activity.coffee
+++ assets/coffeescript/recent_activity.coffee
@@ -64,7 +64,6 @@
       events.push("
#{ dateStr }
new release  for #{ repoName } - #{tagName}

")

     if e.type is "PullRequestEvent"
-      console.log "got pr event"
       repoName = e.repo.name
       actionType = e.payload.action
       pr_number = e.payload.number


keep fresh csrf token in meta

keep fresh csrf token in meta

--- app/assets/javascripts/folio/csrf.coffee
+++ app/assets/javascripts/folio/csrf.coffee
@@ -21,6 +21,10 @@
     return if @value
     @getToken()

+$(document).on 'turbolinks:load', ->
+  return unless window.folioFreshCsrfToken.value
+  $('meta[name="csrf-token"]').prop('content', window.folioFreshCsrfToken.value)
+
 $ ->
   $(document).one 'focus touchstart', 'form', ->
     window.folioFreshCsrfToken.preloadToken()


Enable esc

Enable esc

--- app/assets/javascripts/history.js.coffee
+++ app/assets/javascripts/history.js.coffee
@@ -47,9 +47,16 @@
         btn.popover(options).popover('show')

   $('.deck-edit-button').click ->
-    $(this).siblings('.hero-label').hide()
-    $(this).siblings('form').toggleClass('hidden')
-    $(this).addClass('hidden')
+    button = $(this).addClass('hidden')
+    label = $(this).siblings('.hero-label').addClass('hidden')
+    form = $(this).siblings('form').removeClass('hidden')
+
+    $('select', form).focus() # for esc
+    $(form).on 'keyup', (e) ->
+      if e.which == 27 # esc
+        button.removeClass('hidden')
+        label.removeClass('hidden')
+        form.addClass('hidden')

   $("select[name='result[deck_id]'], select[name='result[opponent_deck_id]']").change (event) ->
     selected = $("option:selected", this).text()
@@ -59,7 +66,7 @@
       url: form.attr('action')
       data: form.serialize(),
       success: ->
-        $(form).siblings('.hero-label').text(selected).show()
+        $(form).siblings('.hero-label').text(selected).removeClass('hidden')
         $(form).siblings('.deck-edit-button').removeClass('hidden')
         form.addClass('hidden')


Fire change, tasklist:change; doc sample markup

--- app/assets/javascripts/task_lists.coffee
+++ app/assets/javascripts/task_lists.coffee
@@ -2,6 +2,7 @@
 #
 #= provides tasklist:enabled
 #= provides tasklist:disabled
+#= provides tasklist:change
 #= provides tasklist:updateSend
 #= provides tasklist:updateComplete
 #
@@ -11,7 +12,23 @@
 #
 # Enables Task List update behavior.
 #
-# ### Expects
+# ### Example Markup
+#
+#

+#

+#

+#
+#           <input type="checkbox" class="js-task-list-item-checkbox" data-item-index="1" disabled />
+#           text
+#
+#

+#

+#     <form class="js-task-list-form">
+#       <textarea class="js-task-list-field">- [ ] text</textarea>
+#     </form>
+#

+#
+# ### Specification
 #
 # TaskLists MUST be contained in a `div.js-task-list-container`.
 #
@@ -32,9 +49,7 @@
 #
 # ### Events
 #
-# When an update succeeds, a `tasklist:updated' event is triggered, passing
-# along whatever data the request responded with. Handle this event if you
-# need additional behavior.
+# When the TaskList field has been changed, a `tasklist:change` event is fired.
 #
 # Updating a task list item by clicking on the check box will send an
 # `tasklist:updateSend` event. Following the `ajaxComplete` event,
@@ -135,6 +150,8 @@
   disableTaskList $container

   $field.val updateTaskListItem($field.val(), index, checked)
+  $field.trigger 'change'
+  $field.trigger 'tasklist:change', [index, checked]
   $form.trigger 'tasklist:updateSend', [index, checked]
   $form.one 'ajaxComplete', ->
     enableTaskList $container


fixing skipped <hr />

fixing skipped <hr />

--- app/classifier/tasks/generic-editor.cjsx
+++ app/classifier/tasks/generic-editor.cjsx
@@ -48,9 +48,11 @@

}

       {if choicesKey?
-

-
-        Choices
+

+

+
+          Choices
+

       }
       {' '}
       {if choicesKey is 'answers'


Prevent fadeOut and fadeIn of final message in interactive tour

Signed-off-by: tomdev <96835dd8bfa718bd6447ccc87af89ae1675daeca@codigy.nl>

--- core/app/backbone/controllers/tour_controller.coffee
+++ core/app/backbone/controllers/tour_controller.coffee
@@ -19,6 +19,8 @@
     FactlinkApp.leftTopCrossFadeRegion.crossFade(tourstep = new AddChannelsTourStep1())

     tourstep.on 'next', =>
+      done = false
+
       FactlinkApp.leftTopCrossFadeRegion.crossFade(new AddChannelsTourStep2())
       FactlinkApp.leftBottomRegion.crossFade(channelCollectionView)
       FactlinkApp.leftMiddleRegion.crossFade(
@@ -26,6 +28,8 @@
           addToCollection: window.Channels
           addToActivities: activities
           collection: @suggestedUserChannels))
-      suggestionview.on 'added', ->
-        FactlinkApp.leftTopCrossFadeRegion.crossFade(tourstep = new AddChannelsTourStep3())
-        tourstep.on 'next', -> window.location = '/'+      suggestionview.on 'added', =>
+        unless done
+          done = true
+          FactlinkApp.leftTopCrossFadeRegion.crossFade(tourstep = new AddChannelsTourStep3())
+          tourstep.on 'next', -> window.location = '/'


don't show channelsuggestions when we have none.

--- core/app/backbone/views/related_channels_view.coffee
+++ core/app/backbone/views/related_channels_view.coffee
@@ -5,4 +5,7 @@

   itemView: RelatedChannelView

-  itemViewOptions: => addToCollection : @options.addToCollection+  itemViewOptions: => addToCollection : @options.addToCollection
+
+  showEmptyView: => this.$el.hide()
+  closeEmptyView: => this.$el.show()


Nodejs Web Server: reduce complexity in createuser.coffee handler

--- servers/lib/server/handlers/api/createuser.coffee
+++ servers/lib/server/handlers/api/createuser.coffee
@@ -7,7 +7,7 @@

 module.exports = createUser = (req, res, next) ->

-  { JAccount, JGroup, JUser, JApiToken } = koding.models
+  { JUser } = koding.models

   clientId = getClientId req, res
   return handleClientIdNotFound res, req  unless clientId
@@ -22,20 +22,9 @@
   queue = [

     ->
-      # checking if token is valid
-      JApiToken.one { code : token }, (err, apiToken_) ->
-        return res.status(500).send 'an error occurred'  if err
-        return res.status(400).send 'invalid token!'     unless apiToken_
-        apiToken = apiToken_
-        queue.next()
-
-    ->
-      # creating a random username with first letters of group slug in front
-      username or= "#{apiToken.group.substring(0, 4)}#{hat(32)}"
-      # checking if username is available
-      JUser.usernameAvailable username, (err, { kodingUser, forbidden }) ->
-        return res.status(500).send 'an error occurred'          if err
-        return res.status(400).send 'username is not available'  if kodingUser or forbidden
+      validateCreateUser { token, username }, (err, data) ->
+        return res.status(err.statusCode).send(err.message)  if err
+        { username, apiToken } = data
         queue.next()

     ->
@@ -82,6 +71,43 @@
   daisy queue

+validateCreateUser = (data, callback) ->
+
+  { JUser, JApiToken } = koding.models
+  { token, username }  = data
+
+  apiToken = null
+
+  queue = [
+
+    ->
+      # checking if token is valid
+      JApiToken.one { code : token }, (err, apiToken_) ->
+        if err
+          return callback { statusCode : 500, message : 'an error occurred' }
+        unless apiToken_
+          return callback { statusCode : 400, message : 'invalid token!' }
+        apiToken = apiToken_
+        queue.next()
+
+    ->
+      # creating a random username with first letters of group slug in front
+      username or= "#{apiToken.group.substring(0, 4)}#{hat(32)}"
+      # checking if username is available
+      JUser.usernameAvailable username, (err, { kodingUser, forbidden }) ->
+        if err
+          return callback { statusCode : 500, message : 'an error occurred' }
+        if kodingUser or forbidden
+          return callback { statusCode : 400, message : 'username is not available' }
+        queue.next()
+
+    -> callback null, { apiToken, username }
+
+  ]
+
+  daisy queue
+
+

 validateRequest = (req) ->


Server: fix coffeelint error

Server: fix coffeelint error

--- servers/lib/server/handlers/api/createuser.coffee
+++ servers/lib/server/handlers/api/createuser.coffee
@@ -157,7 +157,7 @@

   # checking if username has valid length
   unless isUsernameLengthValid username
-     return callback apiErrors.outOfRangeUsername
+    return callback apiErrors.outOfRangeUsername

   # checking if username is available
   JUser.usernameAvailable username, (err, { kodingUser, forbidden }) ->


adding more destructive methods

adding more destructive methods

--- test/point.coffee
+++ test/point.coffee
@@ -78,14 +78,34 @@
     normal = p.norm(5)
     equals normal.x, 5

+  test "#norm$", ->
+    p = Point(6, 8)
+
+    p.norm$(5)
+
+    equals p.x, 3
+    equals p.y, 4
+
   test "#scale", ->
-    p1 = Point(5, 6)
+    p = Point(5, 6)
     scalar = 2

-    result = p1.scale(scalar)
+    result = p.scale(scalar)

-    equals result.x, p1.x * scalar
-    equals result.y, p1.y * scalar
+    equals result.x, p.x * scalar
+    equals result.y, p.y * scalar
+
+    equals p.x, 5
+    equals p.y, 6
+
+  test "#scale$", ->
+    p = Point(0, 1)
+    scalar = 3
+
+    p.scale$(scalar)
+
+    equals p.x, 0
+    equals p.y, 3

   test "#floor", ->
     p1 = Point(7.2, 6.9)


change subject line to put details in subject

--- services/web/public/coffee/main/contact-us.coffee
+++ services/web/public/coffee/main/contact-us.coffee
@@ -91,7 +91,7 @@
 				email: $scope.form.email
 				labels: "#{$scope.form.source} accounts"
 				message: "Please contact me with more details"
-				subject: "#{$scope.form.subject} - #{$scope.form.position} #{$scope.form.university}"
+				subject: "#{$scope.form.name} - General Enquiry - #{$scope.form.position} - #{$scope.form.university}"
 				inbox: "accounts"

 			request = $http.post "/support", data


spec: BrowserWindow.setResizable should not change window size

--- spec/api-browser-window-spec.coffee
+++ spec/api-browser-window-spec.coffee
@@ -128,6 +128,14 @@
   describe 'BrowserWindow.fromId(id)', ->
     it 'returns the window with id', ->
       assert.equal w.id, BrowserWindow.fromId(w.id).id
+
+  describe 'BrowserWindow.setResizable(resizable)', ->
+    it 'does not change window size for frameless window', ->
+      w.destroy()
+      w = new BrowserWindow(show: true, frame: false)
+      s = w.getSize()
+      w.setResizable not w.isResizable()
+      assert.deepEqual s, w.getSize()

   describe '"use-content-size" option', ->
     it 'make window created with content size when used', ->


Change DNT Connect endpoint to www.dnt.no

--- src/dnt-connect.coffee
+++ src/dnt-connect.coffee
@@ -1,6 +1,6 @@
 crypto = require 'crypto'

-CONNECT_URL = 'https://www.turistforeningen.no/connect'
+CONNECT_URL = 'https://www.dnt.no/connect'

 ###
 #


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/emitterable.coffee
+++ src/emitterable.coffee
@@ -18,7 +18,7 @@
       duration: 30
       includedModules: ["Movable"]
       height: 2
-      maxSpeed:
+      maxSpeed: 120
       offset: Point(0, 0)
       sprite: false
       spriteName: false


use reduce

use reduce

--- dbserver.coffee
+++ dbserver.coffee
@@ -49,26 +49,28 @@
 		for index, value of json.scan
 			weights[value.BSSID] = @weight(value.level, value.frequency)

-		place = [0, 0, 0, 0]
-
 		@wifi.find({"BSSID": {$in: scans}}).toArray (err, docs) ->
 			console.log(err) if err

-			for item in docs
-				if item.BSSID of weights
-					itemWeight = weights[item.BSSID]
-					[x, y, z, w] = place
-					newWeight = itemWeight + w
-					newX = (x * w + item.x * itemWeight) / newWeight
-					newY = (y * w + item.y * itemWeight) / newWeight
-					newZ = (z * w + item.z * itemWeight) / newWeight
-					place = [newX, newY, newZ, newWeight]
+			iter = (prev, curr) ->
+				if curr.BSSID of weights
+					currWeight = weights[curr.BSSID]
+					return [
+						prev[0] + (curr.x * currWeight),
+						prev[1] + (curr.y * currWeight),
+						prev[2] + (curr.z * currWeight),
+						prev[3] + currWeight
+					]
+				else
+					return prev
+
+			merge = docs.reduce(iter, [0, 0, 0, 0])

 			location =
-				"x": place[0]
-				"y": place[1]
-				"z": place[2]
-				"weight": place[3]
+				"x": merge[0] / merge[3]
+				"y": merge[1] / merge[3]
+				"z": merge[2] / merge[3]
+				"weight": merge[3]

 			res.send(location)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -161,7 +161,7 @@
       I.active = false
   }

-  defaultModules = [Bindable, Bounded, Cooldown, Drawable, Durable, Metered, TimedEvent]
+  defaultModules = [Bindable, Bounded, Cooldown, Drawable, Durable, Metered, TimedEvents]
   modules = defaultModules.concat(I.includedModules.invoke('constantize'))
   modules = modules.without(I.excludedModules.invoke('constantize'))


simplify Bus: remove extra dispatcher

simplify Bus: remove extra dispatcher

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -809,10 +809,7 @@
       for subscription in cloneArray(subscriptions)
         subscribeInput(subscription)
       unsubAll
-    dispatcher = new Dispatcher(subscribeAll)
-    subscribeThis = (sink) =>
-      dispatcher.subscribe(sink)
-    super(subscribeThis)
+    super(subscribeAll)
     @plug = (input) =>
       return if ended
       sub = { input: input }


check for global exports object

check for global exports object

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -1,4 +1,4 @@
-Bacon = @Bacon = {}
+(exports or @).Bacon = Bacon = {}

 Bacon.asEventStream = (eventName, selector, eventTransformer = _.id) ->
   if (isFunction(selector))
@@ -14,6 +14,7 @@
     element.on(eventName, selector, handler)
     unbind

+# `this isnt window` in broserified enviroment
 if window?
   (window.jQuery || window.Zepto)?.fn.asEventStream = Bacon.asEventStream


js state machine

js state machine

--- src/State.coffee
+++ src/State.coffee
@@ -130,6 +130,18 @@
           }
         }
         return false;`
+
+    check_exceptions: (from) ->
+      return from != I.except
+
+    run_callbacks: (params) ->
+      success = true
+      if I.options.when
+        success = I.options.when.apply(I.object, params)
+      if I.options.unless && success
+        success = !I.options.unless.apply(I.object, params)
+
+      return success

   self


minor spacing...

minor spacing...

--- src/alias.coffee
+++ src/alias.coffee
@@ -111,7 +111,7 @@
   if !config
     robot.logger.warning "Configuration HUBOT_GROUP_ALIAS is not defined."
     return
-
+
   if useDynamicGroups && !robot.auth
     robot.logger.warning "Using dynamic groups requires hubot-auth to be loaded"
     return


Update Atom font size

Update Atom font size

--- config/atom_config.cson
+++ config/atom_config.cson
@@ -7,7 +7,7 @@
     ]
   editor:
     fontFamily: "Menlo"
-    fontSize: 16
+    fontSize: 15
     invisibles: {}
     softWrap: true
   "exception-reporting":


Remove debug statements from test

Remove debug statements from test

--- test/suite.coffee
+++ test/suite.coffee
@@ -127,9 +127,5 @@
         it 'should make a copy of master version objects array'
         it 'should resize and upload original image accroding to versions'

-    it 'should upload', (done) ->
-      @timeout 20000
-      client.upload 'test/assets/photo.tiff', {}, (err, versions, meta) ->
-        assert.ifError err
-        done()
+    it 'should upload image to new random path'


Fill database with random data before each test-set

--- test/suite.coffee
+++ test/suite.coffee
@@ -3,10 +3,18 @@
 request = require 'supertest'
 assert = require 'assert'
 app = require './../coffee/server.coffee'
+data = require('./util/data-gen.coffee')(100)
+
+module.exports = app

 before (done) -> app.once 'ready', done
-
-module.exports = app
+beforeEach (done) ->
+  db = app.get 'db'
+  db.collection('test').drop (err) ->
+    throw err if err
+    db.collection('test').insert data, (err) ->
+      throw err if err
+      done()

 describe 'ntb.api', ->
   describe '/', ->


set @current to the name of the word we're trying to match

--- cf/spell.coffee
+++ cf/spell.coffee
@@ -20,7 +20,7 @@

   random_question = (data) ->
     choice = Math.floor(Math.random() * data['words'].length)
-    @current = choice
+    @current = data['words'][choice]['name']
     return data['words'][choice]

   update_question = (data) ->


добавил возможность вставки иконок

добавил возможность вставки иконок


a little bit of cleaner up the api helper code

--- src/helpers/api.coffee
+++ src/helpers/api.coffee
@@ -25,14 +25,36 @@
   date = headers['X-App-Date'] or headers['Date']
   TimeActions.setFromString(date)

+makeLocalRequest = (requestConfig) ->
+  {url} = requestConfig
+
+  [uri, params] = url.split('?')
+  if requestConfig.method is 'GET'
+    url_parts = ["#{uri}.json", params]
+  else
+    url_parts = ["#{uri}/#{requestConfig.method}.json", params]
+    requestConfig.method = 'GET'
+
+  requestConfig.url = _.compact(url_parts).join('?')
+
+makeLocalResponse = (response) ->
+  payload = if response.config.data then JSON.parse(response.config.data) else {}
+  response.data = _.extend({}, response.data, payload)
+
 setUpXHRInterceptors = ->
   axios.interceptors.request.use (requestConfig) ->
     AppActions.queRequest(requestConfig)
+
+    makeLocalRequest(requestConfig) if IS_LOCAL
+
     requestConfig

   axios.interceptors.response.use (response) ->
     {status, statusText, config} = response
     AppActions.setServerSuccess(status, statusText, config)
+
+    makeLocalResponse(response) if IS_LOCAL
+
     response

   , (errorResponse) ->
@@ -69,19 +91,10 @@

       opts = _.extend({}, opts, options)

-      if IS_LOCAL
-        [uri, params] = url.split("?")
-        if opts.method is 'GET'
-          url = "#{uri}.json?#{params}"
-        else
-          url = "#{uri}/#{opts.method}.json?#{params}"
-          opts.method = 'GET'
-
       resolved = ({headers, data}) ->
         setNow(headers)
-        if IS_LOCAL
-          data = _.extend({}, data, payload)
         successAction(data, args...) # Include listenAction for faking
+
       rejected = (response) ->
         # jqXhr, statusMessage, err
         statusCode = response.status
@@ -94,6 +107,7 @@

         if statusCode is 400
           CurrentUserActions.logout()
+        # TODO eventually pull this out to the interceptors as well or remove if no longer needed
         else if statusMessage is 'parsererror' and statusCode is 200 and IS_LOCAL
           if httpMethod is 'PUT' or httpMethod is 'PATCH'
             # HACK for PUT


Fixed the array flatten method

Fixed the array flatten method

--- src/array.coffee
+++ src/array.coffee
@@ -3,8 +3,8 @@
 #Array.prototype.flatten = (a) ->
 #	a.reduce ((xs, x) -> xs.concat flatten x if Array.isArray x else xs.concat [x]), []

-Array.prototype.flatten = (xs) ->
-  [].concat ((flatten x if Array.isArray x else [x]) for x in xs)...
+Array.prototype.flatten = () ->
+  [].concat ((if Array.isArray x then flatten x else [x]) for x in this)...

 #Array.prototype.flatten = (xs) ->
 #  (x... if Array.isArray x else x) for x in xs


Test that [up-hungry] elements are not updated when they are contained in the original fragment

--- spec_app/spec/javascripts/up/radio_spec.js.coffee
+++ spec_app/spec/javascripts/up/radio_spec.js.coffee
@@ -1,4 +1,5 @@
 u = up.util
+e = up.element
 $ = jQuery

 describe 'up.radio', ->
@@ -209,6 +210,26 @@
           expect($('.inside-text')).toHaveText('new inside')
           expect($('.outside')).toHaveText('old outside')

+      it 'does not update an [up-hungry] element if it was contained by the original fragment', asyncSpec (next) ->
+        container = fixture('.container')
+        e.affix(container, '.child[up-hungry]')
+
+        insertedSpy = jasmine.createSpy('up:fragment:inserted listener')
+        up.on('up:fragment:inserted', insertedSpy)
+
+        up.render
+          target: '.container'
+          document: """
+

+

+

+

+            """
+
+        next ->
+          expect(insertedSpy.calls.count()).toBe(1)
+          expect(insertedSpy.calls.argsFor(0)[0].target).toBe(document.querySelector('.container'))
+
     describe '[up-poll]', ->

       it 'reloads the element periodically', asyncSpec (next) ->