Document.Element.Tag.query.getSelectorCommandsLength now is available as static function

'require('document/element/element/tag/query').getSelectorCommandsLength' now works

--- Element/element/tag/query.coffee
+++ Element/element/tag/query.coffee
@@ -297,12 +297,7 @@
 	Tag = _Tag
 	Text = Element.Text

-	getSelectorCommandsLength: (selector) ->
-		sum = 0
-		queries = getQueries selector, 0
-		for query in queries
-			sum += query.length
-		sum
+	getSelectorCommandsLength: module.exports.getSelectorCommandsLength

 	queryAll: queryAll = (selector, target=[], targetCtx=target, opts=0) ->
 		assert.isString selector
@@ -454,3 +449,10 @@
 				setImmediate updateWatchers
 				pending = true
 			return
+
+module.exports.getSelectorCommandsLength = (selector) ->
+	sum = 0
+	queries = getQueries selector, 0
+	for query in queries
+		sum += query.length
+	sum


Add support for supplying a height for a restack

This will still not exceed the maxHeight which is either explicitly
defined or defaults to a calculation based off the window height.

--- src/coffee/plugins/jquery-stacked.coffee
+++ src/coffee/plugins/jquery-stacked.coffee
@@ -258,17 +258,26 @@
                 toggleChildOverlay($(child), @options)
             return @

-        restack: =>
+        restack: (event, height) =>
+            if not (event instanceof $.Event)
+                height = event
+                event = null
+
             options = $.extend {}, @options,
                 maxHeight: getMaxHeight(@$element, @options)
                 minHeight: getMinHeight(@$element, @options)

+            if height?
+                height = Math.min(height, options.maxHeight)
+            else
+                height = options.maxHeight
+
             if @_optimized
                 @$element.clearQueue('fx')
                 if @options.animate
-                    @$element.animate(height: options.maxHeight, 200)
+                    @$element.animate(height: height, 200)
                 else
-                    @$element.height(options.maxHeight)
+                    @$element.height(height)
             else
                 heights = getStackedHeights(@$element, options)
                 applyStackedHeights(@$element, heights, options)
@@ -285,19 +294,19 @@
             return @

-    $.fn.stacked = (option) ->
-        if $.isPlainObject option
-            options = $.extend {}, defaultOptions, option
+    $.fn.stacked = (method, args...) ->
+        if $.isPlainObject method
+            options = $.extend {}, defaultOptions, method
+            method = null
         else
             options = $.extend {}, defaultOptions

         @each ->
-            $this = $ @
-            data = $this.data('stacked')
-            if not data
-                $this.data 'stacked', (data = new StackedColumn $this, options)
-            if typeof option is 'string' and option.charAt(0) isnt '_'
-                data[option]()
+            $this = $(@)
+            if not (data = $this.data('stacked'))
+                $this.data('stacked', (data = new StackedColumn $this, options))
+            if typeof method is 'string' and method.charAt(0) isnt '_'
+                data[method](args...)

     $.fn.stacked.Constructor = StackedColumn


Moving things around and adding config file parser

--- sew.coffee
+++ sew.coffee
@@ -6,27 +6,28 @@
 less = require 'less'
 stitch = require 'stitch'

-util.log 'test'
-
 class Worker

-  data:
-    scripts: []
-    styles: []
-
   options:
-    config: 'config.json'
+    config: './config.json'
     js_path: './js'
     css_path: './css'
     main_js: './js/script.coffee'
     main_css: './css/style.less'

   constructor: (path) ->
+    util.log __dirname
     @path = path;
     @package = stitch.createPackage({
-      paths: [ './testfiles/lib', './testfiles/js']
+      paths: [ '.testfiles/js']
     })
     @compile()
+    @walk(@path)
+
+  readConfig: ->
+    config = fs.readFileSync @options.config if fpath.extstsSync(@options.config)
+    @options[key] = config[key] for key, value in @options
+

   walk: (path) ->
     for f in fs.readdirSync(path)
@@ -35,46 +36,36 @@
       if stats.isDirectory()
         @walk f
       else
-        @filterFile f
+        @watch(f) if @isWatchable(f)

-  filterFile: (file) ->
+  isWatchable: (file) ->
     ext = fpath.extname file
     switch ext
-      when '.coffee' then @data.scripts.push file
-      when '.less' then @data.styles.push file
-
-  watchFiles: ->
-    for f in @data.scripts
-      util.log f
-      fs.watchFile f, (curr, prev) =>
-        util.log curr.mtime + '  ' + prev.mtime
+      when '.coffee', '.less' then return true
+    return false
+
+  watch: (f) ->
+    fs.watchFile f, (curr, prev) =>
+      if curr and (curr.nlink is 0 or +curr.mtime isnt +prev.mtime)
         @compile()

   compile: ->
-    util.log 'Something updated'
-
+    @compileScripts()
+    @compileStyles()
+
+  compileScripts: ->
+    util.log 'Building scripts...'
     @package.compile (err, source) ->
       fs.writeFile 'application.js', source, (err) ->
         util.log err if err;
         util.log 'Compiled js!!!'
-
+
+  compileStyles: ->
+    util.log 'Building styles...'
     less.render fs.readFileSync('./testfiles/css/style.less', 'utf8'), (e, css) ->
       util.log e if e
       fs.writeFile 'application.css', css, (err) ->
         util.log err if err
-
-
-  compileScripts: ->
-    compiledScripts = []
-    for s in @data.scripts
-      compiledScripts.push cs.compile(fs.readFileSync(s, 'utf8'))
-    compiledScripts.join('\n')
-

 wkr = new Worker('./testfiles')

-wkr.walk('./testfiles')
-#util.log(util.inspect(wkr.data))
-#wkr.watchFiles(
-
-wkr.watchFiles()


add IPC test

add IPC test

--- tests/run.coffee
+++ tests/run.coffee
@@ -59,3 +59,18 @@
       c2 = t.client 'http://127.0.0.1:15803'
       c2.get '/', (err, res) -> t.equal '127.0.0.1', res.body, 'host + string port'
     , 1000
+
+  'path (IPC)': (t) ->
+    t.expect 'ipc'
+
+    zapp = zappa path:'/tmp/zappa_ipc', ->
+      @get '/': 'ipc'
+
+    zapp.server.on 'listening', ->
+      request = require 'request'
+      request.get ('http://unix:/tmp/zappa_ipc:' + '/'), (err,res) ->
+        console.log(err);
+        t.equal 'ipc', res.body, 'ipc'
+
+        zapp.server.close ->
+          (require 'fs').unlink '/tmp/zappa_ipc', -> true


Fixing a spelling mistake in the ES script

--- src/scripts/elasticsearch.coffee
+++ src/scripts/elasticsearch.coffee
@@ -126,7 +126,7 @@
     if cluster_url == "" || cluster_url == undefined
       msg.send("Do not recognise the cluster alias: #{alias}")
     else
-      msg.send("Gettings the Cluster settings for #{cluster_url}")
+      msg.send("Getting the Cluster settings for #{cluster_url}")
       msg.http("#{cluster_url}/_cluster/settings?pretty=true")
         .get() (err, res, body) ->
           msg.send("/code #{body}")


Fix process bug with args

Fix process bug with args

--- src/scripts/tests/process.coffee
+++ src/scripts/tests/process.coffee
@@ -45,7 +45,7 @@
     log.i "Finish request for #{taskid} handed:\n#{JSON.stringify(res, null, 2)}"

 perform = (task) ->
-    argsString = _.toPairs(task.args).map((arg) -> if arg[1]? then arg.join('=') else '').join(' ')
+    argsString = _.toPairs(task.data.args).map((arg) -> if arg[1]? then arg.join('=') else '').join(' ')

     Q.push((finish) ->
         name = task.data.experiment.name


isempty

isempty

--- src/service/device-mailer.coffee
+++ src/service/device-mailer.coffee
@@ -22,7 +22,7 @@
   onConfig: ({metadata, config}, callback) =>
     {options, encryptedOptions} = config
     {auth} = metadata
-    return callback() unless options?
+    return callback() if _.isEmpty options

     @_encryptAndUpdate {auth, options}, (error) =>
       @getVerificationMessage {auth, options}, (error, message) =>


Create all missing VMs

Create all missing VMs

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -1291,8 +1291,10 @@
     else if data.type is "shared"
       data.type = "group"

+    JRecurlyPlan         = require '../recurly'
     JRecurlySubscription = require '../recurly/subscription'
     JVM                  = require '../vm'
+    async                = require 'async'

     # _checkGroupVMs = (cb)=>
     #   JRecurlySubscription.getGroupSubscriptions @, (err, subs)->
@@ -1346,21 +1348,28 @@

             console.log createdVMs

-            for planCode, vmQuantity of paidVMs
+            stack = []
+
+            Object.keys(paidVMs).forEach (planCode)=>
+              vmQuantity = paidVMs[planCode]
               unless planCode in Object.keys createdVMs
                 quantity = vmQuantity
               else
                 quantity = vmQuantity - createdVMs[planCode]
               for i in [1..quantity] when quantity >= 1
                 console.log "Creating #{planCode}"
-                options     =
-                  planCode  : planCode
-                  usage     : {cpu: 1, ram: 1, disk: 1}
-                  type      : type
-                  account   : delegate
-                  groupSlug : @slug
-                JVM.createVm options, ->
-                  cb null, "Created VM: #{planCode}"
+                stack.push (_cb)=>
+                  options     =
+                    planCode  : planCode
+                    usage     : {cpu: 1, ram: 1, disk: 1}
+                    type      : type
+                    account   : delegate
+                    groupSlug : @slug
+                  JVM.createVm options, ->
+                    _cb null, "Created VM - #{planCode}"
+
+            async.parallel stack, (err, results)=>
+              cb err, results

     if data.type in ['user', 'group']
       _createVMs data.type, callback


allowing search for groups for guests. as we show all groups instead of group's groups, permit is not correct here. listing global groups is not part of a specific groups permission set. however once we support subgroups, we need to update this as well as findSuggestions

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -25,7 +25,6 @@
   ]

   @trait __dirname, '../../traits/followable'
-  @trait __dirname, '../../traits/filterable'
   @trait __dirname, '../../traits/taggable'
   @trait __dirname, '../../traits/protected'
   @trait __dirname, '../../traits/joinable'
@@ -343,6 +342,33 @@
       sort    : 'title' : 1
     }, callback

+  @byRelevance = secure (client, seed, options, callback)->
+      [callback, options] = [options, callback] unless callback
+      {limit, blacklist, skip}  = options
+      limit     ?= 10
+      blacklist or= []
+      blacklist = blacklist.map(ObjectId)
+      cleanSeed = seed.replace(/[^\w\s-]/).trim() #TODO: this is wrong for international charsets
+      startsWithSeedTest = RegExp '^'+cleanSeed, "i"
+      startsWithOptions = {limit, blacklist, skip}
+      @findSuggestions client, startsWithSeedTest, startsWithOptions, (err, suggestions)=>
+        if err
+          callback err
+        else if limit is suggestions.length
+            callback null, suggestions
+        else
+          containsSeedTest = RegExp cleanSeed, 'i'
+          containsOptions =
+            skip      : skip
+            limit     : limit-suggestions.length
+            blacklist : blacklist.concat(suggestions.map (o)-> o.getId())
+          @findSuggestions client, containsSeedTest, containsOptions, (err, moreSuggestions)->
+            if err
+              callback err
+            else
+              allSuggestions = suggestions.concat moreSuggestions
+              callback null, allSuggestions
+
   @fetchSecretChannelName =(groupSlug, callback)->
     JName = require '../name'
     JName.fetchSecretName groupSlug, (err, secretName, oldSecretName)->


server: don't export hasCreditCard in payload

currentGroup.payment.hasCard already has that info.

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- workers/social/lib/social/render/scriptblock.coffee
+++ workers/social/lib/social/render/scriptblock.coffee
@@ -5,7 +5,6 @@
   encoder   = require 'htmlencode'
   { argv }  = require 'optimist'
   _         = require 'lodash'
-  { hasCreditCard } = require '../../../../../servers/models/socialapi/requests'
   SocialAccount = require '../../../../../servers/models/socialapi/socialaccount'

   options.client               or= {}
@@ -23,7 +22,6 @@
   roles               = null
   permissions         = null
   combinedStorage     = null
-  hasCard             = no

   { bongoModels, client, session } = options

