fixes #80, add the children property implementation for List (#81)

--- src/list.coffee
+++ src/list.coffee
@@ -60,6 +60,10 @@
       return value
     set: (value) -> @set value, { force: true, suppress: true }

+  @property 'children',
+    get: ->
+      return if @state.value? then Array.from(@state.value.values()) else []
+
   constructor: ->
     super
     @state.value = switch
@@ -84,7 +88,7 @@
     @emit 'update', this unless opts.suppress
     @emit 'delete', item unless opts.suppress
     return this
-
+
   set: (value, opts={}) ->
     { force=false, replace=true, suppress=false } = opts
     @debug "[set] enter with:"


reinstated proxy check. close #3

reinstated proxy check. close #3

--- src/main.coffee
+++ src/main.coffee
@@ -55,22 +55,16 @@

 app.on 'ready', ->

-#    proxiesReturned = 0
-
-#    # Format of proxyURL is either "DIRECT" or "PROXY 127.0.0.1:8888"
-
-#    app.resolveProxy 'https://plus.google.com', (proxyURL) ->
-#        proxiesReturned++
-#        unless proxyURL is 'DIRECT'
-#            process.env.HTTPS_PROXY ?= "http://#{proxyURL.split(' ')[1]}"
-#        doFirstConnection() if proxiesReturned is 2
-
-#    app.resolveProxy 'http://plus.google.com', (proxyURL) ->
-#        proxiesReturned++
-#        unless proxyURL is 'DIRECT'
-#            process.env.HTTP_PROXY ?= "http://#{proxyURL.split(' ')[1]}"
-#        doFirstConnection() if proxiesReturned is 2
-    # The above should be uncommented as soon as we upgrade to a version of Electron that incorporates https://github.com/atom/electron/pull/2054
+    proxycheck = ->
+        todo = [
+           {url:'http://plus.google.com',  env:'HTTP_PROXY'}
+           {url:'https://plus.google.com', env:'HTTPS_PROXY'}
+        ]
+        Q.all todo.map (t) -> Q.Promise (rs) -> app.resolveProxy t.url, (proxyURL) ->
+            # Format of proxyURL is either "DIRECT" or "PROXY 127.0.0.1:8888"
+            [_, purl] = proxyURL.split ' '
+            process.env[t.env] ?= if purl then "http://#{purl}" else ""
+            rs()

     # Create the browser window.
     mainWindow = new BrowserWindow {
@@ -105,7 +99,7 @@

     # keeps trying to connec the hangupsjs and communicates those
     # attempts to the client.
-    reconnect = -> client.connect(creds)
+    reconnect = -> proxycheck().then -> client.connect(creds)

     # counter for reconnects
     reconnectCount = 0


use symbol to indicate stream end

use symbol to indicate stream end


Update stan.cson

Update stan.cson

--- grammars/stan.cson
+++ grammars/stan.cson
@@ -1,8 +1,7 @@
 name: "Stan"
 scopeName: "source.stan"
 'fileTypes': [
-	"stan"
-	,
+	"stan",
 ]
 'patterns': [
 	{


wrong stylus_source_path in config.coffee

--- gulp/config.coffee
+++ gulp/config.coffee
@@ -12,7 +12,7 @@

 config.jade_source_path   = config.source_path + '/jade'
 config.css_source_path    = config.source_path + '/css'
-config.stylus_source_path = config.source_path + '/css'
+config.stylus_source_path = config.source_path + '/stylus'
 config.image_source_path  = config.source_path + '/image'

 # Selectors


noEvent functionality in route and release parent child on screen

--- src/neck.coffee
+++ src/neck.coffee
@@ -369,7 +369,7 @@
     else
       false

-  route: (path, options = {}, back = false)->
+  route: (path, options = {}, back = false, noEvents = false)->
     controller = @_root()._childWithPath path

     # New controller instance is created if there is
@@ -402,8 +402,9 @@
     controller.activate()

     # Send route events
-    @_root().trigger 'route', controller
-    @.trigger 'route', controller
+    unless noEvents
+      @_root().trigger 'route', controller
+      @.trigger 'route', controller

   ### RELEASE ###

@@ -413,6 +414,7 @@
     @child = undefined

     # Delete parent reference
+    @parent?.child = undefined
     @parent = undefined

     @deactivate()


Added ls-files method to repo

Added ls-files method to repo

--- src/repo.coffee
+++ src/repo.coffee
@@ -184,6 +184,21 @@
   #
   status: (callback) ->
     return Status(this, callback)
+
+  # Public: Show information about files in the index and the
+  #         working tree.
+  #
+  # options  - An Object of command line arguments to pass to
+  #            `git ls-files` (optional).
+  # callback - Receives `(err,stdout)`.
+  #
+  ls_files: (options, callback) ->
+    [options, callback] = [callback, options] if !callback
+    @git "ls-files", options
+    , (err, stdout, stderr) =>
+      return callback err if err
+      return callback null, @parse_lsFiles stdout,options
+

   config: (callback) ->
     return Config(this, callback)
@@ -330,3 +345,20 @@
                 return callback null
             else
               return callback null
+
+  # Internal: Parse the list of files from `git ls-files`
+  #
+  # Return Files[]
+  parse_lsFiles: (text,options) ->
+    files = []
+    if _.has(options,'z')
+      lines   = text.split "\0"
+    else
+    	lines   = text.split "\n"
+    while lines.length
+      line =  lines.shift().split(" ")
+      files.push line
+      while lines[0]? && !lines[0].length
+        lines.shift()
+
+    return files


Fixed Issue #11.

Fixed Issue #11.

--- src/role.coffee
+++ src/role.coffee
@@ -1,7 +1,11 @@
 # Role.js, extension for querySelectorAll function v1.0.0 ~ https://github.com/kossnocorp/role

-for elm in [Document, Element]
-  do (elm) ->
-    nativeQuery = elm::querySelectorAll
-    elm::querySelectorAll = (selector) ->
-      nativeQuery.call( @, selector.replace(/@(\w+)/g, "[role~=\"$1\"]") )
+elements = []
+elements << Document if Document?
+elements << Element if Element?
+
+for elm in elements
+  do (elm) ->
+    nativeQuery = elm::querySelectorAll
+    elm::querySelectorAll = (selector) ->
+      nativeQuery.call( @, selector.replace(/@(\w+)/g, "[role~=\"$1\"]") )


Can explicitly say if want to follow the file or to break with an error when the file disappear due to deletion/rename

--- src/tail.coffee
+++ src/tail.coffee
@@ -22,7 +22,7 @@
           @buffer = parts.pop()
           @emit("line", chunk) for chunk in parts

-  constructor:(@filename, @separator=/[\r]{0,1}\n/, @fsWatchOptions = {}, @fromBeginning=false) ->
+  constructor:(@filename, @separator=/[\r]{0,1}\n/, @fsWatchOptions = {}, @fromBeginning=false, @follow=true) ->
     @buffer = ''
     @internalDispatcher = new events.EventEmitter()
     @queue = []
@@ -53,7 +53,12 @@
         @internalDispatcher.emit("next") if @queue.length is 1
     else if e is 'rename'
       @unwatch()
-      setTimeout (=> @watch()), 1000
+      if @follow
+        setTimeout (=> @watch()), 1000
+      else
+        console.error("'rename' event for #{@filename}. File not available.")
+        @emit("error", "'rename' event for #{@filename}. File not available.")
+

   watchFileEvent: (curr, prev) ->
     if curr.size > prev.size
@@ -67,5 +72,4 @@
     @isWatching = false
     @queue = []

-
 exports.Tail = Tail


Fixing a bug where typing help with a space after it (ie. "help ") rendered an error

--- src/tegh.coffee
+++ src/tegh.coffee
@@ -173,7 +173,7 @@
     @cli.render()

   _appendHelp: (cmd) ->
-    return @_appendSpecificHelp(cmd) if cmd?
+    return @_appendSpecificHelp(cmd) if cmd? and cmd.length > 0
     help = """
       Help
       #{"".padLeft("-", @cli.width)}


ComputeEventListener: Make poll interval optional

--- client/Main/providers/computeeventlistener.coffee
+++ client/Main/providers/computeeventlistener.coffee
@@ -1,6 +1,9 @@
 class ComputeEventListener extends KDObject

-  constructor:->
+  constructor:(options = {})->
+
+    super
+      interval : options.interval ? 4000

     @kloud          = KD.singletons.kontrol.getKite
       name          : "kloud"
@@ -9,7 +12,6 @@
     @listeners      = []
     @tickInProgress = no
     @running        = no
-    @timeout        = 5000
     @timer          = null

@@ -17,7 +19,8 @@

     return  if @running
     @running = yes
-    @timer = KD.utils.repeat @timeout, @bound 'tick'
+
+    @timer = KD.utils.repeat @getOption('interval'), @bound 'tick'

   stop:->


allrows and sorting methods

allrows and sorting methods

--- client/lanes/models/query/CollectionResult.coffee
+++ client/lanes/models/query/CollectionResult.coffee
@@ -7,6 +7,9 @@
         model = @collection.at(index)
         for field, i in @query.fields.models when (not options.visibleOnly or field.visible)
             if field.format then field.format(model[field.id], model, @query) else model[field.id]
+    allRows: (options) ->
+        rows = (@rowAt(i, options) for i in [0...@length])
+        _.Promise.resolve(rows)

     modelAt: (index) ->
         @collection.at(index)
@@ -23,7 +26,17 @@
         )

     ensureLoaded: ->
-        @collection.ensureLoaded()
+        @collection.ensureLoaded().then(@)
+
+    sortByIndex: (field, asc) ->
+        @sort =
+            asc: asc, index: @query.fields.indexOf(field)
+            visibleIndex: @query.fields.visible.indexOf(field)
+        @collection.comparator = (a, b) ->
+            sort = field.comparator(a, b)
+            if asc then sort else sort * -1
+        @query.trigger('sort')
+        @collection.sort()

 Object.defineProperties Lanes.Models.Query.CollectionResult.prototype,
     length:


Remove ID

Remove ID

--- app/assets/javascripts/ems/active_admin/components/jquery.aa.asset-store.js.coffee
+++ app/assets/javascripts/ems/active_admin/components/jquery.aa.asset-store.js.coffee
@@ -24,7 +24,7 @@
     suffix = "_destroy"
     @items.each (i,el)=>
       $el = $(el)
-      hid = $el.find('.asset-id-hidden').clone().val(1).attr("class","asset-destroy-hidden")
+      hid = $el.find('.asset-id-hidden').clone().val(1).attr({"class":"asset-destroy-hidden", "id": ""})
       if not hid.length then return false
       name = hid.attr('name').split('[')
       name.splice(-1,1)


dasherize name

dasherize name

--- client/lanes/components/Base.coffee
+++ client/lanes/components/Base.coffee
@@ -24,10 +24,9 @@
             'readTemplate'
         else
             'emptyTemplate'
-
         this.replaceEl( this.renderTemplateMethod(tmpl) );

-    templatePrefix: -> this.FILE.extensionName.toLowerCase() + "/components/"
+    templatePrefix: -> _.dasherize(this.FILE.extensionName) + "/components/"

     calculate_access:->
         if ! @field_name || Lanes.Views.RenderContext.canWrite(@field_name)


initialize out in index request

initialize out in index request

--- server/controllers/album.coffee
+++ server/controllers/album.coffee
@@ -19,6 +19,7 @@
                 Photo.fromAlbum album.id, (err, photos) =>
                     album.thumb = photos[0].id
                     album.orientation = photos[0].orientation
+                    out.push album
                     initAlbums albums, callback
             else
                 callback()


Changed Particles

Changed Particles

--- app/core/ParticleMan.coffee
+++ app/core/ParticleMan.coffee
@@ -112,7 +112,8 @@
 defaults =
   group:
     texture: 'star'
-    maxAge: 4
+    maxAge: 1.9
+    radius: 0.75
     hasPerspective: 1
     colorize: 1
     transparent: 1
@@ -121,20 +122,21 @@
     depthTest: true
     blending: THREE.NormalBlending
   emitter:
-    type: "cube"
-    particleCount: 60
+    type: "disk"
+    particleCount: 100
+    radius: 1
     position: vec 0, 0, 0
     positionSpread: vec 1, 0, 1
-    acceleration: vec 0, -1, 0
+    acceleration: vec 0, 2, 0
     accelerationSpread: vec 0, 0, 0
     velocity: vec 0, 4, 0
     velocitySpread: vec 2, 2, 2
     sizeStart: 6
-    sizeStartSpread: 0
+    sizeStartSpread: 1
     sizeMiddle: 4
-    sizeMiddleSpread: 0
+    sizeMiddleSpread: 1
     sizeEnd: 2
-    sizeEndSpread: 0
+    sizeEndSpread: 1
     angleStart: 0
     angleStartSpread: 0
     angleMiddle: 0


msgflo-register-foreign: Support running --forever

--- src/utils/register-foreign.coffee
+++ src/utils/register-foreign.coffee
@@ -17,6 +17,7 @@
   program
     .option('--broker <uri>', 'Broker address', String, '')
     .option('--role <role>', 'Role of this instance', String, '')
+    .option('--forever <true>', 'Keep running forever', Boolean, false)
     .usage('[options] <definition>')
     .parse(process.argv)
   program = common.normalizeOptions program
@@ -40,6 +41,8 @@
       return onError err if err
       foreigner.register messaging, definition, (err) ->
         return onError err if err
-        onComplete()
+
+        if not program.forever
+          onComplete()

 exports.main = main


Disable the strangely failing chooser existence test.

--- src/volume/volume.e2e-spec.coffee
+++ src/volume/volume.e2e-spec.coffee
@@ -17,7 +17,7 @@

   # Volume display panel.
   imagePanel: ->
-    @find('qi-volume-image')
+    @find('qi-image')

   # The time point slider-player combo.
   timePointChooser: ->
@@ -69,78 +69,71 @@
   #
   # TODO - revisit in 2017 after Angular 2 testing is clarified (as if).
   #   See also tests below.
-  xdescribe 'Image Display', ->
-    pane = null
+  describe 'Image Display', ->
+    panel = null

     beforeEach ->
-      # Wrap the panel resolver in a retry loop, since it might
-      # take a while for the image to load and the pane to display.
-      # Furthermore, wrap the retry argument in a function, since
-      # the then loses the self context.
-      pane = promiseRetry(-> page.imagePanel())
+      panel = page.imagePanel()

     it 'should display the image', ->
-      expect(pane, 'The image panel is missing').to.eventually.exist
+      expect(panel, 'The image panel is missing').to.eventually.exist

   # Verify the volume chooser heading. We can only detect the
   # volume slider and player.
   #
   # TODO - see ImageDisplay TODO.
-  xdescribe 'Time Point Chooser', ->
+  describe 'Time Point Chooser', ->
     chooser = null

     beforeEach ->
-      page.timePointChooser().then (resolved) ->
-        chooser = resolved
+      chooser = page.timePointChooser()

-    it 'should display the volume chooser', ->
-      expect(chooser, 'The chooser is missing').to.exist
+    # Oddly, this simple existence test fails but the following
+    # tests which use the chooser succeed.
+    # TODO - reinvestigate this anomaly.
+    xit 'should display the volume chooser', ->
+      expect(chooser, 'The chooser is missing').to.eventually.exist

     it 'should display the volume chooser heading', ->
       # The heading is the first row.
-      heading = chooser.findAll('.row').then (rows) ->
-        rows[0].text()
+      heading = chooser
+        .then (resolved) ->
+          resolved.findAll('.row')
+        .then (rows) ->
+          rows[0].text()
       expect(heading, 'The chooser heading is missing')
         .to.eventually.exist
       expect(heading, 'The chooser heading is incorrect')
         .to.eventually.equal('Time Point')

     it 'should display the volume chooser slider', ->
-      slider = chooser.find('.qi-vertical-slider')
+      slider = chooser.then (resolved) ->
+        resolved.find('.qi-vertical-slider')
       expect(slider, 'The chooser slider is missing')
         .to.eventually.exist

     it 'should display the volume chooser player', ->
-      chooser.find('.qi-volume-player').then (player) ->
-        expect(player, 'The player is missing').to.exist
-        player.findAll('button').then (buttons) ->
-          expect(buttons, 'The player buttons are missing')
-            .to.not.be.empty
-          expect(buttons.length, 'The player buttons count is incorrect')
-            .to.equal(3)
-          [previous, play, next] = buttons
-          next.click().then ->
-            expect(page.billboard, 'The next button did not change the billboard')
-              .to.eventually.equal('Breast Patient 1 Session 1 Scan 1 Volume 2')
-            previous.click().then ->
-              expect(page.billboard, 'The previous button did not change the billboard')
-                .to.eventually.equal('Breast Patient 1 Session 1 Scan 1 Volume 1')
+      chooser
+        .then (resolved) ->
+          resolved.find('qi-player')
+        .then (player) ->
+          expect(player, 'The player is missing').to.exist
+          player.findAll('button').then (buttons) ->
+            expect(buttons, 'The player buttons are missing')
+              .to.not.be.empty
+            expect(buttons.length, 'The player buttons count is incorrect')
+              .to.equal(3)
+            [previous, play, next] = buttons
+            next.click().then ->
+              expect(page.billboard, 'The next button did not change the billboard')
+                .to.eventually.equal('Breast Patient 1 Session 1 Scan 1 Volume 2')
+              previous.click().then ->
+                expect(page.billboard, 'The previous button did not change the billboard')
+                  .to.eventually.equal('Breast Patient 1 Session 1 Scan 1 Volume 1')

-  # TODO - see ImageDisplay TODO.
-  # TODO - flush out once previous TODO is addressed.
+  # TODO - flush out this test similar to the Time Point chooser test.
   xdescribe 'Session Chooser', ->
     chooser = null

     beforeEach ->
-      page.volumeChooser().then (resolved) ->
-        chooser = resolved
-
-    it 'should display the session chooser', ->
-      expect(chooser, 'The session chooser is missing').to.eventually.exist
-
-    it 'should display the session chooser heading', ->
-      heading = chooser.text('.qi-session-chooser-heading')
-      expect(heading, 'The session chooser heading is missing')
-        .to.eventually.exist
-      expect(heading, 'The session chooser heading is incorrect')
-        .to.eventually.equal('Session')
+      chooser = page.sessionChooser()


Remove example Cornerstone loader.

Remove example Cornerstone loader.

--- app/javascripts/main.coffee
+++ app/javascripts/main.coffee
@@ -59,7 +59,6 @@
     dc: './lib/dc'
     domReady: [DOM_READY_LIB + '/domReady' + MIN, './lib/domReady']
     'error-stack-parser': './lib/error-stack-parser'
-    exampleImageIdLoader: './temp/exampleImageIdLoader'
     jquery: './lib/jquery'
     lodash: './lib/lodash'
     moment: './lib/moment'
@@ -100,7 +99,6 @@
     cornerstoneMath: deps: ['cornerstone']
     cornerstoneTools: deps: ['cornerstoneMath']
     dc: deps: ['crossfilter', 'd3']
-    exampleImageIdLoader: deps: ['cornerstone']
     nganimate: deps: ['angular']
     ngnvd3: deps: ['angular', 'nvd3']
     ngresource: deps: ['angular']


Also sort nat dashboard data by # waterpoints

--- app/scripts/services.coffee
+++ app/scripts/services.coffee
@@ -51,7 +51,11 @@
             )

             # sort by % functional waterpoints
-            data = _.sortBy(data, (x) -> -x.percFun)
+            data.sort (a, b) ->
+              if a.percFun == b.percFun
+                b.count - a.count
+              else
+                b.percFun - a.percFun

             # all done, call the callback
             def.resolve(data)


Stop using `x` and `dx` attributes on `tspan`s together. Fixes #402

--- src/textwrap/helpers/tspan.coffee
+++ src/textwrap/helpers/tspan.coffee
@@ -13,7 +13,6 @@

     tspan
       .attr "x", x + "px"
-      .attr "dx", dx + "px"
       .attr "dy", dy + "px"
       .style "baseline-shift", "0%"
       .attr "dominant-baseline", "alphabetic"
@@ -28,14 +27,14 @@
     anchor = vars.align.value or vars.container.align or "start"

   if anchor is "end" or (anchor is "start" and rtl)
-    dx = width
+    xOffset = width
   else if anchor is "middle"
-    dx = width/2
+    xOffset = width/2
   else
-    dx = 0
+    xOffset = 0

   valign   = vars.valign.value or "top"
-  x        = vars.container.x
+  x        = vars.container.x + xOffset
   fontSize = if vars.resize.value then vars.size.value[1] else vars.container.fontSize or vars.size.value[0]
   dy       = vars.container.dy or fontSize * 1.1
   textBox  = null


修复路径的slash没闭合问题

修复路径的slash没闭合问题

--- honey/index.coffee
+++ honey/index.coffee
@@ -88,7 +88,7 @@
     __pub_tmpl_comp: "#{pubTemplate}component/"
     #__pub_tmpl_module: "#{pubTemplate}module"
     __pub_tmpl_ui: "#{pubTemplate}ui"
-    __pub_tmpl_widget: "#{pubTemplate}widget"
+    __pub_tmpl_widget: "#{pubTemplate}widget/"
     #全局组件的css
     __pub_css_comp: if isProduction then pubCssCompProduction else "/imgotv-pub/css/component/"


Make a minimum width/height so that certain plots (polyhedron!) that bizarely set a width extra option still work...

--- salvus/page/3d.coffee
+++ salvus/page/3d.coffee
@@ -126,6 +126,8 @@

     return _renderer

+MIN_WIDTH = MIN_HEIGHT = 16
+
 class SalvusThreeJS
     constructor: (opts) ->
         @opts = defaults opts,
@@ -161,10 +163,13 @@
         # width, then after 8 3d renders, things get foobared in WebGL mode.  This happens even with the simplest
         # demo using the basic cube example from their site with R68.  It even sometimes happens with this workaround, but
         # at least retrying a few times can fix it.
-        if not @opts.width?
+        if not @opts.width? or @opts.width < MIN_WIDTH
+            # ignore width/height less than a cutoff -- some graphics,
+            # e.g., "Polyhedron([(0,0,0),(0,1,0),(0,2,1),(1,0,0),(1,2,3),(2,1,1)]).plot()"
+            # weirdly set it very small.
             @opts.width  = $(window).width()*.5

-        @opts.height = if @opts.height? then @opts.height else @opts.width*2/3
+        @opts.height = if @opts.height? and @opts.height >= MIN_HEIGHT then @opts.height else @opts.width*2/3
         @opts.container.css(width:"#{@opts.width+50}px")

         @set_dynamic_renderer()


add function for making an account deleted

--- salvus/rethink.coffee
+++ salvus/rethink.coffee
@@ -772,6 +772,24 @@
             cb         : required
         if not @_validate_opts(opts) then return
         @table('accounts').get(opts.account_id).delete().run(opts.cb)
+
+    mark_account_deleted: (opts) =>
+        opts = defaults opts,
+            account_id : required
+            cb         : required
+        if not @_validate_opts(opts) then return
+        email_address = undefined
+        query = @table('accounts').get(opts.account_id)
+        async.series([
+            (cb) =>
+                query.pluck('email_address').run (err, x) =>
+                    email_address = x?.email_address
+                    cb(err)
+            (cb) =>
+                query.update({first_name: 'Deleted', last_name:'User', email_address_before_delete:email_address}).run(cb)
+            (cb) =>
+                query.replace(@r.row.without('email_address')).run(cb)
+        ], opts.cb)

     # determine if the account exists and if so returns the account id; otherwise returns undefined.
     account_exists: (opts) =>


refactor drag and drop sortable clean it up

--- app/assets/javascripts/hanuman/controllers/survey_step.js.coffee
+++ app/assets/javascripts/hanuman/controllers/survey_step.js.coffee
@@ -19,7 +19,6 @@
       question.save().then (question) ->
         # need to add new question to bottom of listing with the right sort order
         survey_step.get('questions').addObject(question)
-
       @set "isNewQuestion", false
       return
     exitCreateQuestion: ->
@@ -30,12 +29,9 @@
   updateSortOrder: (indexes) ->
     console.log "in updateSortOrder"
     @beginPropertyChanges()
-    @get('questions').forEach ((item) ->
+    @get('questions').forEach (item) ->
       index = indexes[item.get("id")]
       item.set "sort_order", index + 1
       item.save()
-      return
-    ), this
     @endPropertyChanges()
-    return
 )


OX-721 testing

OX-721 testing

--- app/assets/javascripts/oxalis/controller/plane_controller.coffee
+++ app/assets/javascripts/oxalis/controller/plane_controller.coffee
@@ -265,10 +265,10 @@

   render : ->

-    @model.binary.ping(@flycam.getPosition(), {zoomStep: @flycam.getIntegerZoomStep(), area: [@flycam.getArea(constants.PLANE_XY),
+    #@model.binary.ping(@flycam.getPosition(), {zoomStep: @flycam.getIntegerZoomStep(), area: [@flycam.getArea(constants.PLANE_XY),
+    #                    @flycam.getArea(constants.PLANE_YZ), @flycam.getArea(constants.PLANE_XZ)], activePlane: @flycam.getActivePlane()})
+    @model.binaryVolume.ping(@flycam.getPosition(), {zoomStep: @flycam.getIntegerZoomStep(), area: [@flycam.getArea(constants.PLANE_XY),
                         @flycam.getArea(constants.PLANE_YZ), @flycam.getArea(constants.PLANE_XZ)], activePlane: @flycam.getActivePlane()})
-    #@model.binaryVolume.ping(@flycam.getPosition(), {zoomStep: @flycam.getIntegerZoomStep(), area: [@flycam.getArea(constants.PLANE_XY),
-    #                    @flycam.getArea(constants.PLANE_YZ), @flycam.getArea(constants.PLANE_XZ)], activePlane: @flycam.getActivePlane()})
     @model.route.globalPosition = @flycam.getPosition()
     @cameraController.update()
     @sceneController.update()


inbox loadmore fixed

inbox loadmore fixed

--- client/landing/app/Applications/Inbox.kdapplication/Views/InboxMessagesList.coffee
+++ client/landing/app/Applications/Inbox.kdapplication/Views/InboxMessagesList.coffee
@@ -99,7 +99,3 @@

   partial:(data)->
     "Load more messages..."
-
-  click:(event)->
-    controller = @getDelegate()
-    controller.continueLoadingMessages @


feed-controller: Fixed wrong sort or filter params in feed controller. Using first one if we couldn't found valid one.

--- client/landing/app/Applications/Feeder.kdapplication/FeedController.coffee
+++ client/landing/app/Applications/Feeder.kdapplication/FeedController.coffee
@@ -73,8 +73,17 @@
     @facetsController.highlight filterName, sortName

   handleQuery:({filter, sort})->
-    @selectFilter filter, no      if filter?
-    @changeActiveSort sort, no    if sort?
+
+    if filter
+      unless @filters[filter]?
+        filter = (Object.keys @filters).first
+      @selectFilter filter, no
+
+    if sort
+      unless @sorts[sort]?
+        sort = (Object.keys @sorts).first
+      @changeActiveSort sort, no
+
     @highlightFacets()
     @loadFeed()

@@ -99,6 +108,7 @@
     @resultsController.openTab @filters[name]
     if @resultsController.listControllers[name].itemsOrdered.length is 0
       @loadFeed() if loadFeed
+    @emit 'FilterChanged', name

   changeActiveSort:(name, loadFeed=yes)->
     @selection.activeSort = name


docs(app generator): add/augment grunt task descriptions

--- generators/app/templates/@Gruntfile.coffee
+++ generators/app/templates/@Gruntfile.coffee
@@ -856,6 +856,7 @@

     grunt.registerTask(
         'lint'
+        'Look for lint in the app\'s code'
         ( target = '' ) ->
             grunt.task.run(
                 "coffeelint:#{target}"
@@ -865,7 +866,7 @@

     grunt.registerTask(
         'style'
-        'Build the app\'s style'
+        'Build the app\'s stylesheet and related assets'
         ( debugging ) ->
             grunt.task.run(
                 'clean:style'


Adding a few names

Adding a few names

--- app/lib/world/names.coffee
+++ app/lib/world/names.coffee
@@ -53,6 +53,7 @@
     'Gerald'
     'Gordon'
     'Gregory'
+    'Guan Yu'
     'Halle'
     'Harold'
     'Harry'
@@ -61,6 +62,7 @@
     'Howard'
     'Huburt'
     'Hugo'
+    'Ieyasu'
     'Jack'
     'James'
     'Jason'
@@ -79,6 +81,7 @@
     'Joseph'
     'Joshua'
     'Juan'
+    'Jun Fan'
     'Justin'
     'Keith'
     'Kenneth'
@@ -101,7 +104,9 @@
     'Michael'
     'Miles'
     'Mischa'
+    'Musashi'
     'Nicholas'
+    'Nick'
     'Noah'
     'Orion'
     'Parker'
@@ -139,6 +144,7 @@
     'Steve'
     'Steven'
     'Stormy'
+    'Tadakatsu'
     'Terry'
     'Thelonious'
     'Theo'
@@ -166,6 +172,7 @@
     'Anna'
     'Anne'
     'Annie'
+    'AnnMaria'
     'Aphrodite'
     'Ashley'
     'Barbara'
@@ -191,6 +198,7 @@
     'Doris'
     'Dorothy'
     'Elizabeth'
+    'Emma'
     'Emily'
     'Evelyn'
     'Fiora'
@@ -246,6 +254,7 @@
     'Melissa'
     'Michelle'
     'Mildred'
+    'Mulan'
     'Nancy'
     'Natasha'
     'Nicole'
@@ -258,6 +267,7 @@
     'Rachel'
     'Rebecca'
     'Robin'
+    'Ronda'
     'Rose'
     'Ruby'
     'Ruth'
@@ -285,6 +295,7 @@
     'Carlton'
     'Charles'
     'Durfkor'
+    'Duan'
     'Fendrel'
     'Gawain'
     'Hamming'
@@ -292,6 +303,7 @@
     'Hershell'
     'Hingle'
     'Hodor'
+    'Jackson'
     'James'
     'Lyle'
     'Merek'
@@ -326,6 +338,7 @@
     'Millicent'
     'Regan'
     'Rose'
+    'Ruth'
     'Tabitha'
     'Thea'
   ]
@@ -333,7 +346,10 @@
     'Agapi'
     'Alden'
     'Alleria'
+    'Atalanta'
+    'Artemis'
     'Beatrice'
+    'Bachi'
     'Beverly'
     'Cairn'
     'Cecily'
@@ -389,6 +405,8 @@
     'Slyvos'
     'Vican'
     'Warshall'
+    'Yue Fei'
+    'Zhou Tong'
   ]
   'Ogre Munchkin M': [
     'Blob'
@@ -459,6 +477,7 @@
     'Mak Fod'
     'Mokrul'
     'Muthyala'
+    'Oni'
     'Polifemo'
     'Saltporker'
     'Skrungt'
@@ -533,6 +552,7 @@
     'Nazgareth'
     'Poult'
     'Sham\'uk'
+    'Torluk'
     'Turann'
     'Tuzang'
     'Tuzell'
@@ -560,6 +580,7 @@
     'Rakash'
     'Rasha'
     'Vujii'
+    'Wuda'
     'Yetu'
     'Zara'
   ]
@@ -572,6 +593,7 @@
     'Aeoldan'
     'Bestarius'
     'Cristofide'
+    'Denestorath'
     'Letholdus'
   ]
   'Potion Master': [
@@ -581,6 +603,7 @@
     'Artephius'
     'Clause'
     'Curie'
+    'Fluvius'
     'Kanada'
     'Omar'
     'Paracelsus'
@@ -595,6 +618,7 @@
     'Hushbaum'
     'Mariam'
     'Matilda'
+    'Nordex'
     'Satish'
   ]
   'Equestrian': [
@@ -649,6 +673,7 @@
   ]
   'Ninja': [
     'Amara'
+    'Goemon'
     'Itachi'
     'Kosaraju'
     'Madara'
@@ -664,6 +689,7 @@
     'Beazer'
     'Claude'
     'Gandalf'
+    'Izzrts'
     'Kleene'
     'Pender'
     'Jezebel'
@@ -749,12 +775,16 @@
     'Wildsilver'
     'Fleetfire'
     'Ed'
+    'Silver'
+    'Hurricane'
   ]
   'Trapper': [
     'Senick'
+    'John'
   ]
   'Forest Archer': [
     'Naria'
+    'Sylvus'
   ]
   'Frog': [
     'Bighead'
@@ -774,10 +804,12 @@
   'Raven': [
     # TODO: raven names
     'Nevermore'
+    'Baltimore'
   ]
   'Cougar': [
     # TODO: cougar/puma/catamount/mountain lion/jaguar names
     'Kitty'
+    'Shasta'
   ]
   'Raider': [
     'Arryn'
@@ -793,6 +825,8 @@
   ]
   'Assassin': [
     'Ritic'
+    'Shade'
+    'BlackJack'
   ]
   'Necromancer': [
     'Nalfar'
@@ -811,6 +845,7 @@
   'Ogre Chieftain': [
     # TODO: ogre chieftain names (female)
     'Zagra Ux'
+    'Oniko'
   ]
   'Ogre Warlock': [
     # TODO: ogre warlock names


#387: I'm not too fond of promises

#387: I'm not too fond of promises

--- chrome/content/zotero-better-bibtex/cayw.coffee
+++ chrome/content/zotero-better-bibtex/cayw.coffee
@@ -124,9 +124,12 @@
       else
         formatted = @config.citeprefix + formatted.join(@config.separator) + @config.citepostfix

+    Zotero.BetterBibTeX.debug('formatted-type:', typeof formatted)
     if typeof formatted == 'string'
       resolve = formatted
-      formatted = Q.defer()
+      deferred = Q.defer()
+      formatted = deferred.promise
+    Zotero.BetterBibTeX.debug('formatted-type:*', typeof formatted)

     formatted.then((res) =>
       Zotero.Utilities.Internal.copyTextToClipboard(res) if @config.clipboard
@@ -134,7 +137,7 @@
       Zotero.Integration.currentWindow.close() unless Zotero.BetterBibTeX.pref.get('tests')
       @doc.activate()
     )
-    formatted.resolve(resolve) if typeof resolve == 'string'
+    deferred.resolve(resolve) if typeof resolve == 'string'

 Zotero.BetterBibTeX.CAYW.Formatter = {
   latex: (citations, config) ->


TeamMembersView: Calculate item count to trigger a scroller.

--- client/admin/lib/views/members/teammemberscommonview.coffee
+++ client/admin/lib/views/members/teammemberscommonview.coffee
@@ -15,7 +15,7 @@
   constructor: (options = {}, data) ->

     options.cssClass                 = 'members-commonview'
-    options.itemLimit               ?= 25
+    options.itemLimit               ?= 10
     options.fetcherMethod          or= 'fetchMembersWithEmail'
     options.noItemFoundWidget      or= new KDCustomHTMLView
     options.listViewItemClass      or= MemberItemView
@@ -33,7 +33,9 @@

     @createSearchView()
     @createListController()
-    @fetchMembers()
+
+    @listController.getView().once 'viewAppended', =>
+      @fetchMembers yes

   createSearchView: ->
@@ -93,7 +95,7 @@
         @fetchMembers()

-  fetchMembers: ->
+  fetchMembers: (isInitialFetch) ->

     return if @isFetching

@@ -102,7 +104,7 @@
     group    = @getData()
     method   = @getOption 'fetcherMethod'
     options  =
-      limit  : @getOptions().itemLimit
+      limit  : @getFetcherLimit isInitialFetch
       sort   : timestamp: -1 # timestamp is at relationship collection
       skip   : @skip

@@ -240,3 +242,22 @@

     @resetListItems()
     @fetchMembers()
+
+
+  # return the ideal number of list items limit to ensure that there will be
+  # always a scroller when list controller added.
+  #
+  # get list view height and divide it to item height then add it to 2
+  # to make sure that there will be a scroller. finally choose the calculated
+  # number or the default item limit if it's bigger than calculated one.
+  getFetcherLimit: (isInitialFetch) ->
+
+    limit = @getOption 'itemLimit'
+
+    return limit unless isInitialFetch
+
+    itemHeight   = 71
+    viewHeight   = @listController.getView().getHeight()
+    initialLimit = Math.ceil(viewHeight / itemHeight) + 2
+
+    return limit = Math.max initialLimit, limit


Ace: Implemented AceApplicationTabView to handle tab close behavior.

--- client/app/Applications/Ace.kdapplication/aceappview.coffee
+++ client/app/Applications/Ace.kdapplication/aceappview.coffee
@@ -3,18 +3,15 @@

     super options, data

-    @aceViews   = {}
-    @timestamp  = Date.now()
-    @appManager = KD.getSingleton "appManager"
-
-    @tabHandleContainer = new ApplicationTabHandleHolder
-      delegate: @
-
-    @tabView = new ApplicationTabView
-      delegate             : @
-      tabHandleContainer   : @tabHandleContainer
-      saveSession          : yes
-      sessionName          : "AceTabHistory"
+    @aceViews            = {}
+    @timestamp           = Date.now()
+    @appManager          = KD.getSingleton "appManager"
+    @tabHandleContainer  = new ApplicationTabHandleHolder delegate: @
+    @tabView             = new AceApplicationTabView
+      delegate           : @
+      tabHandleContainer : @tabHandleContainer
+      saveSession        : yes
+      sessionName        : "AceTabHistory"

     @on "SessionDataCreated", (@sessionData) =>

@@ -119,7 +116,7 @@

   addNewTab: (file) ->
     file = file or FSHelper.createFileFromPath 'localfile:/Untitled.txt'
-    aceView = new AceView {}, file
+    aceView = new AceView delegate: @, file
     aceView.on 'KDObjectWillBeDestroyed', => @removeOpenDocument aceView
     @aceViews[file.path] = aceView
     @setViewListeners aceView
@@ -195,3 +192,42 @@
       {{> @tabHandleContainer}}
       {{> @tabView}}
     """
+
+class AceApplicationTabView extends ApplicationTabView
+
+  handleTabCloseAction: (pane) ->
+    {ace} = pane.getOptions().aceView
+    file  = ace.getData()
+
+    return @removePane pane  unless ace.isContentChanged()
+    modal = new KDModalView
+      cssClass      : "modal-with-text"
+      title         : "Do you want to save your changes?"
+      content       : "
Your changes will be lost if you don't save them.
"
+      overlay       : yes
+      buttons       :
+        "SaveClose" :
+          cssClass  : "modal-clean-gray"
+          title     : "Save and Close"
+          callback  : =>
+            if file.path.indexOf("localfile:") is 0
+              file.once "fs.saveAs.finished", => @removePane pane
+              ace.requestSaveAs()
+              modal.destroy()
+            else
+              ace.requestSave()
+              @closePaneAndModal pane, modal
+        "DontSave"  :
+          cssClass  : "modal-clean-gray"
+          title     : "Don't Save"
+          callback  : =>
+            @closePaneAndModal pane, modal
+        "Cancel"    :
+          cssClass  : "modal-cancel"
+          title     : "Cancel"
+          callback  : =>
+            modal.destroy()
+
+  closePaneAndModal: (pane, modal) ->
+    @removePane pane
+    modal.destroy()


Code refactoring part 1

Code refactoring part 1

--- client/app/lib/commonviews/buttonviewwithprogressbar.coffee
+++ client/app/lib/commonviews/buttonviewwithprogressbar.coffee
@@ -16,8 +16,6 @@
     progressOptions = o.progress    or {}
     loaderOptions   = o.loader      or {}

-    progressOptions.cssClass = kd.utils.curry 'hidden', progressOptions.cssClass
-
     @button = new KDButtonView buttonOptions

     @button.click = (event) =>
@@ -28,6 +26,8 @@

     @progressBar = new KDProgressBarView progressOptions
     @loader      = new KDLoaderView loaderOptions
+
+    @progressBar.hide()

     @progressBar.addSubView @loader, null, yes
     @addSubView @progressBar
@@ -54,7 +54,7 @@
     @button.show()

-  updateProgress: ->
+  updateProgress: (value, unit, label) ->

-    KDProgressBarView::updateBar.apply @progressBar, arguments
+    @progressBar.updateBar value, unit, label


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -9,6 +9,6 @@

   player.include(Controllable)

-  equals p
+  e

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -75,6 +75,8 @@

   ok player.I.facing.equal(Point(-1, 0))

-  window.keydown.
+  window.keydown.left = false
+
+

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -81,8 +81,8 @@

   player.update()

-  equals player.I.velocity.x, Point(-1, -1).norm().x * player.I.speed
-  equals player.I.velocity.y, Point(-1, -1).norm().y * player.I.speed
+  equals player.I.facing.x, Point(-1, -1).norm().x
+  equals player.I.facing.y, Point(-1, -1).norm().y

 test "should correctly track facing direction", ->
   ok player.I.facing.equal(Point(1, 0))


Added tests for filterValues.

Added tests for filterValues.

--- test/utils-test.coffee
+++ test/utils-test.coffee
@@ -49,6 +49,28 @@
 			fn = ->
 				'baz'
 			expect(utils.createPropertyMapper('foo', fn).call input).toBe null
+
+	describe 'filterValues', ->
+		it 'filters an array of key/value pairs by values', ->
+			input = [
+				[ 'foo', 'bar' ]
+				[ 'bar', 'baz' ]
+			]
+			test = [
+				[ 'foo', 'bar' ]
+			]
+			fn = (v) ->
+				v == 'bar'
+			expect(t.seq input, utils.filterValues fn).toEqual test
+		it 'filters truthy values if no filter function is provided', ->
+			input = [
+				[ 'foo', 'bar' ]
+				[ 'bar', '' ]
+			]
+			test = [
+				[ 'foo', 'bar' ]
+			]
+			expect(t.seq input, utils.filterValues()).toEqual test

 	describe 'getProps', ->
 		it 'returns an object with only the props in the provided array of keys', ->


Okay, okay, this time for real.

Okay, okay, this time for real.

--- tests/character.coffee
+++ tests/character.coffee
@@ -68,12 +68,9 @@
 	insert 'locations', id: 2, initial: 1
 	insert 'uniusers', id: 1

-	try
-		charid = character.createCharacter(conn, 1, 'My First Character', 'elf', 'female')
-		char = query.row "SELECT * FROM characters"
-		user = query.row "SELECT * FROM uniusers"
-	catch ex
-		console.log ex.stack
+	charid = character.createCharacter(conn, 1, 'My First Character', 'elf', 'female')
+	char = query.row "SELECT * FROM characters"
+	user = query.row "SELECT * FROM uniusers"

 	test.strictEqual user.character_id, charid, "should switch user's character to new character"
 	test.strictEqual charid, char.id, 'should return new character id'


Fix test for setDebugLevel

Fix test for setDebugLevel

--- test/usb.coffee
+++ test/usb.coffee
@@ -19,7 +19,7 @@
 		it 'should throw when passed invalid args', ->
 			assert.throws((-> usb.setDebugLevel()), TypeError)
 			assert.throws((-> usb.setDebugLevel(-1)), TypeError)
-			assert.throws((-> usb.setDebugLevel(4)), TypeError)
+			assert.throws((-> usb.setDebugLevel(5)), TypeError)

 		it 'should succeed with good args', ->
 			assert.doesNotThrow(-> usb.setDebugLevel(0))


Cleaned up some number extensions

Cleaned up some number extensions

--- source/number_extensions.coffee
+++ source/number_extensions.coffee
@@ -8,8 +8,6 @@
 @methodOf Number#
 @returns {Number} The absolute value of the number.
 ###
-Number::abs = () ->
-  Math.abs(this)

 ###*
 Returns the mathematical ceiling of this number.
@@ -27,8 +25,6 @@
 @methodOf Number#
 @returns {Number} The number truncated to the nearest integer of greater than or equal value.
 ###
-Number::ceil = ->
-  Math.ceil(this)

 ###*
 Returns the mathematical floor of this number.
@@ -46,8 +42,6 @@
 @methodOf Number#
 @returns {Number} The number truncated to the nearest integer of less than or equal value.
 ###
-Number::floor = ->
-  Math.floor(this)

 ###*
 Returns this number rounded to the nearest integer.
@@ -62,8 +56,10 @@
 @methodOf Number#
 @returns {Number} The number rounded to the nearest integer.
 ###
-Number::round = ->
-  Math.round(this)
+
+["abs", "ceil", "floor", "round"].each (method) ->
+  Number::[method] = ->
+    Math[method](this)

 ###*
 Get a bunch of points equally spaced around the unit circle.
@@ -166,7 +162,7 @@
 @returns {Boolean} true if this number is an even integer, false otherwise.
 ###
 Number::even = ->
-  this % 2 == 0
+  @mod(2) is 0

 ###*
 Returns true if this number is odd (has remainder of 1 when divided by 2).
@@ -185,10 +181,7 @@
 @returns {Boolean} true if this number is an odd integer, false otherwise.
 ###
 Number::odd = ->
-  if this > 0
-    this % 2 == 1
-  else
-    this % 2 == -1
+  not @even()

 ###*
 Calls iterator the specified number of times, passing in the number of the
@@ -257,7 +250,7 @@
 Number::primeFactors = ->
   factors = []

-  n = Math.floor(this)
+  n = @floor()

   if n == 0
     return undefined
@@ -300,7 +293,7 @@
 @returns {String} Hexidecimal representation of the number
 ###
 Number::toColorPart = ->
-  s = parseInt(this.clamp(0, 255), 10).toString(16)
+  s = parseInt(@clamp(0, 255), 10).toString(16)

   if s.length == 1
     s = '0' + s
@@ -334,7 +327,7 @@
 @returns {Number} A number toward target by the ratio
 ###
 Number::approachByRatio = (target, ratio) ->
-  this.approach(target, this * ratio)
+  @approach(target, this * ratio)

 ###*
 Returns a number that is closer to the target angle by the delta.
@@ -379,9 +372,9 @@
 # TODO Test and document
 Number::truncate = ->
   if this > 0
-    Math.floor(this)
+    @floor()
   else if this < 0
-    Math.ceil(this)
+    @ceil()
   else
     this

@@ -395,7 +388,7 @@
 Number::d = (sides) ->
   sum = 0

-  this.times ->
+  @times ->
     sum += rand(sides) + 1

   return sum


Remove an orphaned debugging comment

Remove an orphaned debugging comment

--- lib/assets/javascripts/unpoly/form.js.coffee
+++ lib/assets/javascripts/unpoly/form.js.coffee
@@ -782,7 +782,6 @@
   ###

   up.on 'change', '[up-toggle]', (event, $field) ->
-    console.log("CHANGE EVENT")
     toggleTargets($field)

   up.compiler '[up-toggle]', ($field) ->


Add a comment

Add a comment

--- lib/assets/javascripts/up/viewport.js.coffee
+++ lib/assets/javascripts/up/viewport.js.coffee
@@ -58,6 +58,9 @@
       $view.data(SCROLL_PROMISE_KEY, deferred)
       deferred.then ->
         $view.removeData(SCROLL_PROMISE_KEY)
+        # Since we're scrolling using #animate, #finish can be
+        # used to jump to the last frame:
+        # https://api.jquery.com/finish/
         $view.finish()

       targetProps =


added rules to fix function highlight. highlights function calls. still doesnt work with quoted function calls

--- grammars/cql.cson
+++ grammars/cql.cson
@@ -60,17 +60,58 @@
     'name': 'comment.line.double-slash.cql'
   }
   {
-    'match': '\\b(library|using|include|version|called|parameter|default|valueset|codesystems?|display|public|private|context +(Patient|Population)|define( +function +([^\\(]+))?)\\b'
+    'match': '\\b(library|using|include|version|called|parameter|default|valueset|codesystems?|display|public|private|context +(Patient|Population))\\b'
     'captures':
       '2':
         'name': 'support.constant.cql'
-      '4':
+    'name': 'keyword.declarations.cql'
+  }
+  {
+    'begin': '\\b(define( function)?) (".+?")'
+    'beginCaptures':
+      '1':
+        'name': 'keyword.declarations.cql'
+      '3':
         'name': 'entity.name.function.cql'
-    'name': 'keyword.declarations.cql'
+    'end': ':'
+    'patterns': [
+      'include': '$self'
+    ]
+  }
+  {
+    'match': '\\b([^\\s,\\(\\[:"]+?)\\('
+    'captures':
+      '1':
+        'name': 'entity.name.function.cql'
+  }
+  {
+    'match': '(".+?")\\('
+    'captures':
+      '1':
+        'name': 'entity.name.function.cql'
   }
   {
     'match': '\\bsame +((year|month|day|hour|minute|second|millisecond) +)?(or +before|or +after|as)\\b'
     'name': 'keyword.operator.temporal.cql'
+  }
+  {
+    'begin': '{'
+    'beginCaptures':
+      '0':
+        'name': 'meta.brace.curly.cql'
+    'end': '}'
+    'endCaptures':
+      '0':
+        'name': 'meta.brace.curly.cql'
+    'patterns': [
+      {
+        'match': '(\\b\\S+\\b):'
+        'captures':
+          '1':
+            'name': 'variable.other.property.cql'
+        'include': '$self'
+      }
+    ]
   }
   {
     'match': '\\b(properly +)?within\\s+(\\d+)\\s+(year|month|day|hour|minute|second|millisecond)s? +of\\b'
@@ -160,7 +201,7 @@
     'name': 'keyword.operator.aggregate.cql'
   }
   {
-    'match': '\\b(if|then|else|case|when)\\b'
+    'match': '\\b(if|then|else|case|when|end)\\b'
     'name': 'keyword.control.cql'
   }
   {


fix typo again

fix typo again

--- gulp/build.coffee
+++ gulp/build.coffee
@@ -45,8 +45,8 @@

 gulp.task 'build-image', (cb) ->
     gulp
-        .src ['src/images/**/*']
-        .pipe gulp.dest('dist/images')
+        .src ['src/image/**/*']
+        .pipe gulp.dest('dist/image')
         .pipe browserSync.stream()

 gulp.task 'build-jade', ->


fix typo

fix typo

--- gulp/build.coffee
+++ gulp/build.coffee
@@ -43,7 +43,7 @@
         .pipe gulp.dest('dist/css')
         .pipe browserSync.stream()

-gulp.task 'build-images', (cb) ->
+gulp.task 'build-image', (cb) ->
     gulp
         .src ['src/images/**/*']
         .pipe gulp.dest('dist/images')


Recompile vendor css on bootstrap var changes

--- gulp/watch.coffee
+++ gulp/watch.coffee
@@ -10,3 +10,4 @@
     g.watch "scripts/**/*.coffee", ["scripts"]
     g.watch "styles/**/*.scss", ["styles"]
     g.watch "{app,config,views}/**/*", ["views"]
+    g.watch "styles/_variables.scss", ["vendor-css"]


preload-tolerance to 0.9

preload-tolerance to 0.9

--- app/assets/javascripts/model.coffee
+++ app/assets/javascripts/model.coffee
@@ -243,7 +243,7 @@

-	PRELOAD_TOLERANCE : 0.7
+	PRELOAD_TOLERANCE : 0.9
 	PRELOAD_RADIUS : 20
 	preloadVertices : []


refactored model+doc

refactored model+doc

--- app/assets/javascripts/model.coffee
+++ app/assets/javascripts/model.coffee
@@ -1,84 +1,68 @@
+# Initializing `Model`
 Model ?= {}

-Model.BinaryStore =
-
-	data : []
-	size : [0,0,0]
-	offset : [0,0,0]
-
-	BUCKET_WIDTH : 64
-
-	bucketIndex : (x, y, z) ->
-		(x / @BUCKET_WIDTH - @offset[0] >> 0) +
-		(y / @BUCKET_WIDTH - @offset[1] >> 0) * @size[0] +
-		(z / @BUCKET_WIDTH - @offset[2] >> 0) * @size[0] * @size[1]
-
-	pointIndex : (x, y, z) ->
-		(x % @BUCKET_WIDTH >> 0) +
-		(y % @BUCKET_WIDTH >> 0) * @BUCKET_WIDTH +
-		(z % @BUCKET_WIDTH >> 0) * @BUCKET_WIDTH * @BUCKET_WIDTH
-
-	extendPoints : (x0, y0, z0, x1, y1, z1) ->
-
-		@extendCube(
-			x0 / @BUCKET_WIDTH >> 0,
-			y0 / @BUCKET_WIDTH >> 0,
-			x0 / @BUCKET_WIDTH >> 0,
-			x1 / @BUCKET_WIDTH >> 0,
-			y1 / @BUCKET_WIDTH >> 0,
-			z1 / @BUCKET_WIDTH >> 0
-		)
-
-	extendCube : (x0, y0, z0, x1, y1, z1) ->
-
-		cubeOffset = [
-			Math.min(x0, x1, @offset[0])
-			Math.min(y0, y1, @offset[1])
-			Math.min(z0, z1, @offset[2])
-		]
-		cubeSize = [
-			Math.max(x0, x1, @offset[0] + @size[0]) - cubeOffset[0] + 1
-			Math.max(y0, y1, @offset[1] + @size[1]) - cubeOffset[1] + 1
-			Math.max(z0, z1, @offset[2] + @size[2]) - cubeOffset[2] + 1
-		]
-
-		cube = []
-		for z in [0...cubeSize[2]]
-			for y in [0...cubeSize[1]]
-				for x in [0...cubeSize[0]]
-					index =
-						(x - @offset[0]) +
-						(y - @offset[1]) * @size[0] +
-						(z - @offset[2]) * @size[0] * @size[1]
-
-					cube.push @data[index]
-
-
-		@data = cube
-		@offset = cubeOffset
-		@size = cubeSize
-
-	value : (x, y, z, newValue) ->
-
-		bucketIndex = @bucketIndex(x, y, z)
-		bucket = @data[bucketIndex]
-
-		unless newValue?
-			if bucket?
-				bucket[@pointIndex(x, y, z)]
-			else
-				-1
-		else
-			if 0 <= bucketIndex < @data.length
-				unless bucket?
-					bucket = @data[bucketIndex] = new Float32Array(@BUCKET_WIDTH * @BUCKET_WIDTH * @BUCKET_WIDTH)
-					bucket[i] = -1 for i in [0...bucket.length]
-				bucket[@pointIndex(x, y, z)] = newValue
-			else
-				throw "cube fault"
-
+# #Model.Binary#
+# Binary is the real deal.
+# It loads and stores the primary graphical data.
+#
+# **Mixins**: Model.LazyInitializable, Model.Synchronizable
+#
+# ##Data structure##
+#
+# ###Concept###
+# We store 3-dimensional data with each coordinate >= [0,0,0].
+# Each point is stored in **buckets** which resemble a cubical grid.
+# Those buckets are kept in an expandable data structure (**cube**) which
+# represents the smallest cuboid covering all used buckets.
+#
+# ###Implementation###
+# Each point value (greyscale color) is represented by a number
+# between 1 and 2, where 1 is black and 2 white. Actually WebGL
+# generally uses [0, 1], but as TypedArrays are initialized with
+# 0 we shift the actual interval to use 0 as an undefined value.
+#
+# The buckets are implemented as `Float32Array`s with a length of
+# `BUCKET_WIDTH ^ 3`. Each point can be easiliy found through simple
+# arithmetik (see `Model.Binary.pointIndex`).
+#
+# The cube is defined by the offset `[x,y,z]` and size `[a,b,c]`
+# of the cuboid. It is actually just a standard javascript array
+# with each item being either `null` or a bucket. The length of the
+# array is `a * b * c`. Also finding th containing bucket of a point
+# can be done with pretty simple math (see `Model.Binary.bucketIndex`).
+#
+# ###Inserting###
+# When inserting new data into the data structure we first need
+# to make sure the cube is big enough to cover all points. Otherwise
+# we'll have to expand the cube (see `Model.Binary.expandCube`).
+# Then we add each point. If the corresponding bucket of a point
+# isn't initialized we'll handle that on the fly
+# (see `Model.Binary.value`).
+#
+#
+# ##Loading##
+# The server provides the coordinates (vertices) and color values of
+# the data separately.
+#
+#
+#
+#         +--+--+--+--+--+--+--+
+#        /  /  /  /  /  /  /  /|
+#       /--/--/--/--/--/--/--/ |
+#      /  /  /  /  /  /  /  /|/|
+#     +--+--+--+--+--+--+--+ | |
+#     |  |  |  |  |  |  |  |/|/|
+#     |--|--|--|--|--|--|--| | |
+#     |  |  |  |  |  |  |  |/|/
+#     |--|--|--|--|--|--|--| /
+#     |  |  |  |  |  |  |  |/
+#     +--+--+--+--+--+--+--+
+#
+#
+
+#
 Model.Binary =
-
+
 	vertexTemplate : null

 	initialize : (callback) ->
@@ -129,6 +113,8 @@

 	get : (vertices, callback) ->

+		_value = @value
+
 		colors = new Float32Array(~~(vertices.length / 3))

 		for i in [0...vertices.length] by 3
@@ -144,61 +130,67 @@
 			colors[i / 3] = if xd == 0
 				if yd == 0
 					if zd == 0
-						@value(x, y, z)
+						_value(x, y, z)
 					else
 						#linear z
-						Interpolation.linear(@value(x, y, z0), @value(x, y, z1), zd)
+						Interpolation.linear(_value(x, y, z0), _value(x, y, z1), zd)
 				else
 					if zd == 0
 						#linear y
-						Interpolation.linear(@value(x, y0, z), @value(x, y1, z), yd)
+						Interpolation.linear(_value(x, y0, z), _value(x, y1, z), yd)
 					else
 						#bilinear y,z
 						Interpolation.bilinear(
-							@value(x, y0, z0),
-							@value(x, y1, z0),
-							@value(x, y0, z1),
-							@value(x, y1, z1),
+							_value(x, y0, z0),
+							_value(x, y1, z0),
+							_value(x, y0, z1),
+							_value(x, y1, z1),
 							yd, zd)
 			else
 				if yd == 0
 					if zd == 0
 						#linear x
-						Interpolation.linear(@value(x0, y, z), @value(x1, y, z), xd)
+						Interpolation.linear(_value(x0, y, z), _value(x1, y, z), xd)
 					else
 						#bilinear x,z
 						Interpolation.bilinear(
-							@value(x0, y, z0),
-							@value(x1, y, z0),
-							@value(x0, y, z1),
-							@value(x1, y, z1),
+							_value(x0, y, z0),
+							_value(x1, y, z0),
+							_value(x0, y, z1),
+							_value(x1, y, z1),
 							xd, zd)
 				else
 					if zd == 0
 						#bilinear x,y
 						Interpolation.bilinear(
-							@value(x0, y0, z),
-							@value(x1, y0, z),
-							@value(x0, y1, z),
-							@value(x1, y1, z),
+							_value(x0, y0, z),
+							_value(x1, y0, z),
+							_value(x0, y1, z),
+							_value(x1, y1, z),
 							xd, yd)
 					else
 						#trilinear x,y,z
 						Interpolation.trilinear(
-							@value(x0, y0, z0),
-							@value(x1, y0, z0),
-							@value(x0, y1, z0),
-							@value(x1, y1, z0),
-							@value(x0, y0, z1),
-							@value(x1, y0, z1),
-							@value(x0, y1, z1),
-							@value(x1, y1, z1),
+							_value(x0, y0, z0),
+							_value(x1, y0, z0),
+							_value(x0, y1, z0),
+							_value(x1, y1, z0),
+							_value(x0, y0, z1),
+							_value(x1, y0, z1),
+							_value(x0, y1, z1),
+							_value(x1, y1, z1),
 							xd, yd, zd
 						)

 		callback(null, colors)

-	getOld : (position, direction, callback) ->
+
+	ping : (position, direction, callback) ->
+
+		@ping = _.throttle2 @_ping, 20000
+		@ping(position, direction, callback)
+
+	_ping : (position, direction, callback) ->

 		@lazyInitialize (err) =>
 			return callback err if err
@@ -224,11 +216,22 @@
 						min_y = if y < min_y then y else min_y
 						min_z = if z < min_z then z else min_z

+					min_x = if x < 0 then 0 else x
+					min_y = if y < 0 then 0 else y
+					min_z = if z < 0 then 0 else z
+					max_x = if x < 0 then 0 else x
+					max_y = if y < 0 then 0 else y
+					max_z = if z < 0 then 0 else z
+

 					@extendPoints(min_x, min_y, min_z, max_x, max_y, max_z)

 					for i in [0...colors.length]
-						@value(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2], colors[i] / 256)
+						x = vertices[i * 3]
+						y = vertices[i * 3 + 1]
+						z = vertices[i * 3 + 2]
+						if x >= 0 and y >= 0 and z >= 0
+							@value(x, y, z, colors[i] / 256 + 1)

 					console.timeEnd "e"
 					callback null if callback
@@ -238,12 +241,12 @@

 			@load position, direction, @synchronizingCallback(loadedData, finalCallback)

-	load : (point, direction, callback) ->
+	load : (position, direction, callback) ->
 		@lazyInitialize (err) ->
 			return callback(err) if err

 			request
-				url : "/binary/data/cube?px=#{point[0]}&py=#{point[1]}&pz=#{point[2]}&ax=#{direction[0]}&ay=#{direction[1]}&az=#{direction[2]}"
+				url : "/binary/data/cube?px=#{position[0]}&py=#{position[1]}&pz=#{position[2]}&ax=#{direction[0]}&ay=#{direction[1]}&az=#{direction[2]}"
 				responseType : 'arraybuffer'
 				,
 				(err, data) ->
@@ -251,6 +254,95 @@
 						callback err
 					else
 						callback null, new Uint8Array(data)
+
+	data : []
+	size : [0,0,0]
+	offset : [0,0,0]
+
+	BUCKET_WIDTH : 64
+
+	bucketIndex : (x, y, z) ->
+
+		_bucket_width = @BUCKET_WIDTH
+		_offset = @offset
+		_size = @size
+
+		(x / _bucket_width - _offset[0] >> 0) +
+		(y / _bucket_width - _offset[1] >> 0) * _size[0] +
+		(z / _bucket_width - _offset[2] >> 0) * _size[0] * _size[1]
+
+	pointIndex : (x, y, z) ->
+
+		_bucket_width = @BUCKET_WIDTH
+
+		(x % _bucket_width >> 0) +
+		(y % _bucket_width >> 0) * _bucket_width +
+		(z % _bucket_width >> 0) * _bucket_width * _bucket_width
+
+	extendPoints : (x0, y0, z0, x1, y1, z1) ->
+
+		_bucket_width = @BUCKET_WIDTH
+
+		@extendCube(
+			x0 / _bucket_width >> 0,
+			y0 / _bucket_width >> 0,
+			x0 / _bucket_width >> 0,
+			x1 / _bucket_width >> 0,
+			y1 / _bucket_width >> 0,
+			z1 / _bucket_width >> 0
+		)
+
+	extendCube : (x0, y0, z0, x1, y1, z1) ->
+
+		_offset = @offset
+		_size = @size
+		_data = @data
+
+		cubeOffset = [
+			Math.min(x0, x1, _offset[0])
+			Math.min(y0, y1, _offset[1])
+			Math.min(z0, z1, _offset[2])
+		]
+		cubeSize = [
+			Math.max(x0, x1, _offset[0] + _size[0]) - cubeOffset[0] + 1
+			Math.max(y0, y1, _offset[1] + _size[1]) - cubeOffset[1] + 1
+			Math.max(z0, z1, _offset[2] + _size[2]) - cubeOffset[2] + 1
+		]
+
+		cube = []
+		for z in [0...cubeSize[2]]
+			for y in [0...cubeSize[1]]
+				for x in [0...cubeSize[0]]
+					index =
+						(x - _offset[0]) +
+						(y - _offset[1]) * _size[0] +
+						(z - _offset[2]) * _size[0] * _size[1]
+
+					cube.push _data[index]
+
+
+		@data = cube
+		@offset = cubeOffset
+		@size = cubeSize
+
+	value : (x, y, z, newValue) ->
+
+		bucketIndex = @bucketIndex(x, y, z)
+		bucket = @data[bucketIndex]
+
+		unless newValue?
+			if bucket?
+				bucket[@pointIndex(x, y, z)]
+			else
+				-1
+		else
+			if 0 <= bucketIndex < @data.length
+				unless bucket?
+					_bucket_width = @BUCKET_WIDTH
+					bucket = @data[bucketIndex] = new Float32Array(_bucket_width * _bucket_width * _bucket_width)
+				bucket[@pointIndex(x, y, z)] = newValue
+			else
+				throw "cube fault"

 Model.Mesh =
@@ -317,7 +409,7 @@
 			request url : "/assets/shader/#{name}.vs", (@synchronizingCallback loadedData, (@cachingCallback name, callback))
 			request url : "/assets/shader/#{name}.fs", (@synchronizingCallback loadedData, (@cachingCallback name, callback))

-
+
 Model.Route =

 	dirtyBuffer : []
@@ -390,6 +482,12 @@
 			@dirtyBuffer.push position
 			@push()

+
+
+#################################################
+# Mixins
+#################################################
+
 Model.LazyInitializable =

 	initialized : false
@@ -457,7 +555,10 @@
 			return false

-_.extend Model.Binary, Model.BinaryStore
+#################################################
+# Mixin hook up
+#################################################
+
 _.extend Model.Binary, Model.Synchronizable
 _.extend Model.Binary, Model.LazyInitializable
 _.extend Model.Mesh, Model.Cacheable


Tidy up the CoffeeScript

Tidy up the CoffeeScript

--- app/assets/javascripts/posts.coffee
+++ app/assets/javascripts/posts.coffee
@@ -1,16 +1,16 @@
 console.log("init Blend4Web")
-m_app = b4w.require("app");
-m_data = b4w.require("data");
-m_container = b4w.require("container");
-m_anchors = b4w.require("anchors");
-m_main  = b4w.require("main");
+m_app = b4w.require("app")
+m_data = b4w.require("data")
+m_container = b4w.require("container")
+m_anchors = b4w.require("anchors")
+m_main = b4w.require("main")

 $ ->
   $(window).resize ->
     w = $(window).width()
     h = $(window).height()
     console.log("resizing container")
-    m_container.resize_to_container();
+    m_container.resize_to_container()
     console.log($(window).width())
     if $(window).width() > 828
       console.log("one row")


Minor tidying

Minor tidying

--- app/assets/js/components/asg.coffee
+++ app/assets/js/components/asg.coffee
@@ -83,32 +83,28 @@
             onMouseLeave: @disableDetail
           })
           (g {
-            className: "nv-sparklineWrap"
-          }, [
-            (g {
-                className: "nvd3 nv-wrap nv-sparkline"
-              },
-              [(path {
-                d : "M#{@x()(points[0].x)},#{@y()(points[0].y)}" + ("L#{@x()(p.x)},#{@y()(p.y)}" for p in points).join('')
-                stroke: "black"
-              }, [])
-              (circle {
-                cx: @x()(points[points.length - 1].x)
-                cy: @y()(points[points.length - 1].y)
-                r: 2
-                fill: "black"
-              })
-              ]
-            )
-            (text {
-              x: @state.width - 45
-              y: @y()(points[points.length - 1].y) + 10
-              style: {
-                fontSize: '1em'
-                fontWeight: 'normal'
-              }
-            }, points[points.length - 1].y + @props.unit)
-          ])
+              className: "nvd3 nv-wrap nv-sparkline"
+            },
+            [(path {
+              d : "M#{@x()(points[0].x)},#{@y()(points[0].y)}" + ("L#{@x()(p.x)},#{@y()(p.y)}" for p in points).join('')
+              stroke: "black"
+            }, [])
+            (circle {
+              cx: @x()(points[points.length - 1].x)
+              cy: @y()(points[points.length - 1].y)
+              r: 2
+              fill: "black"
+            })
+            ]
+          )
+          (text {
+            x: @state.width - 45
+            y: @y()(points[points.length - 1].y) + 10
+            style: {
+              fontSize: '1em'
+              fontWeight: 'normal'
+            }
+          }, points[points.length - 1].y + @props.unit)
         if @state.showDetail then (g { className: 'nv-hoverValue' }, [
           (line {
             x1: @x()(@state.detailPoint.x)
@@ -178,7 +174,7 @@
     d3.time.scale().range([0, @state.width - 50]).domain(d3.extent(@props.points, (d) -> d.x))

   y: () ->
-    d3.scale.linear().range([@props.height,10]).domain(d3.extent(@props.points, (d) -> d.y))
+    d3.scale.linear().range([@props.height,11]).domain(d3.extent(@props.points, (d) -> d.y))

 ClusterTitle = React.createClass


ConversationStarter: Added hidden class to search results container (showing it if results found)

--- client/landing/app/Applications/Chat.kdapplication/Views/conversationstarter.coffee
+++ client/landing/app/Applications/Chat.kdapplication/Views/conversationstarter.coffee
@@ -33,7 +33,7 @@
       cssClass : "conversation-starter-popup"

     @recipientsWrapper = new KDView
-      cssClass : "completed-items"
+      cssClass : "completed-items hidden"

     @recipient = new KDAutoCompleteController
       name                : "recipient"
@@ -57,15 +57,16 @@

     @recipient.on 'ItemListChanged', (newCount)=>
       if newCount > 0
-      then @startConversationButton.show()
-      else @startConversationButton.hide()
+        @recipientsWrapper.show()
+        @startConversationButton.show()
+      else
+        @startConversationButton.hide()

     @input = @recipient.getView()
     @input.setPlaceHolder "Start by typing user names..."

   viewAppended:->
     super
-
     @addSubView @input
     @addSubView @recipientsWrapper
     @addSubView @startConversationButton


make archive and unarchive parallelLimit to speed it up

--- app/coffee/DocArchiveManager.coffee
+++ app/coffee/DocArchiveManager.coffee
@@ -20,7 +20,7 @@
 			jobs = _.map docs, (doc) ->
 				(cb)->
 					DocArchive.archiveDoc project_id, doc, cb
-			async.series jobs, callback
+			async.parallelLimit jobs, 5, callback

 	archiveDoc: (project_id, doc, callback)->
@@ -52,7 +52,7 @@
 						return cb()
 					else
 						DocArchive.unarchiveDoc project_id, doc._id, cb
-			async.series jobs, callback
+			async.parallelLimit jobs, 5, callback

 	unarchiveDoc: (project_id, doc_id, callback)->
 		logger.log project_id: project_id, doc_id: doc_id, "getting doc from s3"


Fix backticked import in coffeescript

Fix backticked import in coffeescript

--- app/pages/project/project-page.cjsx
+++ app/pages/project/project-page.cjsx
@@ -4,7 +4,7 @@
 {Markdown} = require 'markdownz'
 {sugarClient} = require 'panoptes-client/lib/sugar'
 PotentialFieldGuide = require './potential-field-guide'
-`import ProjectNavbar from './project-navbar';`
+ProjectNavbar = require('./project-navbar').default

 AVATAR_SIZE = 100


Load all memberships on memberships page (#3498)

--- angular/core/components/memberships_page/memberships_page_controller.coffee
+++ angular/core/components/memberships_page/memberships_page_controller.coffee
@@ -5,7 +5,7 @@
     return if @group? or !group?
     if AbilityService.canViewMemberships(group)
       @group = group
-      Records.memberships.fetchByGroup(@group.key, per: 100).then ->
+      Records.memberships.fetchByGroup(@group.key, per: @group.membershipsCount).then ->
         ScrollService.scrollTo("[data-username=#{$routeParams.username}]") if $routeParams.username?
     else
       $rootScope.$broadcast 'pageError', { status: 403 }, group


fix table resize method getHeaderWidth

fix table resize method getHeaderWidth

--- app/assets/javascripts/app/controllers/_application_controller_table.coffee
+++ app/assets/javascripts/app/controllers/_application_controller_table.coffee
@@ -384,12 +384,12 @@

   getHeaderWidths: (headers) ->
     widths = _.reduce headers, (memo, col, i) =>
-      if col.style && col.width
+      if col.width
         value = parseInt col.width, 10
         unit = col.width.match(/[px|%]+/)[0]
       else
         # !!! sets the width to default width if not set
-        headers[i].style = { width: @baseColWidth+'px' }
+        headers[i].width = @baseColWidth+'px'
         value = @baseColWidth
         unit = 'px'


Added members view to registered users more link at activity.

--- client/app/Applications/Activity.kdapplication/views/newmemberbucket.coffee
+++ client/app/Applications/Activity.kdapplication/views/newmemberbucket.coffee
@@ -22,32 +22,37 @@
     lastFetchedDate = null

     @group.on "moreLinkClicked", =>
-      # TBDL an accordion view
-      return
-      log "expand the view to show more users"
+      # @group.loader.show()
+
       selector    =
         type      : { $in : ['CNewMemberBucketActivity'] }
         createdAt :
-          $lt     : lastFetchedDate or @getData().createdAt[0]
-          $gt     : @getData().createdAt[1]
+          $gt     : lastFetchedDate or @getData().createdAtTimestamps[0]
+          $lt     : @getData().createdAtTimestamps[1]

       options     =
-        limit     : 20
-
-      @group.loader.show()
+        limit     : 10
+
       KD.remote.api.CActivity.some selector, options, (err, activities)=>
-        if err then warn err
-        else
-          activities = ActivityAppController.clearQuotes activities
-          lastFetchedDate = activities.last.createdAt
-          KD.remote.reviveFromSnapshots activities, (err, teasers)=>
-            if err then warn err
-            else
-              @getData().anchors = @getData().anchors.concat teasers.map (item)-> item.anchor
-              @group.setData @getData().anchors
-              @group.visibleCount += 20
-              @group.render()
-              @group.loader.hide()
+        return warn err if err
+        activities = ActivityAppController.clearQuotes activities
+        # lastFetchedDate = activities.last.createdAt
+        KD.remote.reviveFromSnapshots activities, (err, teasers)=>
+          if err then warn err
+          else
+            @getData().anchors = @getData().anchors.concat teasers.map (item)-> item.anchor
+            @group.setData @getData().anchors
+            @group.visibleCount += 10
+            # @group.render()
+            # @group.loader.hide()
+
+            @newMembersList = new KDListViewController
+              view        : new NewMemberList
+              wrapper     : no
+              scrollView  : no
+            , items: @getData().anchors
+
+            @group.addSubView @newMembersList.getView()

   pistachio:->

@@ -57,13 +62,45 @@
     {{> @group}}
     """

+
+class NewMemberList extends KDListView
+  constructor: (options = {}, data) ->
+    options.tagName   = "ul"
+    options.itemClass = NewMemberListItem
+
+    super options, data
+
+
+class NewMemberListItem extends KDListItemView
+  constructor: (options = {}, data) ->
+    options.tagName   = "li"
+
+    super options, data
+
+  fetchUserDetails: ->
+    KD.remote.cacheable "JAccount", @getData().id, (err, res) =>
+      @addSubView new MembersListItemView {}, res
+
+  viewAppended: ->
+    @setTemplate @pistachio()
+    @template.update()
+    @fetchUserDetails()
+
+  pistachio: -> ""
+
+  userTemplate: (data) ->
+    # """
+    #   #{userName}
+    # """
+
+
 class NewMemberLinkGroup extends LinkGroup

   constructor:->

     super

-    # @loader = new KDLoaderView
+    @loader = new KDLoaderView

   createMoreLink:->


initialize with correct defaults

initialize with correct defaults

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -30,7 +30,10 @@

     super options

-    @currentFilter  = activityTypes
+    if KD.config.useNeo4j
+      @setfilter "Everything"
+    else
+      @currentFilter  = activityTypes
     @feedType       = "Public"
     @appStorage     = new AppStorage 'Activity', '1.0'
     @isLoading      = no
@@ -229,7 +232,6 @@
         createdAt : -1

     {CActivity}   = KD.remote.api
-
     if KD.config.useNeo4j
       options.feedType = @feedType


Fix typo

Fix typo

--- examples/hello-server/app.coffee
+++ examples/hello-server/app.coffee
@@ -52,7 +52,7 @@
   res.send template.outerHTML
   ###

-  # templating with jQuery - remove is using jsdom
+  # templating with jQuery - remove if using jsdom
   template = $ """


Use TAPi18n.getLanguages() to determine supported languages, fix #4

Use TAPi18n.getLanguages() instead of TAPi18n.conf.supported_languages
to determine supported languages.

TAPi18n.conf.supported_languages is null for projects in which tap:i18n
is
supported but project-tap.i18n `supported_languages` property isn't
defined,
meaning, when the project developer wants all the languages that have a
language file to be supported.

--- i18n_ui_amplify.coffee
+++ i18n_ui_amplify.coffee
@@ -9,4 +9,4 @@
   if storedLanguage
     TAPi18n.setLanguage storedLanguage
   else
-    TAPi18n.setLanguageAmplify TAPi18n.conf.supported_languages[0]
+    TAPi18n.setLanguageAmplify _.keys(TAPi18n.getLanguages())[0]


Fix #2970: Turn admin off by default on localhost

--- server/users/User.coffee
+++ server/users/User.coffee
@@ -200,7 +200,6 @@

 UserSchema.methods.register = (done) ->
   @set('anonymous', false)
-  @set('permissions', ['admin']) if not isProduction and not GLOBAL.testing
   if (name = @get 'name')? and name isnt ''
     unconflictName name, (err, uniqueName) =>
       return done err if err


maybe

maybe

--- services/data/orm.coffee
+++ services/data/orm.coffee
@@ -23,7 +23,7 @@
         mongo.db.collection(@collection).count query, (err, n) => cb err, n
     @insert: (item, cb) ->
         mongo.db.collection(@collection).insert item, (err, inserted) =>
-            cb err, (@coerce(item) for item in inserted)
+            cb err, inserted.map (i) => @coerce(i)
     @update: (query, item, options, cb) ->
         if !cb
             cb = options


fixing js driver carrot-printing when applied to the entire query

--- drivers/javascript/errors.coffee
+++ drivers/javascript/errors.coffee
@@ -36,7 +36,10 @@
         term.compose(args, optargs)

     printCarrots: (term, frames) ->
-        tree = composeCarrots(term, frames)
+        if frames.length is 0
+            tree = [carrotify(composeTerm(term))]
+        else
+            tree = composeCarrots(term, frames)
         (joinTree tree).replace(/[^\^]/g, ' ')

     composeCarrots = (term, frames) ->
@@ -69,9 +72,9 @@
         for term in tree
             if Array.isArray term
                 if term.length == 2 and term[0] is carrotMarker
-                        str += (joinTree term[1]).replace(/./g, '^')
+                    str += (joinTree term[1]).replace(/./g, '^')
                 else
-                        str += joinTree term
+                    str += joinTree term
             else
                 str += term
         return str


- Cleanup whitespace fix Leave Question box

--- client/Framework/core/KDWindowController.coffee
+++ client/Framework/core/KDWindowController.coffee
@@ -7,7 +7,7 @@

 ###
 class KDWindowController extends KDController
-
+
   constructor:(options,data)->
     @windowResizeListeners = {}
     @keyView
@@ -16,18 +16,18 @@
     @bindEvents()
     @setWindowProperties()
     super
-
+
   addLayer: (layer)->

     unless layer in @layers
       @layers.push layer
-
+
   removeLayer: (layer)->

     if layer in @layers
       index = @layers.indexOf(layer)
       @layers.splice index, 1
-
+
   bindEvents:()->

     $(window).bind "keydown keyup keypress",@key
@@ -53,7 +53,7 @@
       @propagateEvent (KDEventType: 'DropOnWindow'), event
       @setDragInAction no
     , yes
-
+
     @layers = layers = []

     document.body.addEventListener 'mousedown', (e)=>
@@ -68,31 +68,31 @@
       @unsetDragView e if @dragView
       @propagateEvent (KDEventType: 'ReceivedMouseUpElsewhere'), e
     , yes
-
+
     document.body.addEventListener 'mousemove', (e)=>
       @redirectMouseMoveEvent e
     , yes
-

-    window.onbeforeunload = (event) =>
-      # event.preventDefault();
-      # event.stopImmediatePropagation();
-      # event.stopPropagation();
+    unless window.location.hostname is 'localhost'
+      window.onbeforeunload = (event) =>
+        # event.preventDefault();
+        # event.stopImmediatePropagation();
+        # event.stopPropagation();

-      event or= window.event
-      msg = "Please make sure that you saved all your work."
-
-      event.returnValue = msg if event # For IE and Firefox prior to version 4
+        event or= window.event
+        msg = "Please make sure that you saved all your work."

-      return msg unless window.location.hostname isnt 'localhost'
+        event.returnValue = msg if event # For IE and Firefox prior to version 4
+
+        return msg

   setDragInAction:(action = no)->
     $('body')[if action then "addClass" else "removeClass"]("dragInAction")
     @dragInAction = action
-
+
   setMainView:(view)->
     @mainView = view
-
+
   getMainView:(view)->
     @mainView

@@ -102,7 +102,7 @@
       @setKeyView @oldKeyView

   setKeyView:(newKeyView)->
-
+
     return if newKeyView is @keyView

     newKeyViewAcceptsKeyStatus = ()->
@@ -115,33 +115,33 @@
       @keyView = newKeyView
       newKeyView?.propagateEvent KDEventType : 'KDViewBecameKeyView'
       @propagateEvent (KDEventType: 'WindowChangeKeyView', globalEvent : yes), view: newKeyView
-
+
   setDragView:(dragView)->
-
+
     @setDragInAction yes
     @dragView = dragView
-
+
   unsetDragView:(e)->
-
+
     @setDragInAction no
     @dragView.emit "DragFinished", e, @dragState
     @dragView = null
-
-
+
+
   redirectMouseMoveEvent:(event)->

     view = @dragView
     return unless @dragView
-
+
     {pageX, pageY}   = event
     {startX, startY} = view.dragState

     delta =
       x : pageX - startX
       y : pageY - startY
-
+
     view.drag event, delta
-
+
   getKeyView:()->
     @keyView

@@ -151,7 +151,7 @@

   allowScrolling:(shouldAllowScrolling)->
     @scrollingEnabled = shouldAllowScrolling
-
+
   registerWindowResizeListener:(instance)->
     @windowResizeListeners[instance.id] = instance
     instance.registerListener
@@ -159,11 +159,11 @@
       listener      : @
       callback      : =>
         delete @windowResizeListeners[instance.id]
-
+
   setWindowProperties:(event)->
     @winWidth  = $(window).width()
     @winHeight = $(window).height()
-
+
   notifyWindowResizeListeners:(event, throttle = no, duration = 17)->
     event or= type : "resize"
     if throttle


component-lab: change column widths of card info

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

--- client/component-lab/CreditCard/CardInfo.coffee
+++ client/component-lab/CreditCard/CardInfo.coffee
@@ -15,10 +15,10 @@

     <Row className={styles['info-row']}>
-      <Col xs={7}>
+      <Col xs={6}>

Credit Card Number

       </Col>
-      <Col xs={5}>
+      <Col xs={6}>

Expiration

       </Col>
     </Row>
@@ -30,7 +30,7 @@
           style={background: '#989898'} />
       </Col>

-      <Col xs={5}>
+      <Col xs={4}>
         <CardNumber
           size={18}
           type='default'
@@ -40,7 +40,7 @@

       <Col xs />

-      <Col xs={2}>
+      <Col xs={3}>
         <CardDate
           size={18}
           type='default'


StatusBarMenu: It will only work for EditorPane for now.

--- client/ide/views/statusbar/statusbarmenu.coffee
+++ client/ide/views/statusbar/statusbarmenu.coffee
@@ -5,7 +5,8 @@
     menuItems         = @getMenuItems options
     {delegate}        = options
     options.menuWidth = 220
-    options.x         = delegate.getX()
+    options.x         = delegate.getX() - 5
+    options.y         = delegate.getY() + 20
     options.cssClass  = 'status-bar-menu'

     super options, menuItems
@@ -17,38 +18,15 @@
   getMenuItems: (options) ->
     appManager = KD.getSingleton 'appManager'
     items      = {}
-
-    @addEditorMenuItems  items, options, appManager  if options.paneType is 'editor'
-    @addDefaultMenuItems items, options, appManager
+    items.Save                   = callback: -> appManager.tell 'IDE', 'saveFile'
+    items['Save As...']          = callback: -> appManager.tell 'IDE', 'saveAs'
+    items['Save All']            = callback: -> appManager.tell 'IDE', 'saveAllFiles'
+    items.customView             = @syntaxSelector = new IDE.SyntaxSelectorMenuItem
+    items.Preview                = callback: -> appManager.tell 'IDE', 'previewFile'
+    items['Find...']             = callback: -> appManager.tell 'IDE', 'showFindReplaceView'
+    items['Find and replace...'] = callback: -> appManager.tell 'IDE', 'showFindReplaceView', yes
+    items['Find in files...']    = callback: -> appManager.tell 'IDE', 'showContentSearch'
+    items['Jump to file...']     = callback: -> appManager.tell 'IDE', 'showFileFinder'
+    items['Go to line...']       = callback: -> appManager.tell 'IDE', 'goToLine'

     return items
-
-  addEditorMenuItems: (items, options, appManager) ->
-    items.Save                = callback: -> appManager.tell 'IDE', 'saveFile'
-    items['Save As...']       = callback: -> appManager.tell 'IDE', 'saveAs'
-    items['Save All']         = callback: -> appManager.tell 'IDE', 'saveAllFiles'
-    items.customView          = @syntaxSelector = new IDE.SyntaxSelectorMenuItem
-    items.Preview             = callback: -> appManager.tell 'IDE', 'previewFile'
-    items['More...']          =
-      children                :
-        'Find...'             :
-          callback            : -> appManager.tell 'IDE', 'showFindReplaceView'
-        'Find and replace...' :
-          callback            : -> appManager.tell 'IDE', 'showFindReplaceView', yes
-        'Find in files...'    :
-          callback            : -> appManager.tell 'IDE', 'showContentSearch'
-        'Jump to file...'     :
-          callback            : -> appManager.tell 'IDE', 'showFileFinder'
-        'Go to line...'       :
-          callback            : -> appManager.tell 'IDE', 'goToLine'
-      separator               : yes
-
-  addDefaultMenuItems: (items, options, appManager) ->
-    items.Shortcuts  = callback: -> appManager.tell 'IDE', 'showShortcutsView'
-
-    items.Contribute = callback: -> KD.utils.createExternalLink 'https://github.com/koding/IDE'
-
-    items.Quit       = callback: ->
-      mainView = KD.getSingleton 'mainView'
-      appManager.quit appManager.getFrontApp()
-      KD.getSingleton('router').handleRoute '/Activity'


Override global jasmine spec functions

Override global jasmine spec functions

Currently, if a spec uses the global `it` function on an async test,
that test will always pass (since the jasmine version checked in Atom
does not natively support tests that return promises). This can be
confusing since the test behaviour is different between the
async-test-helpers methods and the global ones.

By overriding the global functions, we'll also be able to remove all the
imports from async-test-helpers since they won't be needed anymore.

More info: https://github.com/atom/atom/pull/18896#discussion_r260396102

--- spec/jasmine-test-runner.coffee
+++ spec/jasmine-test-runner.coffee
@@ -9,6 +9,16 @@
 module.exports = ({logFile, headless, testPaths, buildAtomEnvironment}) ->
   window[key] = value for key, value of require '../vendor/jasmine'
   require 'jasmine-tagged'
+
+  # Rewrite global jasmine functions to have support for async tests.
+  # This way packages can create async specs without having to import these from the
+  # async-spec-helpers file.
+  global.it = asyncifyJasmineFn global.it, 1
+  global.fit = asyncifyJasmineFn global.fit, 1
+  global.ffit = asyncifyJasmineFn global.ffit, 1
+  global.fffit = asyncifyJasmineFn global.fffit, 1
+  global.beforeEach = asyncifyJasmineFn global.beforeEach, 0
+  global.afterEach = asyncifyJasmineFn global.afterEach, 0

   # Allow document.title to be assigned in specs without screwing up spec window title
   documentTitle = null
@@ -58,6 +68,28 @@

   jasmineEnv.execute()
   promise
+
+asyncifyJasmineFn = (fn, callbackPosition) ->
+  (args...) ->
+    if typeof args[callbackPosition] is 'function'
+      callback = args[callbackPosition]
+
+      args[callbackPosition] = (args...) ->
+        result = callback.apply this, args
+        if result instanceof Promise
+          waitsForPromise(-> result)
+
+    fn.apply this, args
+
+waitsForPromise = (fn) ->
+  promise = fn()
+
+  global.waitsFor('spec promise to resolve', (done) ->
+    promise.then(done, (error) ->
+      jasmine.getEnv().currentSpec.fail error
+      done()
+    )
+  )

 disableFocusMethods = ->
   ['fdescribe', 'ffdescribe', 'fffdescribe', 'fit', 'ffit', 'fffit'].forEach (methodName) ->
@@ -124,4 +156,4 @@
     new JasmineListReporter(options)
   else
     {TerminalReporter} = require 'jasmine-tagged'
-    new TerminalReporter(options)
+    new TerminalReporter(options)


Add regression test for delete key

Add regression test for delete key

--- spec/keymap-manager-spec.coffee
+++ spec/keymap-manager-spec.coffee
@@ -384,6 +384,7 @@
         expect(keymapManager.keystrokeForKeyboardEvent(keydownEvent('*'))).toBe '*'
         expect(keymapManager.keystrokeForKeyboardEvent(keydownEvent('left'))).toBe 'left'
         expect(keymapManager.keystrokeForKeyboardEvent(keydownEvent('\b'))).toBe 'backspace'
+        expect(keymapManager.keystrokeForKeyboardEvent(keydownEvent('U+007F', keyCode: 46))).toBe 'delete'

     describe "when a modifier key is combined with a non-modifier key", ->
       it "returns a string that identifies the modified keystroke", ->
@@ -404,6 +405,7 @@
         expect(keymapManager.keystrokeForKeyboardEvent(keydownEvent('PageDown', keyCode: 34, location: 3))).toBe 'num-pagedown'
         expect(keymapManager.keystrokeForKeyboardEvent(keydownEvent('PageUp', keyCode: 33, location: 3))).toBe 'num-pageup'
         expect(keymapManager.keystrokeForKeyboardEvent(keydownEvent('U+004B', keyCode: 107, location: 3))).toBe 'num-+'
+        expect(keymapManager.keystrokeForKeyboardEvent(keydownEvent('U+007F', keyCode: 46, location: 3))).toBe 'num-delete'

     describe "when a non-English keyboard language is used", ->
       it "uses the physical character pressed instead of the character it maps to in the current language", ->


Cancel modals in afterEach in UploadDialog spec.

--- cms/static/coffee/spec/views/upload_spec.coffee
+++ cms/static/coffee/spec/views/upload_spec.coffee
@@ -15,8 +15,7 @@

         afterEach ->
             delete CMS.URL.UPLOAD_ASSET
-            if (@lastView && modal_helpers.isShowingModal(@lastView))
-                @lastView.hide()
+            modal_helpers.cancelModalIfShowing()

         createTestView = (test) ->
             view = new UploadDialog(
@@ -99,6 +98,7 @@
                 @clock = sinon.useFakeTimers()

             afterEach ->
+                modal_helpers.cancelModalIfShowing()
                 @clock.restore()

             it "can upload correctly", ->


Fix spec

Fix spec

--- spec/unpoly/element_spec.coffee
+++ spec/unpoly/element_spec.coffee
@@ -1046,7 +1046,7 @@
       expect(element).toBeVisible()

     it 'restores the { display } property the element had before hiding', ->
-      element = fixture('div', style: { display: 'flex '})
+      element = fixture('div', style: { display: 'flex'})
       expect(getComputedStyle(element).display).toBe('flex')
       up.element.hide(element)
       expect(element).not.toBeVisible()


Minor change

Minor change

--- lib/assets/javascripts/async_notifications/loader.js.coffee
+++ lib/assets/javascripts/async_notifications/loader.js.coffee
@@ -1,5 +1,5 @@
-#= require async_notifications/helper
 #= require bootstrap-notify
 #= require danthes
 #= require mustache
 #= require jquery.mustache
+#= require async_notifications/helper


Minor fix, store full property in @_asyncProperties

--- src/access/active-record.coffee
+++ src/access/active-record.coffee
@@ -72,8 +72,8 @@
     defineAsyncProperty: (key, property) ->
         F.demandGoodString(key, 'key')
         F.demandGoodObject(property, 'property')
-
-        @_asyncProperties[key] = { get: property.get, set: property.set }
+
+        @_asyncProperties[key] = property

         Object.defineProperty(@, key, {
             configurable: property.configurable?


Groups: Fixes undefined KONFIG error

Groups: Fixes undefined KONFIG error

--- workers/social/lib/social/models/referral/index.coffee
+++ workers/social/lib/social/models/referral/index.coffee
@@ -4,6 +4,8 @@
 JUser = require '../user'
 JVM = require '../vm'
 KodingError = require '../../error'
+{argv}   = require 'optimist'
+KONFIG = require('koding-config-manager').load("main.#{argv.c}")

 module.exports = class JReferral extends jraphical.Message


Reverted toLowerCase to match PSR rules

Reverted toLowerCase to match PSR rules

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

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


sending all events to particles

sending all events to particles

--- test/timed_events.coffee
+++ test/timed_events.coffee
@@ -1,7 +1,10 @@
 module "TimedEvents"

 test "#every", ->
-  gameObject = GameObject
+  gameObject = GameObject()
+
+  gameObject.every
+

 # Clear out the module
 module()


fix error messages

fix error messages

--- test/files/unbox_cant_verify.iced
+++ test/files/unbox_cant_verify.iced
@@ -63,6 +63,6 @@
   await unbox { armored : msg , keyfetch : km, strict : false }, defer err, literals, warnings
   T.no_error err
   wv = warnings.warnings()
-  T.equal wv, [ 'Problem fetching key 11e8aed7fa9fbb74: Error: No keys match the given fingerprint'], "the right warnings"
+  T.equal wv, [ "Problem fetching key 11e8aed7fa9fbb74: Error: Can't find a key for 11e8aed7fa9fbb74: No keys match the given key IDs"], "the right warnings"
   T.equal literals[0].toString(), "hi, this is a test message\n", "the right message decrypted"
   cb()


minor edits to rounds spec

minor edits to rounds spec

--- test/resources/rounds_spec.coffee
+++ test/resources/rounds_spec.coffee
@@ -11,6 +11,6 @@
 	  global.Round = window.Cobudget.Resources.Round(Restangular)

 	describe 'get', ->
-		it 'returns a round based on the id', ->
-			global.Round.get(1)
+		it 'calls one rounds with id', ->
+			Round.get(1)
 			expect(Restangular.one).to.have.been.calledWith('rounds', 1)


Adding a blind command.

Adding a blind command.

--- scripts/common.coffee
+++ scripts/common.coffee
@@ -7,6 +7,7 @@
 #   hubot commands    - Shows a current list of commands
 #   hubot bot         - A quick bot about/introduction
 #   hubot currency    - An explanation of points and the rate.
+#   hubot blind       - A warning about spoilers and backseating.

 module.exports = (robot) ->
     robot.respond /schedule$/i, (msg) ->
@@ -29,3 +30,6 @@
         if robot.auth.hasRole(msg.envelope.user, ['admin', 'moderator'])
             msg.send "We use a points currency here. You get 5 points for every 15 minutes spent in the channel whilst we're live and 1 point per 15 minutes for time spent in here when offline. You can do !points to see your current balance. Points are used for our raffle system to buy entrance tickets!"
             return
+
+    robot.respond /blind$/i, (msg) ->
+        msg.send "This game is being played through for the first time. Mistakes will happen. Frequently. Please do not ruin it by posting spoilers/hints/tips of any kind."


Delete devide.coffee

Delete devide.coffee

--- scripts/devide.coffee
+++ scripts/devide.coffee
@@ -1,25 +0,0 @@
-# Description:
-#   Allows Hubot to help you decide between multiple options
-#
-# Dependencies:
-#   None
-#
-# Configuration:
-#   None
-#
-# Commands:
-#   hubot decide "<option 1>" "<option 2>" "<option x>" - Randomly picks an option
-#   hubot decide <option1> <option2> <option3> - Randomly picks an option
-#
-# Author:
-#   logikal
-#   streeter
-
-module.exports = (robot) ->
-    robot.respond /decide "(.*)"/i, (msg) ->
-        options = msg.match[1].split('" "')
-        msg.reply("Definitely \"#{ msg.random options }\".")
-
-    robot.respond /decide ([^"]+)/i, (msg) ->
-        options = msg.match[1].split(' ')
-msg.reply("Definitely \"#{msg.random options}\".")


Fixed an additional bracket on uploadfield

--- snippets/php_fields.cson
+++ snippets/php_fields.cson
@@ -5,7 +5,7 @@
     'body': 'HtmlEditorField::create(\'${1:name}\',\'${2:title}\')$0'
   'UploadField':
     'prefix': 'uploadfield'
-    'body': 'UploadField::create(\'${1:name}\',\'${2:title}\'))$0'
+    'body': 'UploadField::create(\'${1:name}\',\'${2:title}\')$0'
   'TextField':
     'prefix': 'textfield'
     'body': 'TextField::create(\'${1:name}\',\'${2:title}\')$0'


Be sure when a message is selected enableMessageActionButtons

--- client/app/Applications/Inbox.kdapplication/AppController.coffee
+++ client/app/Applications/Inbox.kdapplication/AppController.coffee
@@ -102,7 +102,7 @@
           paneView.addSubView detail
           paneView.detail = detail
           item.paneView = paneView
-
+        newMessageBar.enableMessageActionButtons()
         @selectMessage data, item, paneView

     newMessageBar.registerListener


validate captcha on every email addition

--- services/web/public/coffee/ide/share/controllers/ShareProjectModalController.coffee
+++ services/web/public/coffee/ide/share/controllers/ShareProjectModalController.coffee
@@ -96,50 +96,50 @@
 					if email in currentMemberEmails
 						# Skip this existing member
 						return addNextMember()
-
-					if email in currentInviteEmails and inviteId = _.find(($scope.project.invites || []), (invite) -> invite.email == email)?._id
-						request = projectInvites.resendInvite(inviteId)
-					else
-						request = projectInvites.sendInvite(email, $scope.inputs.privileges, $scope.grecaptchaResponse)
-
-					request
-						.then (response) ->
-							{ data } = response
-							if data.error
+					validateCaptcha (response) ->
+						$scope.grecaptchaResponse = response
+						if email in currentInviteEmails and inviteId = _.find(($scope.project.invites || []), (invite) -> invite.email == email)?._id
+							request = projectInvites.resendInvite(inviteId)
+						else
+							request = projectInvites.sendInvite(email, $scope.inputs.privileges, $scope.grecaptchaResponse)
+
+						request
+							.then (response) ->
+								{ data } = response
+								if data.error
+									$scope.state.error = true
+									$scope.state.errorReason = "#{data.error}"
+									$scope.state.inflight = false
+								else
+									if data.invite
+										invite = data.invite
+										$scope.project.invites.push invite
+									else
+										if data.users?
+											users = data.users
+										else if data.user?
+											users = [data.user]
+										else
+											users = []
+										$scope.project.members.push users...
+
+								setTimeout () ->
+									# Give $scope a chance to update $scope.canAddCollaborators
+									# with new collaborator information.
+									addNextMember()
+								, 0
+							.catch (httpResponse) ->
+								{data, status, headers, config } = httpResponse
+								$scope.state.inflight = false
 								$scope.state.error = true
-								$scope.state.errorReason = "#{data.error}"
-								$scope.state.inflight = false
-							else
-								if data.invite
-									invite = data.invite
-									$scope.project.invites.push invite
+
+								if data?.errorReason?
+									$scope.state.errorReason = data?.errorReason
 								else
-									if data.users?
-										users = data.users
-									else if data.user?
-										users = [data.user]
-									else
-										users = []
-									$scope.project.members.push users...
-
-							setTimeout () ->
-								# Give $scope a chance to update $scope.canAddCollaborators
-								# with new collaborator information.
-								addNextMember()
-							, 0
-						.catch (httpResponse) ->
-							{data, status, headers, config } = httpResponse
-							$scope.state.inflight = false
-							$scope.state.error = true
-
-							if data?.errorReason?
-								$scope.state.errorReason = data?.errorReason
-							else
-								$scope.state.errorReason = null
-
-			validateCaptcha (response) ->
-				$scope.grecaptchaResponse = response
-				$timeout addMembers, 50 # Give email list a chance to update
+									$scope.state.errorReason = null
+
+
+			$timeout addMembers, 50 # Give email list a chance to update

 		$scope.removeMember = (member) ->
 			$scope.state.error = null


Support for optional views

Support for optional views

--- components/tooltip/KDTooltip.coffee
+++ components/tooltip/KDTooltip.coffee
@@ -24,7 +24,7 @@

     @avoidDestroy = no
     @visible      = yes
-    @view         = @getDelegate()
+    @view         = options.view or @getDelegate()

     # Container for positioning in the viewport
     @setClass 'kdtooltip'


FIX: Remove log to task pdf url

FIX: Remove log to task pdf url

--- src/app/units/states/tasks/viewer/directives/task-sheet-view/task-sheet-view.coffee
+++ src/app/units/states/tasks/viewer/directives/task-sheet-view/task-sheet-view.coffee
@@ -17,7 +17,6 @@
         if newTask?
           $scope.hasPdf = newTask.has_task_pdf
           $scope.taskPdfUrl = Task.getTaskPDFUrl($scope.unit, $scope.task)
-          console.log($scope.taskPdfUrl)
       setDetails()
 )


Remove modal backdrop on delete

Remove modal backdrop on delete

--- src/scripts/modules/workspace/popovers/delete/modals/delete.coffee
+++ src/scripts/modules/workspace/popovers/delete/modals/delete.coffee
@@ -34,6 +34,8 @@
       .fail (error) ->
         alert("#{error.status}: #{error.statusText}")

+      $('.modal-backdrop').remove() # HACK: Ensure bootstrap modal backdrop is removed
+

     show: () ->
       @render()


Fix typo

Fix typo

--- lib/assets/javascripts/views/app.js.coffee
+++ lib/assets/javascripts/views/app.js.coffee
@@ -50,7 +50,7 @@
         @showSuccess(app_cid, "Successfully deleted #{model.get('content.name')}!")
         Marbles.DOM.removeNode(app_el)

-        if model.get('id') == TentAdmin.config.app.id
+        if model.get('id') == TentAdmin.config.app?.id
           # we just deleted this app, wait a few seconds then signout
           setTimeout(( => window.location.reload()), 2000)


Required winston in Downloader

Required winston in Downloader

--- source/Downloader.coffee
+++ source/Downloader.coffee
@@ -5,6 +5,7 @@

 # Require external libs
 fs = require('fs')
+winston = require('winston')
 request = require('request')
 cheerio = require('cheerio')
 inquirer = require('requirer')


Trying that again.

Trying that again.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -86,9 +86,9 @@
         .get() (err, res, body) ->
             chat = JSON.parse(body)
             people = [chat.chatters.moderators, chat.chatters.staff, chat.chatters.admins, chat.chatters.global_mods, chat.chatters.viewers]
-            timerMethod = setInterval(10000)

-            timerMethod = ->
+            setInterval(->
                 for username in people
-                    points =+ 5
+                    points[username] += 5
                     return
+            , 10000)


removed some unneeded code

removed some unneeded code

--- core/app/backbone/views/autocompleted_add_to_channel_view.coffee
+++ core/app/backbone/views/autocompleted_add_to_channel_view.coffee
@@ -13,7 +13,6 @@
     "click div.auto_complete": "addCurrentlySelectedChannel"
     "click div.fake-input a": "addCurrentlySelectedChannel"
     "click .show-input-button": "showInput"
-    "mouseenter .auto_complete>div": "selectDefaultItem"

   regions:
     'added_channels': 'div.added_channels_container'
@@ -66,14 +65,6 @@
   focusInput: -> @$("input.typeahead").focus()

-  selectDefaultItem: -> console.info 'we could do something here'
-
-  activateAutoCompleteView: (view) -> view.trigger 'requestActivate'
-
-  deActivateAutoCompleteView: ->
-    activeview = @_auto_completes_view.list[@activeChannelKey()]
-    activeview.trigger "deactivate"  if activeview isnt `undefined`
-    @setActiveChannelKey `undefined`

   addCurrentlySelectedChannel: ->
     @disable()


Turn off cron if it's holiday

Turn off cron if it's holiday

--- scripts/yamada.coffee
+++ scripts/yamada.coffee
@@ -129,6 +129,14 @@
         if /JR東日本|東京メトロ|都営地下鉄|東武鉄道|西武鉄道|京成電鉄|京王電鉄|小田急電鉄|東急電鉄|京急電鉄|横浜市営地下鉄|りんかい線|つくばエクスプレス|ゆりかもめ|東京モノレール|日暮里・舎人ライナー/.test($(this).text())
           trains.push($(this).text())
       callback(trains)
+
+  isHoliday = (holidayCallback, elseCallback) ->
+    cheerio-httpcli.fetch 'http://s-proj.com/utils/checkHoliday.php?kind=h', {}, (err, $, res)->
+      date = res.body.toString('utf-8')
+      if date is 'holiday'
+        holidayCallback()
+      else
+        elseCallback()

   # example for calling API
   robot.respond /weather/i, (msg) ->
@@ -189,5 +197,8 @@
   , null, true, "Asia/Tokyo"

   new cron '00 30 17 * * 1-5', () ->
-    robot.send {room: "#general"}, "社畜の通過点"
+    isHoliday () ->
+      robot.send {room: "#general"}, "社畜の通過... おっと休日だったか"
+    , () ->
+      robot.send {room: "#general"}, "社畜の通過点"
   , null, true, "Asia/Tokyo"