@@ -67,7 +65,6 @@
         userRoles: #{userRoles},
         userPermissions: #{userPermissions},
         currentGroup: #{currentGroup},
-        hasCreditCard: #{hasCard},
         isLoggedInOnLoad: true,
         userEnvironmentData: #{userEnvironmentData}
       };
@@ -106,13 +103,7 @@

         currentGroup = group  if group

-        if currentGroup?.payment?.customer?.hasCard?
-          hasCard = yes
-          return fin()
-
-        hasCreditCard client, (err) ->
-          hasCard = not err?
-          return fin()
+        fin()

     (fin) ->
       { delegate : account } = client.connection


Adjust spec.

Adjust spec.

--- src/spec/integration.spec.coffee
+++ src/spec/integration.spec.coffee
@@ -30,7 +30,7 @@
 </root>'
     @import.run(rawXml)
     .then (result) ->
-      expect(result[0]).toBe 'Update of customer is not implemented yet!'
+      expect(result[0]).toBe "Update of customer is not implemented yet - email 'some.one+27@example.com' exist!"
       done()
     .fail (err) ->
       console.log "E %j", err


quadtree seems to be working for circletest, added quadtree timer to avoid updating quadtree too often

--- src/game/objects/abstract/element.coffee
+++ src/game/objects/abstract/element.coffee
@@ -26,28 +26,30 @@
     @quadtree  = @config.quadtree  || null
     @width     = @svg.attr("width")
     @height    = @svg.attr("height")
+    @lastquad  = Utils.timestamp()
+    @quadwait  = @config.quadwait  || 500 # don't update the quadtree too often
     Utils.addChainedAttributeAccessor(@, 'fill')
     Utils.addChainedAttributeAccessor(@, 'stroke')

   collision_detect: -> # default collision detection
     return unless @react
-    data = @n.map((d) -> d.r)
-    console.log(data)
-    @quadtree = d3.geom.quadtree(data)
+    if @quadtree is null or Utils.timestamp() - @lastquad > @quadwait
+      data = @n.map((d) -> {x: d.r.x, y: d.r.y, d: d})
+      @quadtree = d3.geom.quadtree(data)
+      @lastquad = Utils.timestamp()
     if @quadtree
-      x0 = -@size - 1
-      x3 = @size + 1
-      y0 = -@size - 1
-      y3 = @size + 1
+      x0 = @r.x - 3 * @size - 1
+      x3 = @r.x + 3 * @size + 1
+      y0 = @r.y - 3 * @size - 1
+      y3 = @r.y + 3 * @size + 1
       @quadtree.visit( (node, x1, y1, x2, y2) =>
         p = node.point
-        # console.log(node, this, x0, x3, y0, y3, x1, y1, x2, y2)
         if p isnt null
-          if (p.r.x >= x0) and (p.r.x < x3) and (p.r.y >= y0) and (p.r.y < y3)
-            Collision.check(@, p)
+          if (p.x >= x0) and (p.x < x3) and (p.y >= y0) and (p.y < y3)
+            Collision.check(@, p.d)
         x1 >= x3 || y1 >= y3 || x2 < x0 || y2 < y0
       )
-    else
+    else # old neighbor detection - exhaustive (slowZZZZz)
       for n in @n
         continue unless n.react
         Collision.check(@, n)


Reduce duplicated assignment

Reduce duplicated assignment

--- resources/assets/coffee/react/mp-history/main.coffee
+++ resources/assets/coffee/react/mp-history/main.coffee
@@ -29,7 +29,7 @@

   componentDidMount: =>
-    @timeouts.autoload = Timeout.set REFRESH_TIMEOUT, @autoload
+    @delayedAutoload()

   render: =>


Check against current value of field.

Check against current value of field.

Resolves the issue where the validation result did not
match the username entered.

--- resources/assets/coffee/store-username-change.coffee
+++ resources/assets/coffee/store-username-change.coffee
@@ -26,7 +26,7 @@

   $.post '/users/check-username-availability', username: requestedUsername
   .done (data) ->
-    return unless data.username == requestedUsername
+    return unless data.username == $('#username.form-control').val()

     if data.available
       $('.js-store-add-to-cart').attr 'disabled', false


Improve feedback for indexes

Improve feedback for indexes

--- admin/static/coffee/tables/table.coffee
+++ admin/static/coffee/tables/table.coffee
@@ -345,9 +345,11 @@
         render_feedback: (args) =>
             if @$('.main_alert').css('display') is 'none'
                 @$('.alert_content').html @alert_message_template args
+                @$('.main_alert').slideDown 'fast'
             else
-                @$('.alert_content').append @alert_message_template args
-            @$('.main_alert').slideDown 'fast'
+                @$('.main_alert').fadeOut 'fast', =>
+                    @$('.alert_content').html @alert_message_template args
+                    @$('.main_alert').fadeIn 'fast'
             @$('.main_alert_error').slideUp 'fast'

         render: =>


Disable button and change text while loading.

--- app/assets/javascripts/application/views/users/edit.js.coffee
+++ app/assets/javascripts/application/views/users/edit.js.coffee
@@ -7,13 +7,18 @@
   render: ->
     @$el.html(@template(@model.attributes))
   onFormSubmit: ->
+    $('#get_api').attr('disabled', true)
+    $('#get_api').val('Loading...')
     form = @$el.find "form"
     newAttrs = @updateModelFromForm(form)
     success = =>
       gh_handle = @model.get('github_handle')
       window.getGitHubData(gh_handle) if gh_handle
       App.router.navigate("/#{@model.get('username')}", {trigger: true});
-    error = -> $('.message').html("There was an issue saving your updates")
+    error = ->
+      $('#get_api').attr('disabled', false)
+      $('#get_api').val('Submit')
+      $('.message').html("There was an issue saving your updates")
     @model.save(newAttrs, success: success, error: error)
     false


[#81130804] fix bug where you cannot remove just added participant

--- app/assets/javascripts/components/select2_component.js.coffee
+++ app/assets/javascripts/components/select2_component.js.coffee
@@ -8,6 +8,7 @@
   multiSelect: false
   initSelectionData: []
   selectedData: []
+  selectionInitialized: false

   setupSelectedListener: ->
     @.$().off 'select2-selecting'
@@ -21,9 +22,11 @@
       @sendAction 'selectionRemoved', e.choice

   setSelectedData: (->
-    if !@.$().val()
+    # ember triggers changes on us willy nilly so we need flags to protect ourselves
+    if @get('selectedData').length && !@selectionInitialized
       @initSelectionData = @get('selectedData')
-    @.$().select2('val', @get('selectedData').mapProperty('id'))
+      @.$().select2('val', @get('selectedData').mapProperty('id'))
+      @selectionInitialized = true
   ).observes('selectedData.[]')

   setup:(->


Delete unnecessary commented code

Delete unnecessary commented code

--- app/assets/javascripts/controllers/index_controller.js.coffee
+++ app/assets/javascripts/controllers/index_controller.js.coffee
@@ -19,5 +19,4 @@
     loadMorePapers: ->
       @store.find 'lite_paper', page_number: (@get('pageNumber') + 1)
       .then (litePapers) =>
-        # @get('papers').pushObjects litePapers
         @incrementProperty 'pageNumber'


no message

no message

--- tumo/-Rainbow.coffee
+++ tumo/-Rainbow.coffee
@@ -1,54 +0,0 @@
-glob = require('glob')
-methods = require('methods')
-fs = require('fs')
-BaseController = require('./BaseController')
-config = require('./../config')
-
-###*
-# Main function to initialize routers of a Express app.
-#
-# @param  {Express} app  Express app instance
-# @param  {Object} paths (optional) For configure relative paths of
-#                        controllers and filters rather than defaults.
-###
-
-exports.route = (app, paths) ->
-  paths = paths or {}
-  app.set 'views', config.base_path + paths.template
-  ctrlDir = config.base_path + (paths.controllers or '/controllers')
-  fltrDir = config.base_path + (paths.filters or '/filters')
-  tplDir = config.base_path + (paths.template or '/templates')
-  glob.sync(ctrlDir + '/**/*.+(js|coffee)').forEach (file) ->
-    file = file.replace(/\/index\.(js|coffee)$/, '')
-    router = require(file)
-    single = typeof router == 'function'
-    path = file.replace(ctrlDir.replace(/\/$/, ''), '').replace(/\.(js|coffee)$/, '')
-    tplPath = tplDir + path + '.html'
-    isTplExist = fs.existsSync(tplPath)
-
-    setup = (req, res, next) ->
-      req.rb_path = path
-      return
-
-    # '-----------------为windows修正path'
-    path = path.replace(/.*\/controllers/, '')
-    for i of router
-      p = path + i
-      if p != '/'
-        p = p.replace(/\/$/, '')
-      r = router[i]
-      methods.forEach (method) ->
-        eachRouter = r[method]
-        if eachRouter
-          controller = new BaseController(app, eachRouter, p.replace(/^\//, ''), method)
-          if controller.newName
-            console.log 'controller.newName', controller.newName
-            p = p.replace(controller.name, controller.newName)
-            console.log 'route:' + method + ':' + p
-            app[method].apply app, [ p ].concat(controller.getRoutes())
-          else
-            console.log 'route:' + method + ':' + p
-            app[method].apply app, [ p ].concat(controller.getRoutes())
-        return
-    return
-  return


Use defaultVM name if its not provided for fshelper

--- client/app/MainApp/fs/fshelper.coffee
+++ client/app/MainApp/fs/fshelper.coffee
@@ -129,6 +129,9 @@
     unless data and data.type and data.path
       return warn "pass a path and type to create a file instance"

+    unless data.vmName?
+      data.vmName = KD.getSingleton('vmController').getDefaultVmName()
+
     if @registry[data.path]
       instance = @registry[data.path]
       @updateInstance data


shortcuts: make sure we override platform bindings even if they don't exist :rabbit:

--- client/app/lib/shortcuts/index.coffee
+++ client/app/lib/shortcuts/index.coffee
@@ -305,9 +305,12 @@
   @_insertPlatformBinding: (model, platformBinding) ->

     binding = cloneArray model.binding
+    idx = klass._bindingPlatformIndex()

     if _.isArray platformBinding
-      binding[klass._bindingPlatformIndex()] = platformBinding.filter _.isString
+      binding[idx] = platformBinding.filter _.isString
+    else
+      binding[idx] = []

     return binding


fix: search filter does not reset

fix: search filter does not reset

--- client/controllers/searchInput.coffee
+++ client/controllers/searchInput.coffee
@@ -14,6 +14,9 @@
 clearSearch = (instance) ->
   instance.textFilter.set('')
   instance.$('.search').val('')
+
+Template.searchInput.onRendered ->
+  clearSearch(@)

 Template.searchInput.onCreated ->
   instanceData = @data


allow overwriting singular asset on upload

--- app/assets/javascripts/effective_assets/s3_uploader.js.coffee
+++ app/assets/javascripts/effective_assets/s3_uploader.js.coffee
@@ -37,7 +37,7 @@
         # Make sure the user isn't over the upload limit
         $asset_box = $uploadForm.closest('.asset-box-input')
         limit = $asset_box.data('limit')
-        if typeof limit == 'number'
+        if typeof limit == 'number' && limit > 1
           count = parseInt($asset_box.attr('data-attachment-count'), 10)
           if count >= limit
             alert("Unable to add file(s).  You have exceeded the limit of #{limit} uploads.") unless $asset_box.data('over-limit-alerted')
@@ -290,6 +290,7 @@
     $uploadForm.data("key", $uploadForm.find("input[name='key']").val())
     $uploadForm.data("fileCount", 0)
     $(document).on 'drop', '.asset-box-uploader', resetOverLimitAlert
+    $(document).on 'click', '.asset-box-uploader .asset-box-uploader-fileinput', resetOverLimitAlert
     setUploadForm()
     this


we assume cliente is a relationship. so we set it to the model

--- app/assets/javascripts/mixins/cliente_on_form_mixin.js.coffee
+++ app/assets/javascripts/mixins/cliente_on_form_mixin.js.coffee
@@ -7,18 +7,17 @@
 # view events
   clienteChanged: (->
     Ember.debug "cliente changed"
-    cliente = @get("cliente") || Ember.Object.create()
+    cliente = @get("model.cliente") || Ember.Object.create()
     attributes = ["tipoId", "nombres", "direccion", "telefono", "email"]
     for attribute in attributes
       @set attribute, cliente.get(attribute)
-    @set "model.cliente", cliente
-  ).observes("cliente")
+  ).observes("model.cliente")

 # query callbacks
   gotClientes: (clientes) ->
     @setProperties
       isFetchingCliente: false
-      cliente: clientes.get("firstObject")
+      "model.cliente": clientes.get("firstObject")

   didNotGetCliente: ->
     @setProperties


updated query param is clone_status=unused_source

--- tutor/src/api.coffee
+++ tutor/src/api.coffee
@@ -247,7 +247,7 @@
   route 'GET', "/api/offerings",
     actions: OfferingsActions, trigger: 'load', onSuccess: 'loaded'

-  route 'GET', "/api/courses/{courseId}/plans?cloned=true",
+  route 'GET', "/api/courses/{courseId}/plans?clone_status=unused_source",
     actions: PastTaskPlansActions, trigger: 'load', onSuccess: 'loaded'

   apiHelper CourseListingActions, CourseListingActions.load, CourseListingActions.loaded, 'GET', ->


Ensure domNode is not set arbitrarily to null

--- src/text-editor-component.coffee
+++ src/text-editor-component.coffee
@@ -35,6 +35,12 @@
   gutterComponent: null
   mounted: true
   initialized: false
+
+  Object.defineProperty @prototype, "domNode",
+    get: -> @domNodeValue
+    set: (domNode) ->
+      atom.assert domNode?, "TextEditorComponent::domNode was set to null."
+      @domNodeValue = domNode

   constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize}) ->
     @tileSize = tileSize if tileSize?


:art:

:art:

--- src/text-editor-presenter.coffee
+++ src/text-editor-presenter.coffee
@@ -51,7 +51,7 @@
   # Private: Determines whether {TextEditorPresenter} is currently batching changes.
   # Returns a {Boolean}, `true` if is collecting changes, `false` if is applying them.
   isBatching: ->
-    @updating == false
+    @updating is false

   # Private: Executes `fn` if `isBatching()` is false, otherwise sets `@[flagName]` to `true` for later processing. In either cases, it calls `emitDidUpdateState`.
   # * `flagName` {String} name of a property of this presenter


sankey: removed link tooltips

sankey: removed link tooltips

They were ugly, the node tooltip is better

--- app/assets/javascripts/d3/sankey.coffee
+++ app/assets/javascripts/d3/sankey.coffee
@@ -389,20 +389,6 @@
         .style("fill", "none")
         .style("opacity", selectedLinkOpacity)
         .attr("d", (link) => @link_line link.path_points())
-        .on("mouseover", @link_mouseover)
-        .on("mouseout", @node_mouseout)
-
-      $('g.link path').qtip
-        content: -> $(this).attr('data-tooltip')
-        show:
-          event: 'mouseover' # silly IE
-        hide:
-          event: 'mouseout'  # silly IE
-        position:
-          target: 'mouse'
-          my: 'bottom right'
-          at: 'top center'
-
       return links

     draw_nodes: =>
@@ -454,14 +440,6 @@
         .transition()
         .duration(200)
         .style("opacity", selectedLinkOpacity)
-
-    link_mouseover: ->
-      current_id = $(this).parent().attr("data-cid")
-      d3.selectAll(".link").
-        each (d) ->
-          item = d3.select(this)
-          unless d.cid == current_id
-            item.transition().duration(200).style("opacity", unselectedLinkOpacity)

     # this method is called every time we're updating the chart
     refresh: =>
@@ -506,14 +484,6 @@
           if link.value() == 0.0 then 'none' else 'inline'
         )

-
-      # then move the link labels and update their value
-      @links.selectAll("path")
-        .attr('data-tooltip', (d) =>
-          html = "#{d.left.label()} -> #{d.right.label()}"
-          html += "<br/>"
-          html += Metric.autoscale_value d.value(), 'PJ', 2)
-
 class D3.sankey.NodeList extends Backbone.Collection
   model: D3.sankey.Node


removed people js file

removed people js file

--- app/assets/javascripts/people.js.coffee
+++ app/assets/javascripts/people.js.coffee
@@ -1,18 +0,0 @@
-
-debug = false
-
-class Person
-  constructor: ->
-    $('.randcheck.being-name').click ->
-     $.get '/people/random-name',
-       (data) ->
-         console.log data
-         $('#person_name').val data[1]
-           .reverse()
-           .join(' ')
-         $('#person_gender_input').val data[0]
-         $('#person_age').val data[2]
-       'json'
-
-$ ->
-  window.person = new Person


Improve jPlayer reconnect strategy

Improve jPlayer reconnect strategy

--- app/assets/javascripts/player.js.coffee
+++ app/assets/javascripts/player.js.coffee
@@ -39,80 +39,29 @@
   $(window).trigger('page:refresh', global: true)

 $(document).ready ->
-  playing = false
-  callback = false
+  $player = $('#jquery_jplayer_1')

-  $.jPlayer.reload = ->
-    DEBUG('jPlayer: reload')
-    $self = $(this)
-    playing = false
-    volume = memcache.get('volume')
-    muted = memcache.get('muted')
-    $self.jPlayer('destroy')
-    $self.jPlayer($.jPlayer.config)
-    $self.jPlayer('volume', volume) unless isNaN(volume)
-    $self.jPlayer('mute', muted) unless isNaN(muted)
-
-  $.jPlayer.restart = ->
-    DEBUG('jPlayer: restart')
-    $self = $(this)
-    callback = ->
-      setTimeout ->
-        $self.jPlayer('play')
-      , rand(1, 5) * 1000
-    $.jPlayer.reload.call(this)
-
-  $.jPlayer.config =
+  $player.jPlayer
     ready: ->
       DEBUG('jPlayer: ready')
-      $self = $(this)
-      stream = mp3: '/listen?_=' + $.now()
-      $self.jPlayer('setMedia', stream)
-      callback() if callback
+      $this = $(this)
+      $this.trigger('reload')

     play: ->
       DEBUG('jPlayer: play')
-      playing = true
+      $this = $(this)
+      $this.data('playing', true)

     pause: ->
       DEBUG('jPlayer: pause')
-      callback = false
-      $(this).jPlayer('clearMedia')
-      $.jPlayer.reload.call(this)
+      $this = $(this)
+      $this.data('playing', false)
+      $this.trigger('reload')

-    stalled: ->
-      return unless playing
-      DEBUG('jPlayer: stalled')
-
-    ended: ->
-      return unless playing
-      DEBUG('jPlayer: ended')
-      $.jPlayer.restart.call(this)
-
-    suspend: ->
-      return unless playing
-      DEBUG('jPlayer: suspend (Ignored)')
-      # $.jPlayer.restart.call(this)
-
-    abort: ->
-      return unless playing
-      DEBUG('jPlayer: abort')
-      $.jPlayer.restart.call(this)
-
-    emptied: ->
-      return unless playing
-      DEBUG('jPlayer: emptied')
-      $.jPlayer.restart.call(this)
-
-    stop: ->
-      return unless playing
-      DEBUG('jPlayer: stop')
-      $.jPlayer.restart.call(this)
-
-    error: (event) ->
-      return unless playing
-      DEBUG('jPlayer: error: ' + event.jPlayer.error.type)
-      $.jPlayer.restart.call(this)
+    progress: (event) ->
+      DEBUG('jPlayer: progress')
+      $this = $(this)
+      $this.trigger('ping')

     volumechange: (event) ->
       $self = $(this)
@@ -139,13 +88,33 @@
       fullScreen: false
       loop: false

-  $jquery_jplayer_1 = $('#jquery_jplayer_1')
+  $player.on 'reload', ->
+    DEBUG('jPlayer: reload')
+    $this = $(this)
+    listen_path = mp3: "/listen?_=#{$.now()}"
+    is_playing = $this.data('playing')
+
+    $this.jPlayer('clearMedia')
+    $this.jPlayer('setMedia', listen_path)
+    if is_playing
+      $this.jPlayer('play')
+    else
+      old_timeout_id = $this.data('timeout-id')
+      clearTimeout(old_timeout_id)
+
+  $player.on 'ping', ->
+    $this = $(this)
+    old_timeout_id = $this.data('timeout-id')
+    clearTimeout(old_timeout_id)
+    new_timeout_id = setTimeout ->
+      $this.trigger('ping')
+      $this.trigger('reload')
+    , rand(10, 30) * 1000
+    $this.data('timeout-id', new_timeout_id)

   $('#jp_container_1 .jp-volume-bar-container').on 'mousewheel DOMMouseScroll', (event) ->
     event.preventDefault()
     delta = event.originalEvent.wheelDelta or event.originalEvent.detail * -40
     DEBUG('Mousewheel delta: ' + delta)
-    volume = $jquery_jplayer_1.jPlayer('option', 'volume') + if delta > 0 then 0.05 else -0.05
-    $jquery_jplayer_1.jPlayer('volume', volume)
-
-  $.jPlayer.reload.call($jquery_jplayer_1)
+    volume = $player.jPlayer('option', 'volume') + if delta > 0 then 0.05 else -0.05
+    $player.jPlayer('volume', volume)


Reconnect dongle at the end of FUP

Reconnect dongle at the end of FUP

--- app/src/controllers/update/update_done_view_controller.coffee
+++ app/src/controllers/update/update_done_view_controller.coffee
@@ -3,8 +3,9 @@
   localizablePageSubtitle: "update.done.update_succeeded"
   localizableNextButton: "common.restore"
   navigation:
-    nextRoute: "/onboarding/management/welcome"
+    nextRoute: "/onboarding/device/connecting"

   navigateNext: ->
-    ledger.app.setExecutionMode(ledger.app.Modes.Wallet)
-    super+    @getRequest().cancel()
+    ledger.app.reconnectDongleAndEnterWalletMode().then =>
+      super


Terminal: when sessions restored tabs wont come to front by default since it causes a bad ux

--- client/Terminal/AppView.coffee
+++ client/Terminal/AppView.coffee
@@ -86,7 +86,11 @@
             [alias, sessionId] = session.split ':'
             if sessionId in activeSessions
               sessionRestored = yes
-              @createNewTab {vm: vmList[alias], session: sessionId, mode: 'resume'}
+              @createNewTab
+                vm         : vmList[alias]
+                session    : sessionId
+                mode       : 'resume'
+                shouldShow : no

           unless sessionRestored
             @notify
@@ -94,12 +98,6 @@
                            If you want always on VMs, you can upgrade your plan"
               cssClass  : "fail"

-          activeIndex = storage.getValue 'activeIndex'
-          activePane = @tabView.getPaneByIndex activeIndex ? 1
-          if sessionRestored and activeIndex isnt 0
-            @tabView.showPane activePane
-            { terminalView } = activePane.getOptions()
-            terminalView.setKeyView()

   initPane: (pane) ->

@@ -252,7 +250,8 @@

   createNewTab: (options = {}) ->

-    { hostnameAlias: vmName, region } = options.vm
+    {shouldShow} = options
+    {hostnameAlias: vmName, region} = options.vm

     defaultOptions =
       testPath    : "webterm-tab"
@@ -262,7 +261,7 @@

     @emit 'TerminalStarted'

-    @appendTerminalTab terminalView
+    @appendTerminalTab terminalView, shouldShow
     terminalView.connectToTerminal()
     @forwardEvent terminalView, "WebTermConnected"
     @forwardEvent terminalView, "TerminalClosed"
@@ -281,7 +280,7 @@

     @tabView.addPane pane

-  appendTerminalTab: (terminalView) ->
+  appendTerminalTab: (terminalView, shouldShow = yes) ->

     @forwardEvents terminalView, ['KeyViewIsSet', 'command']

@@ -289,11 +288,11 @@
       name          : 'Terminal'
       terminalView  : terminalView

-    @tabView.addPane pane
+    @tabView.addPane pane, shouldShow
     pane.addSubView terminalView

     terminalView.on "WebTermNeedsToBeRecovered", ({vm, session}) =>
-      @createNewTab {vm, session, mode: 'resume'}
+      @createNewTab {vm, session, mode: 'resume', shouldShow : no}
       @notify
         title   : "Reconnected to Terminal"


MainView: Glance unread counts for ChannelsStore

--- client/app/lib/mainview.coffee
+++ client/app/lib/mainview.coffee
@@ -16,6 +16,7 @@
 MainTabView             = require './maintabview'
 TopNavigation           = require './topnavigation'
 environmentDataProvider = require 'app/userenvironmentdataprovider'
+actionTypes             = require 'activity/flux/actions/actiontypes'

 module.exports = class MainView extends KDView
@@ -237,7 +238,12 @@

   glanceChannelWorkspace: (channel) ->

-    @activitySidebar.glanceChannelWorkspace channel
+    if isKoding()
+      @activitySidebar.glanceChannelWorkspace channel
+    else
+      kd.singletons.reactor.dispatch actionTypes.SET_CHANNEL_UNREAD_COUNT,
+        unreadCount : 0
+        channelId   : channel.id

   createAccountArea:->


Modify fromJSON method.

Modify fromJSON method.

--- client/app/models/chart.coffee
+++ client/app/models/chart.coffee
@@ -36,16 +36,24 @@
       chartKeywords = chart.keywords()
       keywords.every (item) -> item in chartKeywords

-  @fromJSON: (objects) =>
+  @fromJSON: (records) =>
     objects = super
+    return objects if @count() is 0
     dedupedObjects = []
     for object in objects
       originalObject = @findByAttribute "slug", object.slug
       if originalObject
-        if new Date(originalObject.modified_at).getTime() < new Date(object.modified_at).getTime()
-          originalObject.obsolete = true
+        objectDate = new Date(object.modified_at).getTime()
+        originalObjectDate = new Date(originalObject.modified_at).getTime()
+        if originalObjectDate <= objectDate
+          attributes = {}
+          for key, value of object.attributes()
+            if JSON.stringify(object[key]) isnt JSON.stringify(originalObject[key])
+              debugger
+              attributes[key] = value
+          originalObject.updateAttributes(attributes)
         else
-          for key, value of object
+          for key, value of object.attributes()
             if not originalObject[key]?
               originalObject[key] = value
         dedupedObjects.push originalObject


Correct usage of '==' rather than '=' when checking err.code.

--- services/filestore/app/coffee/FSPersistorManager.coffee
+++ services/filestore/app/coffee/FSPersistorManager.coffee
@@ -37,7 +37,7 @@
     sourceStream = fs.createReadStream "#{location}/#{filteredName}", opts
     sourceStream.on 'error', (err) ->
       logger.err err:err, location:location, name:name, "Error reading from file"
-      if err.code = 'ENOENT'
+      if err.code == 'ENOENT'
         callback new Errors.NotFoundError(err.message), null
       else
         callback err


light refactor for repl.coffee

light refactor for repl.coffee

--- repl.coffee
+++ repl.coffee
@@ -2,7 +2,6 @@

 # Required modules.
 coffee: require 'coffee-script'
-process.mixin require 'sys'

 # Shortcut variables.
 prompt: 'coffee> '
@@ -12,8 +11,7 @@
 # Attempt to evaluate the command. If there's an exception, print it.
 readline: (code) ->
   try
-    js:  coffee.compile code, {no_wrap: true, globals: true}
-    val: eval(js)
+    val: eval coffee.compile code, {no_wrap: true, globals: true}
     p val if val isnt undefined
   catch err
     puts err.stack or err.toString()


Fix spacing

Fix spacing

--- src/scripts/models/search-results.coffee
+++ src/scripts/models/search-results.coffee
@@ -31,4 +31,4 @@
         _.each item.authors, (author, index) ->
           item.authors[index] = authors.get(author).toJSON()

-      return response+      return response


Fixed download reg contests

Fixed download reg contests

--- server/cron/downloadContests.coffee
+++ server/cron/downloadContests.coffee
@@ -84,7 +84,7 @@

     run: ->
         levels = []
-        for year, cont of @REGION_CONTESTS
+        for year, cont of REGION_CONTESTS
             fullText = ' тур региональной олимпиады ' + year + ' года'
             @processContest(year * 10 + 1, '', @contestBaseUrl + cont[0], cont[0], 'Первый' + fullText, 'reg' + year)
             @processContest(year * 10 + 2, '', @contestBaseUrl + cont[1], cont[1], 'Второй' + fullText, 'reg' + year)


Move custom element specs to up.change

Move custom element specs to up.change

--- spec_app/spec/javascripts/up/fragment_spec.js.coffee
+++ spec_app/spec/javascripts/up/fragment_spec.js.coffee
@@ -958,6 +958,49 @@
               expect(text0).toEqual('')
               expect(text1).toEqual('')

+      describeCapability 'canCustomElements', ->
+
+        describe 'custom elements', ->
+
+          beforeAll ->
+            TestComponent = ->
+              instance = Reflect.construct(HTMLElement, [], TestComponent)
+              instance.innerHTML = 'component activated'
+              up.emit('test-component:new')
+              instance
+            Object.setPrototypeOf(TestComponent.prototype, HTMLElement.prototype)
+            Object.setPrototypeOf(TestComponent, HTMLElement)
+
+            window.customElements.define('test-component-activation', TestComponent)
+
+          it 'activates custom elements in inserted fragments', asyncSpec (next) ->
+            fixture('.target')
+
+            up.change '.target', content: """
+              <test-component-activation></test-component-activation>
+              """
+
+            next =>
+              expect('.target test-component-activation').toHaveText('component activated')
+
+          it 'does not activate custom elements outside of inserted fragments (for performance reasons)', asyncSpec (next) ->
+            fixture('.target')
+            constructorSpy = jasmine.createSpy('constructor called')
+            up.on('test-component:new', constructorSpy)
+
+            up.change '.target', document: """
+

+                <test-component-activation></test-component-activation>
+

+

+                <test-component-activation></test-component-activation>
+

+              """
+
+            next =>
+              expect(constructorSpy.calls.count()).toBe(1)
+
+

     ##############################################################################
     ##############################################################################
@@ -1604,61 +1647,6 @@
                 done()

-          if up.browser.canCustomElements()
-
-            describe 'custom elements', ->
-
-              beforeAll ->
-                TestComponent = ->
-                  instance = Reflect.construct(HTMLElement, [], TestComponent)
-                  instance.innerHTML = 'text from component'
-                  up.emit('test-component:new')
-                  instance
-                Object.setPrototypeOf(TestComponent.prototype, HTMLElement.prototype)
-                Object.setPrototypeOf(TestComponent, HTMLElement)
-
-                window.customElements.define('test-component-activation', TestComponent)
-
-              it 'activates custom elements in inserted fragments', asyncSpec (next) ->
-                @responseText = """
-

-                    <test-component-activation></test-component-activation>
-

-                  """
-
-                promise = up.replace('.middle', '/path')
-
-                next =>
-                  @respond()
-                  next.await(promise)
-
-                next =>
-                  expect('.middle test-component-activation').toHaveText('text from component')
-
-              it 'does not activate custom elements outside of inserted fragments', asyncSpec (next) ->
-                constructorSpy = jasmine.createSpy('constructor called')
-                up.on('test-component:new', constructorSpy)
-
-                @responseText = """
-

-                    <test-component-activation></test-component-activation>
-

-

-                    <test-component-activation></test-component-activation>
-

-

-                    <test-component-activation></test-component-activation>
-

-                  """
-
-                promise = up.replace('.middle', '/path')
-
-                next =>
-                  @respond()
-                  next.await(promise)
-
-                next =>
-                  expect(constructorSpy.calls.count()).toBe(1)

         describe 'with { restoreScroll: true } option', ->


Don't append '.' to autocompleted items if they are just substrings of other items

--- static/coffee/completions.coffee
+++ static/coffee/completions.coffee
@@ -111,7 +111,7 @@

           hasMore = false
           for option in ac.variables
-            hasMore = valueForName and option.name.indexOf(valueForName) is 0 and option.name isnt valueForName
+            hasMore = valueForName and option.name.indexOf(valueForName + '.') is 0 and option.name isnt valueForName
             if hasMore
               break


Client: Mock: Added mockChannel, mockMessage and MockThread methods to mockingjay

--- client/mocks/mockingjay.coffee
+++ client/mocks/mockingjay.coffee
@@ -7,6 +7,9 @@
 mockjaccount   = require './mock.jaccount'
 mockjmachine   = require './mock.jmachine'
 mockjworkspace = require './mock.jworkspace'
+mockMessage    = require 'app/util/generateDummyMessage'
+mockChannel    = require 'app/util/generateDummyChannel'
+mockThread     = require 'app/util/generateDummyThread'

 mockMachine    = new Machine { machine: mockjmachine }
 { socialapi }  = kd.singletons
@@ -194,3 +197,11 @@
   getMockWorkspace: -> return mockjworkspace

   getMockAccount: ->   return mockjaccount
+
+  getMockMessage: (args...) -> return mockMessage(args...)
+
+  getMockChannel: (args...) -> return mockChannel(args...)
+
+  getMockThread: (args...)  ->  return mockThread(args...)
+
+


Delete example.coffee

Delete example.coffee

--- example.coffee
+++ example.coffee
@@ -1,15 +0,0 @@
-x = (param)-->
-  console.log param
-  yield 'a'
-  y = yield 'b'
-  console.log "result:", y
-  yield 'c'
-  return 'end'
-
-g = x('hello world')
-
-console.log g.next() # 'a'
-console.log g.next() # 'b'
-# pass arg as return of 2nd yield;
-console.log g.next({prop: 'foobar'}) # 'c'
-console.log g.next() # 'end'


Extract duplicates into separate functions.

--- example.coffee
+++ example.coffee
@@ -8,9 +8,23 @@
 exampleName = argv._[0] or throw "Specify the example name. Available examples: #{examplesList}"
 app = express()

+readFile = (filename, coffee = true)->
+  source = fs.readFileSync(fs.realpathSync("#{__dirname}/#{filename}", "utf-8")).toString()
+  if coffee then coffeescript.compile source, literate: true else source
+
+readCoffeeFile = (filename)-> readFile filename, true
+readRegularFile = (filename)-> readFile filename, false
+
+sendData = (res, data, contentType)->
+  res.set 'Content-Type', contentType
+  res.send data
+
+sendHTML =   (res, data, handler = (i)-> i )-> sendData res, handler(data), 'text/html'
+sendJS =     (res, data, handler = (i)-> i )-> sendData res, handler(data), 'application/javascript'
+sendCoffee = (res, data, handler = (i)-> i )-> sendJS   res, handler(data)
+
 app.get '/', (req, res)->
-  res.set('Content-Type', 'text/html')
-  res.send '
+  sendHTML res, '
 <!DOCTYPE html>
 <html>
 <head>
@@ -47,14 +61,10 @@
 '

 app.get '/jnoid.js', (req, res)->
-  source = fs.readFileSync(fs.realpathSync("#{__dirname}/jnoid.coffee.md", "utf-8")).toString()
-  res.set('Content-Type', 'text/javascript')
-  res.send coffeescript.compile source, literate: true
+  sendCoffee res, "jnoid.coffee.md", readCoffeeFile

 app.get '/example.js', (req, res)->
-  source = fs.readFileSync(fs.realpathSync("#{__dirname}/examples/#{exampleName}.coffee.md", "utf-8")).toString()
-  res.set('Content-Type', 'text/javascript')
-  res.send coffeescript.compile source, literate: true
+  sendCoffee res, "examples/#{exampleName}.coffee.md", readCoffeeFile

 app.get '/check-login/:login', (req, res)->
   if _.contains ['Peter', 'Jack', 'Jackson', 'Samuel'], req.params.login


Reorganize

Reorganize

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -9,14 +9,6 @@
   constructor: (@element) ->
     @selectionObserver = new Trix.SelectionObserver
     @selectionObserver.delegate = this
-
-  selectionDidChange: (domRange) ->
-    @updateCurrentLocationRange(domRange)
-    @delegate?.locationDidChange?(@currentLocationRange)
-
-  updateCurrentLocationRange: (domRange) ->
-    domRange ?= @getDOMRange()
-    @currentLocationRange = @createLocationRangeFromDOMRange(domRange)

   getLocationRange: ->
     @lockedLocationRange ? @currentLocationRange
@@ -42,38 +34,17 @@
     @setDOMRange(range)
     range

-  getLocationRangeAtPoint: ([pageX, pageY]) ->
-    if document.caretPositionFromPoint
-      {offsetNode, offset} = document.caretPositionFromPoint(pageX, pageY)
-      domRange = document.createRange()
-      domRange.setStart(offsetNode, offset)
+  # Selection observer delegate

-    else if document.caretRangeFromPoint
-      domRange = document.caretRangeFromPoint(pageX, pageY)
+  selectionDidChange: (domRange) ->
+    @updateCurrentLocationRange(domRange)
+    @delegate?.locationDidChange?(@currentLocationRange)

-    else if document.body.createTextRange
-      range = document.body.createTextRange()
-      range.moveToPoint(pageX, pageY)
-      range.select()
-      return @updateCurrentLocationRange()
+  # Private

-    if domRange
-      @createLocationRangeFromDOMRange(domRange)
-
-  getPointAtEndOfSelection: ->
-    if range = @getDOMRange()
-      rects = range.getClientRects()
-      if rects.length > 0
-        rect = rects[rects.length - 1]
-
-        pageX = rect.right
-        pageY = rect.top + rect.height / 2
-
-        if @clientRectIsRelativeToBody()
-          pageX -= document.body.scrollLeft
-          pageY -= document.body.scrollTop
-
-        [pageX, pageY]
+  updateCurrentLocationRange: (domRange) ->
+    domRange ?= @getDOMRange()
+    @currentLocationRange = @createLocationRangeFromDOMRange(domRange)

   getDOMRange: ->
     selection = window.getSelection()
@@ -99,8 +70,6 @@
     selection = window.getSelection()
     selection.removeAllRanges()
     selection.addRange(range)
-
-  # Private

   createLocationRangeFromDOMRange: (range) ->
     return unless range?
@@ -165,6 +134,39 @@
     else
       NodeFilter.FILTER_SKIP

+  getLocationRangeAtPoint: ([pageX, pageY]) ->
+    if document.caretPositionFromPoint
+      {offsetNode, offset} = document.caretPositionFromPoint(pageX, pageY)
+      domRange = document.createRange()
+      domRange.setStart(offsetNode, offset)
+
+    else if document.caretRangeFromPoint
+      domRange = document.caretRangeFromPoint(pageX, pageY)
+
+    else if document.body.createTextRange
+      range = document.body.createTextRange()
+      range.moveToPoint(pageX, pageY)
+      range.select()
+      return @updateCurrentLocationRange()
+
+    if domRange
+      @createLocationRangeFromDOMRange(domRange)
+
+  getPointAtEndOfSelection: ->
+    if range = @getDOMRange()
+      rects = range.getClientRects()
+      if rects.length > 0
+        rect = rects[rects.length - 1]
+
+        pageX = rect.right
+        pageY = rect.top + rect.height / 2
+
+        if @clientRectIsRelativeToBody()
+          pageX -= document.body.scrollLeft
+          pageY -= document.body.scrollTop
+
+        [pageX, pageY]
+
   # ClientRect position properties should be relative to the viewport,
   # but in some browsers (like mobile Safari), they're relative to the body.
   clientRectIsRelativeToBody: memoize ->


Fix manual folding of image with child. Fix #406

--- client/message.coffee
+++ client/message.coffee
@@ -389,7 +389,7 @@
   ## Image gets unnaturally folded if it's referenced at least once
   ## and doesn't have any children (don't want to hide children, and this
   ## can also lead to infinite loop if children has the image reference).
-  messageFolded.set id, images[id].naturallyFolded or
+  messageFolded.set id, true if images[id].naturallyFolded or
     (not images[id].children and
      (images[id].count > 0 or
       (imagesInternal[images[id].file]? and


loading game descriptions from json now

loading game descriptions from json now

--- web/scripts/codeland.coffee
+++ web/scripts/codeland.coffee
@@ -4,6 +4,7 @@
 root.UIcont = null

 root.initialize = (UIcont) ->
+    root.loadGameDescriptions()
     root.UIcont = UIcont
     player = root.getPlayer()
     root.drawGameMap(player)
@@ -127,149 +128,23 @@
     root.store("CurrentPlayer", player)
     return

-
-_sequence1 = {
-    name : 'Code Sequence Puzzle #1'
-    description : 'Find the correct sequence of code lines'
-    task : 'Re-order existing code that only uses a small set of statements'
-    tags : 'intro'
-    editor : {
-        freeformEditting: false
-        buttons : ['switchUp','switchDown']
-        commands : {
-            go : { inputs:0, maxUses:8 }
-            turnRight : { inputs:0, maxUses:1 }
-        }
-    }
-    code : {
-        prefix: ''
-        postfix: ''
-        show: false
-        initial: 'go();\ngo();\ngo();\ngo();\ngo();\nturnRight();\ngo();\ngo();\ngo();'
-    }
-    events: {
-        victory: [7 ,5]
-    }
-    game : {
-        startpos : [1, 1]
-        targetpos : [5, 5]
-        characters: {
-            protagonist: {
-                dir: 1
-                index: 0
-            }
-            # guard: {
-            #     index: 2
-            #     x: 3
-            #     y: 0
-            #     dir: 2
-            #     ai: {
-            #         move: (gamestate) =>
-            #             [newx, newy] = gameState.computeStepInDirection(
-            #                 @dir, @x, @y)
-            #             hitEvent = gamestate.checkEvent(@x, @y)
-            #             if !hitEvent
-            #                 @x = newx
-            #                 @y = newy
-            #                 gamestate.gameVisual.gridMove @index, 1
-            #             else
-            #                 @dir = @dir ^ 2
-            #             return
-            #     }
-            # }
-        }
-    }
-    visual : {
-        gameType: "grid",
-        grid: {
-            gridUnit: 30,
-            border: 30,
-            gridX: 10,
-            gridY: 10
-        },
-        animation: {
-            length: 30,
-            pixMoveRate: 1
-        },
-        characters: {
-            protagonist: {
-                dir: 1,
-                imgSet: 0,
-                # x: 0,
-                # y: 0,
-                xOff: 2,
-                yOff: 2,
-                xSize: 26,
-                ySize: 26
-            }
-            gflag: {
-                dir: 0,
-                imgSet: 1,
-                x: 7,
-                y: 5,
-                xOff: 2,
-                yOff: 2,
-                xSize: 26,
-                ySize: 26
-            }
-            # guard: {
-            #     imgSet: 1,
-            #     xOff: 2,
-            #     yOff: 2,
-            #     xSize: 26,
-            #     ySize: 26
-            # }
-        }
-    }
-    help : [ 'Oops your go statements and turnRight statementsneed to be in the correct sequence of operations to solve this puzzle',
-    'Use up, down reset and test buttons to fix your code!','The reset button changes the code back to the original version']
-    dyk : [ 'Computer programs contain many statements.','Most of the time we need to write a ; after each statement','When the computer executes your program, it executes one statement at a time before executing the next one.','Computer programs are a like movie scripts and cooking recipies - the correct sequence matters!']
-}
-
-
-_sequence2= deepcopy _sequence1
-_sequence2.name = 'Code Sequence Puzzle #2'
-_sequence2.game.startpos = [6,7]
-_sequence2.game.targetpos = [5,5]
-_sequence2.editor.commands = {go : { inputs:0, maxUses:7 }, turnLeft : { inputs:0, maxUses:4 }}
-_sequence2.code.initial = 'go();\ngo();\ngo();\ngo();\ngo();\ngo();\n\nturnLeft();\nturnLeft();\nturnLeft();\ngo();'
-_sequence2.depends = ['sequence1']
-_sequence2.help = ['Perhaps if you go a bit too far you can end at the target square?' ]
-_sequence2.dyk = [ 'When the Java compiler reads our program it looks for semicolons', 'So we could write our entire program on just one line but that would be very difficult for people to read!']
-
-
-_sequence3= deepcopy _sequence2
-_sequence3.name = 'Code Sequence Puzzle #3'
-_sequence3.editor.buttons = ['delete']
-_sequence3.editor.commands = {go : { inputs:0, maxUses:3 },goNorth : { inputs:0, maxUses:2 }, turnLeft : { inputs:0, maxUses:6 }, turnRight: { inputs:0, maxUses:2 }}
-_sequence3.code.initial = 'turnLeft();\nturnRight();\nturnLeft();\nturnRight();go();\ngoNorth();\ngo();\nturnLeft();\nturnLeft();\nturnLeft();\nturnLeft();\ngo();\ngoNorth();'
-_sequence3.game.startpos = [7,3]
-_sequence3.game.targetpos = [5,2]
-_sequence3.depends = ['sequence1']
-_sequence3.help = ['Time to debug this code!','This code has too many statements. Delete the unnecessary statements.']
-_sequence3.dyk = ['When your code runs but does not work correctly you have a bug in your code!','Finding the cause and fixing the problem is called \'Debugging\'',]
-_sequence2.depends = ['sequence1']
-
-_sequence4= deepcopy _sequence3
-_sequence4.name = 'Code Sequence Puzzle #4'
-#goEast4.goSouth1.goWest2.
-_sequence4.editor.buttons = ['switchUp','switchDown' , 'delete','mysteryA', 'mysteryB', 'mysteryC']
-_sequence4.game.startpos = [2,4]
-_sequence4.game.targetpos = [6,8]
-_sequence4.editor.commands = {mysteryA : { inputs:0, maxUses:5 }, mysteryB : { inputs:0, maxUses:5 }, mysteryC : { inputs:0, maxUses:5 }}
-_sequence4.code.initial = ''
-_sequence4.depends = ['sequence3']
-_sequence4.help = ['Time to play! Write some test programs to discover out what each mystery function does']
-_sequence4.dyk = []
-
+root.loadGameDescriptions = () ->
+    jQuery.ajax({
+        dataType: "json",
+        url: 'config/quest1.json',
+        async: false,
+        success: (data) ->
+            root.gameDescriptions = data
+            return
+    });
+    return

 root.getGameDescriptions = ->
-    @gameDescriptions ?= {
-        sequence1 : _sequence1
-        sequence2 : _sequence2
-        sequence3 : _sequence3
-        sequence4 : _sequence4
-    }
+    if root.gameDescriptions?
+        return root.gameDescriptions
+    root.loadGameDescriptions()
+    return root.gameDescriptions
+
 root.getGameSequence = ->
     return @gameSequence if @gameSequence
     @gameSequence = []


restore url if user not found

restore url if user not found

--- coffee/services/redmine.coffee
+++ coffee/services/redmine.coffee
@@ -207,8 +207,11 @@
    find user from url.
   ###
   findUser: (success, error) ->
+    orgUrl = @auth.url
     @auth.url = @auth.url + '/'
-    @_findUser(success, error)
+    @_findUser(success, (param...) =>
+      @auth.url = orgUrl
+      error(param...))

   ###


Git rid of file.

Git rid of file.

--- examples/google-search/client.coffee
+++ examples/google-search/client.coffee
@@ -1,14 +0,0 @@
-searchContainer = bone.view '.search-container',
-    refresh: (root, listings) ->
-        $listings = $(root).find('ul.listings')
-        $listings.html ''
-        $('<li>').appendTo($root).html fact
-    search: (root, event) ->
-        fragment = $(root).find('input.search').val()
-        bone.io.get('listings').emit 'search', fragment
-    events:
-        'keypress input.search': 'search'
-
-bone.io.route 'listings',
-    results: (results, listings) ->
-        searchContainer.refresh listings


Ensure fetch options are defined

Ensure fetch options are defined

--- src/scripts/models/search-results.coffee
+++ src/scripts/models/search-results.coffee
@@ -54,15 +54,14 @@

       return @

-    fetch: () ->
-      fetchArgs = arguments[0]
-      if fetchArgs isnt undefined
-        fetchArgs.timeout = 60000 # one minute
+    fetch: (options = {}) ->
+      options.timeout or= 1000 * 60 # 1 minute
+
       # Reset search results
       @clear({silent: true}).set(@defaults)
       @set('loaded', false)

-      return super(fetchArgs)
+      return super(options)
       .always () =>
         @set('loaded', true)
         @unset('promise', {silent: true})


If we get an offset before play, use it when the player connects

--- src/streammachine/outputs/sockets.coffee
+++ src/streammachine/outputs/sockets.coffee
@@ -26,7 +26,7 @@
                     rewind:     v.rewind
                     socket:     sock
                     listener:   null
-                    offset:     0
+                    offset:     1
                 }

                 # add offset listener
@@ -38,6 +38,7 @@
                         s.listener.setOffset(i)
                         s.offset = s.listener._playHead
                     else
+                        # just set it on the socket.  we'll use it when they connect
                         s.offset = s.rewind.checkOffset i

                     fn?(s.offset / s.rewind.framesPerSec)
@@ -52,7 +53,7 @@
                     sock.emit "timecheck"
                         time:       new Date
                         buffered:   v.rewind.bufferedSecs()
-                , 2000)
+                , 5000)

     #----------

@@ -60,20 +61,20 @@
         requrl = url.parse(req.url,true)

         if requrl.query.socket? && sess = @sessions[requrl.query.socket]
-            listen = new Sockets.Listener sess,req,res,sess.rewind
+            listen = new Sockets.Listener sess,req,res,sess.rewind,sess.offset
             sess.listener = listen
             console.log "wired listener to session #{sess.id}"

     #----------

     class @Listener
-        constructor: (session,req,res,rewind) ->
+        constructor: (session,req,res,rewind,offset=1) ->
             @req = req
             @res = res
             @rewind = rewind

             # set our internal offset to be live by default
-            @_offset = 1
+            @_offset = offset
             @_playHead = 1

             console.log "req is ", req.headers


add bottom border to active language toggle

--- components/layout/index.coffee
+++ components/layout/index.coffee
@@ -24,12 +24,17 @@
       copy: footerData
       language: language

+
   render(DEFAULT_LANGUAGE)

   toggleLanguageOnClick = (e) ->
     e.preventDefault()
+
     language = $(e.currentTarget).data('language')
-    render(language)
+    render language
+
+    $('.nav-items a.is-active').removeClass('is-active')
+    $(".nav-items a[data-language='#{language}'").addClass('is-active')

   $('body').on 'click', '.js-language-toggle', toggleLanguageOnClick


Update the Copy Workflow button (#5961)

Update the Copy Workflow button (#5961)

Copy workflow steps, prioritized andgrouped (if defined) when cloning a workflow. Set the new workflow's primary language to the same language as the original.

--- app/pages/lab/workflow-create-form.cjsx
+++ app/pages/lab/workflow-create-form.cjsx
@@ -26,12 +26,15 @@

     newWorkflow =
       display_name: @refs.newDisplayName.value
-      primary_language: counterpart.getLocale()
+      primary_language: workflowToClone?.primary_language
+      steps: workflowToClone?.steps ? undefined
       tasks: workflowToClone?.tasks ? {}
       first_task: workflowToClone?.first_task ? ''
       configuration: workflowToClone?.configuration ? {}
       retirement: workflowToClone?.retirement ? {}
       active: @props.workflowActiveStatus ? false
+      grouped: workflowToClone?.grouped ? false
+      prioritized: workflowToClone?.prioritized ? false

     awaitSubmission = @props.onSubmit(@props.projectID, newWorkflow)


graph titles

graph titles

--- script.coffee
+++ script.coffee
@@ -27,6 +27,7 @@
       rollPeriod: 1
       showRoller: true
       strokeWidth: 1
+      title: @name
     )

   create: ->
@@ -81,7 +82,11 @@
       for u in v.urls
         divid = u.url.replace(/\./g, '_') # '.' in id breaks jQuery!
         $("#menu-container>ul>li[id=#{k}]>ul")
-          .append("
#{u.year} #{u.month}
")
+          .append("
#{u.year}-#{u.month}
")
     $('#menu-container a').click ->
       # TODO move remaining code into Graph method
       anchor = this
@@ -95,7 +100,7 @@

loading

         event.preventDefault()
         $(this).parent().remove()
-        $("#menu-container a[data-div-id=#{$(this).attr('data-div-id')}]").removeClass('selected')
+        dataDivId = $(this).attr('data-div-id')
+        $("#menu-container a[data-div-id=#{dataDivId}]").removeClass('selected')

       $(this).toggleClass('selected')


chore (env) Election day 2

chore (env) Election day 2

todo: move dates out from git eg. to an injected file

--- app/scripts/services/environment.coffee
+++ app/scripts/services/environment.coffee
@@ -11,10 +11,10 @@
       moment('2015-12-14 17:00', 'YYYY-MM-DD HH:mm').utcOffset('+0200')

     @electionSignInStartsAt = do ->
-      moment('2015-12-15 09:00', 'YYYY-MM-DD HH:mm').utcOffset('+0200')
+      moment('2015-12-16 09:00', 'YYYY-MM-DD HH:mm').utcOffset('+0200')

     @electionSignInEndsAt = do ->
-      moment('2015-12-15 20:00', 'YYYY-MM-DD HH:mm').utcOffset('+0200')
+      moment('2015-12-16 17:00', 'YYYY-MM-DD HH:mm').utcOffset('+0200')

     @isEligibilityActive = ->
       moment().isAfter(@eligibilitySignInStartsAt) &&


Bug fix

Bug fix

--- server.coffee
+++ server.coffee
@@ -26,6 +26,6 @@
 app.get '/items', (req, res) ->
   getData 'items', req, res

-app.listen 3000, ->
+app.listen process.env.PORT || 3000, ->
   console.log 'Listening on port 3000!'
   return


Fix danmaku report messages

Fix danmaku report messages

--- server.coffee
+++ server.coffee
@@ -599,6 +599,7 @@

   say: (text, address, attributes = {}, callback = false) =>
     self = this
+    id = self.last_record_id + 1
     uid = get_ip_with_tripcode(address)
     timestamp = (new Date()).getTime()

@@ -609,7 +610,7 @@
     speed = attributes.speed || 1.0

     item =
-      id: self.last_record_id + 1
+      id: id
       text: text
       top: top
       color: color.split(',')
@@ -631,7 +632,7 @@
       tags = self.metadata.tags
       nodeirc.say("\u000306Now playing: \u000314#{artist} - #{title} (#{tags})")
       self.metadata_updated = false
-    nodeirc.action("\u000306[#{uid}] \u000314#{text}")
+    nodeirc.action("\u000306~#{uid} \u000310##{id} \u000314#{text}")

   system_say: (text, attributes = {}) =>
     self = this
@@ -639,7 +640,7 @@
     attributes.size ||= 0.75
     attributes.speed ||= 0.75
     attributes.address ||= '127.0.0.1'
-    self.say text, attributes.address, attributes
+    self.say(text, attributes.address, attributes)

   ban: (uid, by_, reason, duration = 3600, message = '') =>
     self = this
@@ -650,14 +651,14 @@
       moderator: by_
       timestamp: new_timestamp
     duration_hours = parseInt((new_timestamp - now_time) / 3600 / 1000)
+    self.system_say("“#{message}” has been banned #{duration_hours} hours. (#{reason})") if message
     nodeirc.action("\u000301,04#{uid} has been banned #{duration_hours} hours. (#{reason} by #{by_})")
-    self.system_say("“#{message}” has been banned #{duration_hours} hours. (#{reason})") if message
     self.saveBlacklist()

   unban: (uid, by_) =>
     self = this
     delete self.blacklist[uid] if self.blacklist[uid]
-    nodeirc.action("\u000303#{uid} has been unbanned.")
+    nodeirc.action("\u000301,03#{uid} has been unbanned.")
     self.saveBlacklist()

   createRoutes: =>
@@ -711,7 +712,7 @@
             ret.status = error
         WARN('%s (%s)', error, ip)
         if self.last_blacklist_uid != uid
-          nodeirc.action("\u000306[#{uid}]\u000301,01 #{text} \u000304(#{error})")
+          nodeirc.action("\u000306~#{uid} \u000310#BAKA \u000301,01 #{text} \u000304(#{error})")
         self.last_blacklist_uid = uid
       res.send(jsonp_stringify(ret, callback))

@@ -767,17 +768,17 @@
           target_message = item.text
           throw 'Target UID Not Found' unless self.fragtable[target_uid]
           if self.fragtable[target_uid].flag[uid]
-            nodeirc.action("\u000307#{target_uid} has been reported by #{uid})")
+            nodeirc.action("\u000307#{target_uid} has been reported by #{uid} (duplicated)")
           else
             self.fragtable[target_uid].flag[uid] = 1
             if object_length(self.fragtable[target_uid].flag) >= 2
-              self.ban(target_uid, uid, 'Online Report', 86400, target_message)
+              self.ban(target_uid, uid, "Online Report by #{uid}", 86400, target_message)
             else
               self.system_say(
-                "“#{target_message}” is reported by #{uid}",
-                color: '255,128,0,0.9',
-                address: ip
+                "“#{target_message}” has been reported (Online Report by #{uid})",
+                color: '255,128,0,0.9'
               )
+              nodeirc.action("\u000301,07“#{target_message}” has been reported by #{uid}")
         catch error
           switch error
             when 'BLACKLIST'


Update _player.coffee

Update _player.coffee

--- coffee/_player.coffee
+++ coffee/_player.coffee
@@ -27,6 +27,8 @@
 spinner_cover = null

 isBusyBuffering = 0
+
+isRepeat = null

 PlayNext = (artist, title, success) ->
   $.each __playerTracklist, (i, track) ->
@@ -241,7 +243,13 @@
       .find('.info .track-info .action i.pause')
       .show()

-    notify.NowPlaying(__currentTrack.artist, __currentTrack.title)
+    cover = playerContainer.find(".cover").css("background-image")
+
+    link = cover.replace(/.*\s?url\([\'\"]?/, "").replace(/[\'\"]?\).*/, "")
+
+    if isRepeat == false
+      notify.emit("Now Playing",
+        {body: __currentTrack.artist + ' - ' + __currentTrack.title,link: link})

   @.on 'pause', ->
     playerContainer
@@ -262,6 +270,7 @@
     ).send()

     alertify.alert 'Playback Error (' + video_error_codes[code] + ')'
+    notify.emit("Playback Error: ", {body: video_error_codes[code]})
     PlayNext(__currentTrack.artist, __currentTrack.title)

 playerContainer.find('.volume-bg').ready ->


Added animation on volume and progress click

--- coffee/_player.coffee
+++ coffee/_player.coffee
@@ -175,7 +175,7 @@
 playerContainer.find('.progress-bg').on 'click', (e) ->
     percentage = (e.pageX - $(this).offset().left) / $(this).width()
     videojs('video_player').currentTime(percentage * videojs('video_player').duration())
-    playerContainer.find('.progress-current').css({'width': (percentage) * 100 + '%'})
+    playerContainer.find('.progress-current').animate({'width': (percentage) * 100 + '%'})

 playerContainer.find('.volume-bg').on 'click', (e) ->
     percentage = (e.pageX - $(this).offset().left) / $(this).width()
@@ -183,7 +183,7 @@
     playerContainer.find('.volume-icon').attr("data-ismuted", 0)
     playerContainer.find('.volume-icon').find("i").removeClass("fa-volume-off").addClass("fa fa-volume-up")
     videojs('video_player').volume(percentage)
-    playerContainer.find('.volume-current').css({'width': (percentage) * 100 + '%'})
+    playerContainer.find('.volume-current').animate({'width': (percentage) * 100 + '%'})

 playerContainer.find('.track-info .backward').on 'click', (e) ->
     PlayPrevious(__currentTrack.artist, __currentTrack.title)


add dummy player data

add dummy player data

--- coffee/players.coffee
+++ coffee/players.coffee
@@ -8,61 +8,73 @@
       "name":"Thibault Courtois"
       "position": "Goalkeeper"
       "number": "1"
+      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
     },
     "fabregas": {
       "name":"Cesc Fabregas"
       "position": "Attacking Midfielder"
       "number": "4"
+      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
     },
     "drogba": {
       "name":"Didier Drogba"
       "position": "Center Forward"
       "number": "11"
+      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
     },
     "terry": {
       "name":"John Terry"
       "position": "Center Fullback"
       "number": "26"
+      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
     },
     "casillas": {
       "name":"Iker Casillas"
       "position": "Goalkeeper"
       "number": "1"
+      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
     },
     "ramos": {
       "name":"Sergio Ramos"
       "position": "Center Fullback"
       "number": "4"
+      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
     },
     "bale": {
       "name":"Gareth Bale"
       "position": "Winger"
       "number": "11"
+      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
     },
     "ronaldo": {
       "name":"Cristiano Ronaldo"
       "position": "Center Forward"
       "number": "7"
+      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
     },
     "abbiati": {
       "name":"Christian Abbiati"
       "position": "Goalkeeper"
       "number": "1"
+      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
     },
     "alex": {
       "name":"Alex"
       "position": "Center Fullback"
       "number": "33"
+      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
     },
     "essien": {
       "name":"Michael Essien"
       "position": "Midfielder"
       "number": "15"
+      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
     },
     "torres": {
       "name":"Fernado Torres"
       "position": "Center Forward"
       "number": "9"
+      "copy" :"Francesc \"Cesc\" Fàbregas Soler is a Spanish footballer who plays for Chelsea in the Premier League, and the Spanish national team. Fàbregas plays mainly as a central midfielder or attacking midfielder but has also played as a winger or a second striker on some occasions."
     }
   }


submit notes forms by pressing ctrl+enter

--- app/assets/javascripts/notes.js.coffee
+++ app/assets/javascripts/notes.js.coffee
@@ -52,6 +52,12 @@

     # fetch notes when tab becomes visible
     $(document).on "visibilitychange", @visibilityChange
+
+    @notes_forms = '.js-main-target-form textarea, .js-discussion-note-form textarea'
+    $(document).on('keypress', @notes_forms, (e)->
+      if event.keyCode == 10 || (event.ctrlKey && event.keyCode == 13)
+        $(@).parents('form').submit()
+    )

   cleanBinding: ->
     $(document).off "ajax:success", ".js-main-target-form"
@@ -67,6 +73,7 @@
     $(document).off "click", ".js-discussion-reply-button"
     $(document).off "click", ".js-add-diff-note-button"
     $(document).off "visibilitychange"
+    $(document).off "keypress", @notes_forms

   initRefresh: ->


Fix translation

Fix translation

--- app/assets/javascripts/projects.coffee
+++ app/assets/javascripts/projects.coffee
@@ -44,12 +44,11 @@
   return

-inviteInitUser = ->
+inviteInitUser = (locale) ->
   count = 0
   $('body').on 'click', '#invite_user', ->
     email = $('#invitation_mail').val()
     if validateEmail(email)
-      language = getLanguage()
       $('#invitation_mail').val('')

       span = document.createElement("span")
@@ -58,11 +57,12 @@

       deleteButton = document.createElement("input")
       deleteButton.type = 'RemoveButton'
-      console.log language
-      if language == 'en'
+
+      if locale == 'en'
         deleteButton.value = 'Remove'
       else
         deleteButton.value = 'Entfernen'
+
       deleteButton.id = 'delete' + count
       deleteButton.name = 'deleteInvitationButton' + count

@@ -93,7 +93,7 @@

       return
     else
-      if language == 'en'
+      if locale == 'en'
         alert 'Please enter a valid email adress'
       else
         alert 'Bitte gibt eine valide Email-Adresse ein'
@@ -110,11 +110,6 @@
   re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
   re.test email

-getLanguage = ->
-  $.ajax '/users/language',
-    success: (res, status, xhr) ->
-      return language = res.msg
-
 ready = ->
   if $('#setInactiveButton').length
     sendLanguageWithButtonToCallback $('#setInactiveButton'), setInactiveWarning
@@ -123,7 +118,7 @@
   if $('.typeahead').length
     typeahead()
   if $('#invite_user').length
-    inviteInitUser()
+    sendLanguageWithButtonToCallback $('#invite_user'), inviteInitUser
   return


Allow middle-click on a Todo row to open in a new tab

--- app/assets/javascripts/todos.js.coffee
+++ app/assets/javascripts/todos.js.coffee
@@ -102,7 +102,8 @@
     todoLink = $(this).data('url')
     return unless todoLink

-    if e.metaKey
+    # Allow Meta-Click or Mouse3-click to open in a new tab
+    if e.metaKey or e.which is 2
       e.preventDefault()
       window.open(todoLink,'_blank')
     else


fix typo in default for direction property of arcs

--- static/coffee/renderers/glyph/arc.coffee
+++ static/coffee/renderers/glyph/arc.coffee
@@ -111,7 +111,7 @@
 Arc::display_defaults = _.clone(Arc::display_defaults)
 _.extend(Arc::display_defaults, {

-  diection: 'anticlock'
+  direction: 'anticlock'
   line_color: 'red'
   line_width: 1
   line_alpha: 1.0


load utils file before rest of javascripts

Transit.storage_* methods must be defined before geocode.js is loaded.

--- app/assets/transit/lib/index.js.coffee
+++ app/assets/transit/lib/index.js.coffee
@@ -4,6 +4,7 @@
 #= require_tree ./models
 #= require_tree ./collections
 #= require_tree ./views
+#= require ./utils
 #= require_tree .

 window.Transit =


more progress and fixups

more progress and fixups

--- src/base.iced
+++ src/base.iced
@@ -55,8 +55,6 @@

   set_error : (code, desc = null, fields = null) ->
     @oo.status = make_status_obj code, desc, fields
-    log.warn "set_error #{code} #{desc}" unless code is sc.OK
-    new Error code

   #-----------------------------------------

@@ -82,21 +80,10 @@
   #-----------------------------------------

   send_res_json : (cb) ->
-    @format_res()
     respond { obj : @oo, code : @http_out_code, encoding : @out_encoding, @res }
     @response_sent_yet = true
     cb()

-  #-----------------------------------------
-
-  format_res : ->
-    if @oo.status?.code
-      # noop
-    else if not @is_input_ok()
-      @set_error sc.INPUT_ERROR, "Error in JSON input", @_error_in_field
-    else
-      @set_ok()
-
   #==============================================

   handle : (cb) ->
@@ -141,6 +128,8 @@

   __check_inputs : () ->
     err = core.check_template @input_template(), @input, "HTTP"
+    if err?
+      err.sc = sc.INPUT_ERROR
     return err

   #------
@@ -154,8 +143,7 @@
   __handle_input : (cb) ->
     @input = @req.body
     @__set_out_encoding()
-    if (err = @__check_inputs())? then @set_error sc.INPUT_ERROR, err.message
-    else @set_ok()
+    @set_ok() unless (err = @__check_inputs())?
     cb err

   #------
@@ -178,6 +166,7 @@
       code = err.sc or sc.GENERIC_ERROR
       @set_error code, err.message
       @http_out_code = c if (c = err.http_code)?
+      log.warn "Error #{code}: #{err.message}"

   #------


Fetch report on report change and on load

--- app/js/lib/models/app.coffee
+++ app/js/lib/models/app.coffee
@@ -6,5 +6,11 @@
   initialize: (reports) ->
     @reports = new LedgerWeb.Collections.Reports(reports)

+    @on 'change:showReport', @fetchReport
+    @fetchReport()
+
+  fetchReport: =>
+    @currentReport().fetch()
+
   currentReport: ->
     @reports.at(@get('showReport'))


Remove commented-out specs

Remove commented-out specs

--- spec/unpoly/fragment_spec.js.coffee
+++ spec/unpoly/fragment_spec.js.coffee
@@ -4590,80 +4590,6 @@
               expect('.element').toHaveText('text from /path2')
               expect(change1Error).toBeUndefined()
               expect(up.network.queue.allRequests.length).toEqual(1)
-
-        describe 'with { abort: Function } option zzz', ->
-
-  #        it 'may be passed as a function that decides which existing requests are aborted', asyncSpec (next) ->
-  #          fixture('.element')
-  #
-  #          change1Promise = undefined
-  #          change1Error = undefined
-  #
-  #          change2Promise = undefined
-  #          change2Error = undefined
-  #
-  #          change3Promise = undefined
-  #          change3Error = undefined
-  #
-  #          soloFn = (request) ->
-  #            u.matchURLs(request.url, '/path1')
-  #
-  #          change1Promise = up.render('.element', url: '/path1', abort: false)
-  #          change1Promise.catch (e) -> change1Error = e
-  #          change2Promise = up.render('.element', url: '/path2', abort: false)
-  #          change2Promise.catch (e) -> change2Error = e
-  #
-  #          next =>
-  #            expect(up.network.queue.allRequests.length).toEqual(2)
-  #            expect(change1Error).toBeUndefined()
-  #            expect(change2Error).toBeUndefined()
-  #
-  #            change3Promise = up.render('.element', url: '/path3', abort: soloFn)
-  #            change3Promise.catch (e) -> change3Error = e
-  #
-  #          next =>
-  #            expect(change1Error).toBeAbortError()
-  #            expect(change2Error).toBeUndefined()
-  #            expect(change3Error).toBeUndefined()
-  #
-  #            expect(up.network.queue.allRequests.length).toEqual(2)
-  #
-  #        it 'may be passed as a function that decides which existing requests are aborted when updating from local content', asyncSpec (next) ->
-  #          fixture('.element1')
-  #          fixture('.element2')
-  #          fixture('.element3')
-  #
-  #          change1Promise = undefined
-  #          change1Error = undefined
-  #
-  #          change2Promise = undefined
-  #          change2Error = undefined
-  #
-  #          change3Promise = undefined
-  #          change3Error = undefined
-  #
-  #          change1Promise = up.render('.element1', url: '/path1')
-  #          change1Promise.catch (e) -> change1Error = e
-  #          change2Promise = up.render('.element2', url: '/path2')
-  #          change2Promise.catch (e) -> change2Error = e
-  #
-  #          next =>
-  #            expect(up.network.queue.allRequests.length).toEqual(2)
-  #            expect(change1Error).toBeUndefined()
-  #            expect(change2Error).toBeUndefined()
-  #
-  #            soloFn = (request) ->
-  #              u.matchURLs(request.url, '/path1')
-  #
-  #            change3Promise = up.render('.element3', content: 'new content', abort: soloFn)
-  #            change3Promise.catch (e) -> change3Error = e
-  #
-  #          next =>
-  #            expect(change1Error).toBeAbortError()
-  #            expect(change2Error).toBeUndefined()
-  #            expect(change3Error).toBeUndefined()
-  #
-  #            expect(up.network.queue.allRequests.length).toEqual(1)

       describe 'with { cache } option', ->


BF-119: Testing on server

BF-119: Testing on server

--- app/assets/javascripts/model/binary/pullqueue.coffee
+++ app/assets/javascripts/model/binary/pullqueue.coffee
@@ -73,6 +73,7 @@

     pullLoadingCount = @pullLoadingCount

+    console.log "requesting: ", [bucket_x, bucket_y, bucket_z]
     Cube.setBucketByAddress3(bucket_x, bucket_y, bucket_z, zoomStep, Cube.LOADING_PLACEHOLDER_OBJECT)
     pullLoadingCount++


Fix #141, don't show disabled/archived users in the UI for assigning users to a project

--- app/assets/javascripts/views/projects/show.js.coffee
+++ app/assets/javascripts/views/projects/show.js.coffee
@@ -135,7 +135,8 @@

   renderUnassignedUsers: ->
     # If there are users not assigned to this project in the current company, show them here
-    unassignedUsers = @model.getUnassignedUsers()
+    unassignedUsers = @model.getUnassignedUsers().select (user) ->
+      !user.get('membership').archived && !user.get('membership').disabled
     @$el.find('section.main').append StaffPlan.Templates.Projects.show.addSomeone
       unassignedUsers: unassignedUsers.map (u) -> u.attributes
       projectId: @model.id


Factor out a setQuestion method on QuestionView so it can be reused

--- app/assets/javascripts/views/question_view.js.coffee
+++ app/assets/javascripts/views/question_view.js.coffee
@@ -53,35 +53,25 @@
           @h5 'Discussion', class: 'column-header', outlet: 'discussionHeader'
           @subview 'discussion', new Views.DiscussionView(question.comments())

-  initialize: (@question) ->
-    @rankedItemsByAnswerId = {}
-    @body.text(question.body())
-
+  initialize: (question) ->
+    @subscriptions = new Monarch.Util.SubscriptionBundle
     @collectiveVote.buildItem = (answer) => @buildAnswerItem(answer, draggable: true)
-    @collectiveVote.setRelation(question.answers())
-
-    @updateVotingInstructions()
-    @subscriptions = new Monarch.Util.SubscriptionBundle
-    personalRankings = Models.User.getCurrent().rankingsForQuestion(question)
-
-    @subscriptions.add personalRankings.onInsert =>
-      @updateRankIndicators()
-      @updateVotingInstructions()
-
-    @subscriptions.add personalRankings.onUpdate =>
-      @updateRankIndicators()
-
-    @subscriptions.add personalRankings.onRemove =>
-      @updateRankIndicators()
-      @updateVotingInstructions()

     @personalVote.buildItem = (ranking) =>
       @buildAnswerItem(ranking.answer(), position: ranking.position())
+
     @personalVote.onInsert = (item, ranking) =>
       @rankedItemsByAnswerId[ranking.answerId()]?.remove()
       @rankedItemsByAnswerId[ranking.answerId()] = item
-
-    @personalVote.setRelation(personalRankings)
+      @updateRankIndicators()
+      @updateVotingInstructions()
+
+    @personalVote.onUpdate = =>
+      @updateRankIndicators()
+
+    @personalVote.onRemove = =>
+      @updateRankIndicators()
+      @updateVotingInstructions()

     removeItem = null
     @personalVote.sortable(
@@ -92,7 +82,20 @@
       stop: (event, ui) => @updateAnswerRanking(ui.item)
     )

+    @setQuestion(question)
+
+  setQuestion: (@question) ->
+    @subscriptions.destroy()
+    @rankedItemsByAnswerId = {}
+
+    @body.text(question.body())
+    question.getField('body').onChange (body) => @body.text(body)
+
+    @collectiveVote.setRelation(question.answers())
+
+    @personalVote.setRelation(Models.User.getCurrent().rankingsForQuestion(question))
     @allVotes.setRelation(@question.votes())
+    @updateVotingInstructions()

     @subscriptions.add @question.votes().onInsert => @updateShowAllVotesLink()
     @subscriptions.add @question.votes().onRemove => @updateShowAllVotesLink()
@@ -105,9 +108,6 @@
     unless @question.creator() == Models.User.getCurrent()
       @editButton.hide()
       @deleteButton.hide()
-
-    question.getField('body').onChange (body) =>
-      @body.text(body)

   buildAnswerItem: (answer, options) ->
     new Views.AnswerItem(answer, options)


Fix failure case

Fix failure case

--- app/src/tasks/operations_synchronization_task.coffee
+++ app/src/tasks/operations_synchronization_task.coffee
@@ -19,7 +19,7 @@
       d = ledger.defer()
       ledger.api.TransactionsRestClient.instance.refreshTransaction [operations[index]], (operation, error) =>
         if error?
-          failedOperations = failedOperations.concat(operation[0])
+          failedOperations = failedOperations.concat(operations[index])
         else
           updatedOperations = updatedOperations.concat(operation[0])
           ledger.tasks.TransactionConsumerTask.instance.pushTransaction(operation[0])
@@ -35,9 +35,10 @@
       do callback
     .fail (er) =>
       return $error("An unexpected error occurred during operation synchronization ") unless er.failedOperations?
-      ops = (op for op in (ops or operations) when @checkForDoubleSpent(op) is false)
-      @_retryNumber = Math.min(@_retryNumber + 1, 12)
-      _.delay (=> @synchronizeConfirmationNumbers ops, callback), ledger.math.fibonacci(@_retryNumber) * 500
+      _.defer =>
+        ops = (op for op in (ops or operations) when @checkForDoubleSpent(op) is false)
+        @_retryNumber = Math.min(@_retryNumber + 1, 12)
+        _.delay (=> @synchronizeConfirmationNumbers ops, callback), ledger.math.fibonacci(@_retryNumber) * 500
     .done()

   @reset: () ->


Adjust attributions

Adjust attributions

--- views/map.coffee
+++ views/map.coffee
@@ -67,7 +67,6 @@

"
     else '') + "
-

@@ -133,7 +132,7 @@
 $ ->
   map = L.map('map').setView([28.3, 20.8], 3)
   L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
-    attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox',
+    attribution: 'GitHub Project | Map data © OpenStreetMap, © Mapbox',
     maxZoom: 18,
     id: 'mapbox.streets-satellite',
     accessToken: $('#map').data().mapboxAccessToken


reenable zoom on drag

reenable zoom on drag

--- app/assets/transit/lib/views/plan.coffee
+++ app/assets/transit/lib/views/plan.coffee
@@ -25,7 +25,6 @@
       date: new Date()
       from: @map.get('from')
       to: @map.get('to')
-    @map.set fit_bounds: false, { silent: true }
     @model.trigger 'fetch'


fix(kanban): undefined role

fix(kanban): undefined role

--- app/coffee/modules/taskboard/main.coffee
+++ app/coffee/modules/taskboard/main.coffee
@@ -742,11 +742,12 @@
                 role = getRole(roleId)
                 point = getPoint(pointId) || { value: 0 }

-                if !result[role.id]
-                    result[role.id] = role
-                    result[role.id].points = 0
-
-                result[role.id].points += point.value
+                if role
+                    if !result[role.id]
+                        result[role.id] = role
+                        result[role.id].points = 0
+
+                    result[role.id].points += point.value

             return result
         , {}


Add support for FULLY_QUALIFY_ASSET_URL.

--- compiler/index.coffee
+++ compiler/index.coffee
@@ -143,6 +143,8 @@
                     asset_dest_directory    : asset_dest_directory
                     asset_cache_directory   : path.join(build_directory, '.asset-cache')

+                if project_config.FULLY_QUALIFY_ASSET_URL
+                    _prefix = "//#{ project_config.HOST }#{ _prefix }"

                 if asset_hash
                     global.ASSET_URL = "#{ _prefix }/assets/#{ asset_hash }/"


Clean up

Clean up

--- components/Hub.coffee
+++ components/Hub.coffee
@@ -1,5 +1,5 @@
-noflo = require("noflo")
-_ = require("underscore")
+noflo = require "noflo"
+_ = require "underscore"

 class Hub extends noflo.Component

@@ -12,12 +12,12 @@
       out: new noflo.ArrayPort

     @inPorts.in.on "begingroup", (group) =>
-      @outPorts.out.beginGroup(group)
+      @outPorts.out.beginGroup group

     @inPorts.in.on "data", (data) =>
-      @outPorts.out.send(data)
+      @outPorts.out.send data

-    @inPorts.in.on "endgroup", =>
+    @inPorts.in.on "endgroup", (group) =>
       @outPorts.out.endGroup()

     @inPorts.in.on "disconnect", =>


MainViewController: Fix LazyLoadThresoldReached issue

--- client/Main/maincontroller/mainviewcontroller.coffee
+++ client/Main/maincontroller/mainviewcontroller.coffee
@@ -29,22 +29,19 @@

     mainViewController = this
     window.onscroll = do ->
-      lastRatio = 0
-      threshold = 50
+      threshold  = 50
+      lastScroll = 0
+
       (event)->
         el = document.body
         {scrollHeight, scrollTop} = el

-        dynamicThreshold = if threshold > 1
-        then (scrollHeight - threshold) / scrollHeight
-        else threshold
-
-        ratio = (scrollTop + window.innerHeight) / scrollHeight
-
-        if dynamicThreshold < ratio > lastRatio
+        current = scrollTop + window.innerHeight
+        if current > scrollHeight - threshold
+          return if lastScroll > 0
           appManager.getFrontApp()?.emit "LazyLoadThresholdReached"
-
-        lastRatio = ratio
+          lastScroll = current
+        else if current < lastScroll then lastScroll = 0

   loadView:(mainView)->


Apps: Simplfy the display controller of apps

--- client/Social/Apps/ContentDisplays/controller.coffee
+++ client/Social/Apps/ContentDisplays/controller.coffee
@@ -2,24 +2,13 @@

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

-    options.view or= mainView = new KDView cssClass : 'apps content-display'
+    options.view or= mainView = new KDView cssClass : 'content-page appstore singleapp'

     super options, data

   loadView:(mainView)->

-    app = @getData()
-
-    mainView.addSubView subHeader = new KDCustomHTMLView
-      tagName  : "h2"
-      cssClass : 'sub-header'
-
-    mainView.addSubView appView = new AppView
-      cssClass : "profilearea clearfix"
+    mainView.addSubView appView = new AppDetailsView
+      cssClass : "app-details"
       delegate : mainView
-    , app
-
-    mainView.addSubView appView = new AppDetailsView
-      cssClass : "info-wrapper"
-      delegate : mainView
-    , app
+    , @getData()


DefineStackView: fix fat arrow on error handler for bootstrap

--- client/admin/lib/views/stacks/definestackview.coffee
+++ client/admin/lib/views/stacks/definestackview.coffee
@@ -141,7 +141,7 @@
     {credentialsData} = @credentialStatus
     [credential]      = credentialsData

-    failed = (err) ->
+    failed = (err) =>
       @outputView.handleError err
       callback err


Remove the __package__ stuff and with it the Dojo warning.

--- application/chrome/content/wesabe.coffee
+++ application/chrome/content/wesabe.coffee
@@ -1,10 +1,6 @@
 #
 # Provides the basic methods needed to load packages and any functions
 # useful enough to live in the core.
-#
-# NOTE: Much of the package stuff is drawn from Dojo, which is a project
-# that is arguably over-engineered and generally stuffy, but hopefully
-# taking a small slice out of it will not spread the seeds of doom.
 #

 if window? and not GLOBAL?
@@ -103,14 +99,11 @@
   #   # looks for files in this order:
   #   # wesabe/math/vector.js
   #   # wesabe/math.js
-  #   # wesabe/math/__package__.js
   #   wesabe.require("math.vector");
   #
   #   # specifying an asterisk last changes the order:
-  #   # wesabe/math/vector/__package__.js
   #   # wesabe/math/vector.js
   #   # wesabe/math.js
-  #   # wesabe/math/__package__.js
   #
   require: (module) ->
     return required[module].exports if required[module]
@@ -175,11 +168,9 @@
   # Transforms an array of module parts to a list of possible Uris.
   # @method _getUrisForParts
   # @param parts {Array} The list of module parts.
-  # @param lookForPackages {Boolean} Whether to look for a package file at all.
-  # @param preferPackages {Boolean} Whether to look for a package file first.
   # @private
   #
-  _getUrisForParts: (parts, scheme, lookForPackages, preferPackages) ->
+  _getUrisForParts: (parts, scheme) ->
     s = wesabe.baseUrlForScheme(scheme)
     u = (pp) ->
       base = [s].concat(pp).join('/')
@@ -188,23 +179,13 @@
     if parts.length is 0
       return []

-    if parts[parts.length-1] is '*'
-      parts.pop()
-      lookForPackages = preferPackages = true
-
     uris = []
-    # A/B/C/__package__.{js,coffee}
-    if lookForPackages
-      uris = uris.concat(u(parts.concat(['__package__'])))

     # A/B/C.{js,coffee}
-    if preferPackages
-      uris = uris.concat(u(parts))
-    else
-      uris = u(parts).concat(uris)
+    uris = u(parts).concat(uris)

     # start over with A/B
-    uris.concat(wesabe._getUrisForParts(parts[0...parts.length-1], scheme, true, false))
+    uris.concat(wesabe._getUrisForParts(parts[0...parts.length-1], scheme))

   #
   # Loads (and evals) the JS file at +uri+. Returns true on success.


neo4j: send [] to decorators if response from neo4j is empty

--- server/lib/server/graph/fetch.coffee
+++ server/lib/server/graph/fetch.coffee
@@ -1,3 +1,5 @@
+# TODO: refactor this ugliness
+
 module.exports = class FetchAllActivityParallel
   _              = require "underscore"
   async          = require "async"
@@ -22,31 +24,31 @@

   fetchSingles:(callback)->
     graph = new Graph @neo4j
-    graph.fetchAll @startDate, (err, rawResponse)->
+    graph.fetchAll @startDate, (err, rawResponse=[])->
       GraphDecorator.decorateSingles rawResponse, (decoratedResponse)->
         callback err, decoratedResponse

   fetchTagFollows: (callback)->
     graph = new Graph @neo4j
-    graph.fetchTagFollows @startDate, (err, rawResponse)->
+    graph.fetchTagFollows @startDate, (err, rawResponse=[])->
       GraphDecorator.decorateFollows rawResponse, (decoratedResponse)->
         callback err, decoratedResponse

   fetchMemberFollows: (callback)->
     graph = new Graph @neo4j
-    graph.fetchMemberFollows @startDate, (err, rawResponse)->
+    graph.fetchMemberFollows @startDate, (err, rawResponse=[])->
       GraphDecorator.decorateFollows rawResponse, (decoratedResponse)->
         callback err, decoratedResponse

   fetchInstalls: (callback)->
     graph = new Graph @neo4j
-    graph.fetchNewInstalledApps @startDate, (err, rawResponse)->
+    graph.fetchNewInstalledApps @startDate, (err, rawResponse=[])->
       GraphDecorator.decorateInstalls rawResponse, (decoratedResponse)->
         callback err, decoratedResponse

   fetchNewMembers: (callback)->
     graph = new Graph @neo4j
-    graph.fetchNewMembers @startDate, (err, rawResponse)->
+    graph.fetchNewMembers @startDate, (err, rawResponse=[])->
       GraphDecorator.decorateMembers rawResponse, (decoratedResponse)->
         callback err, decoratedResponse