updating ui from opts

updating ui from opts

--- src/ubret/sw_viewer.coffee
+++ src/ubret/sw_viewer.coffee
@@ -175,11 +175,18 @@
     @dfs = undefined

     @wfits.setCalibrations(1, 1, 1)
-    scales = @opts.scales or @defaultScales
+
+    # Get settings or fallback to defaults
+    scales  = @opts.scales or @defaultScales
+    alpha   = @opts.alpha or @defaultAlpha
+    Q       = @opts.q or @defaultQ
+    min     = @opts.extent.min or gMin
+    max     = @opts.extent.max or gMax
+
     @wfits.setScales.apply(@wfits, scales)
-    @wfits.setAlpha(@opts.alpha or @defaultAlpha)
-    @wfits.setQ(@opts.q or @defaultQ)
-    @wfits.setExtent(gMin, gMax)
+    @wfits.setAlpha(alpha)
+    @wfits.setQ(Q)
+    @wfits.setExtent(min, max)

     # Enable mouse controls
     @wfits.setupControls({
@@ -194,26 +201,26 @@
         })
     })

-    # Default to color composite
-    @wfits.drawColor('i', 'r', 'g')
-    @trigger 'swviewer:loaded'
-
-  updateBand: =>
-    console.log 'updateBand'
-    if @wfits?
-      band = @opts.band
+    band = @opts.band
+    if band?
       if band is 'gri'
         @wfits.drawColor('i', 'r', 'g')
       else
-        # Compute the min/max of the image set
-        unless @collection.hasExtent
-          mins = @collection.map( (l) -> return l.get('minimum'))
-          maxs = @collection.map( (l) -> return l.get('maximum'))
-          @min = Math.min.apply(Math, mins)
-          @max = Math.max.apply(Math, maxs)
-          @wfits.setExtent(@min, @max)
-          @collection.hasExtent = true
-
+        @wfits.setImage(band)
+        @wfits.setStretch(@opts.stretch or 'linear')
+    else
+      # Default to color composite
+      @wfits.drawColor('i', 'r', 'g')
+
+    @trigger 'swviewer:loaded'
+
+  updateBand: =>
+    band = @opts.band
+
+    if @wfits?
+      if band is 'gri'
+        @wfits.drawColor('i', 'r', 'g')
+      else
         @wfits.setImage(band)
         @wfits.setStretch(@opts.stretch)

@@ -231,10 +238,7 @@
     @wfits?.setStretch(@stretch)

   updateExtent: =>
-    console.log 'updateExtent'
-    min = @opts.extent.min
-    max = @opts.extent.max
-    @wfits?.setExtent(min, max)
+    @wfits?.setExtent(@opts.extent.min, @opts.extent.max)

 window.Ubret.SpacewarpViewer = SpacewarpViewer


better estimation of default text height, labels now line up properly with grid lines

--- src/coffee/bokeh_views.coffee
+++ src/coffee/bokeh_views.coffee
@@ -458,8 +458,8 @@
       y_ticks.push(current_tick)
       #@plot_view.x_can_ctx.moveTo(xpos(current_tick), 0)
       #text_width = can_ctx.measureText(current_tick.toString()).width
-      text_width = 14
-      y = (ypos(current_tick) - (text_width/2))
+      text_width = 8
+      y = (ypos(current_tick) + (text_width/2))
       if y < last_tick_end
         can_ctx.fillText(current_tick.toString(), 0, y)
         last_tick_end = (y + text_width) + 10


reverted changes

reverted changes

--- review_front/app/components/copy-to-clipboard.coffee
+++ review_front/app/components/copy-to-clipboard.coffee
@@ -1,5 +1,5 @@
 `import Ember from 'ember'`
-`import ZeroClipboard from 'ember-cli-zero-clipboard/components/zero-clipboard';`
+`import ZeroClipboard from 'ember-cli-zero-clipboard/components/zero-clipboard'`

 CopyToClipboard = ZeroClipboard.extend
   tagName: 'button'
@@ -7,16 +7,9 @@
   justCopied: false

   justCopiedToggle: ->
-    @set 'justCopied', !@justCopied
+     @justCopiedToggle()
+     Ember.run.later @, (->
+       @justCopiedToggle()
+     ), 1500

-  actions:
-    afterCopy: ->
-      @justCopiedToggle()
-      setTimeout(
-        Ember.run.bind @, (->
-        @justCopiedToggle()
-        ), 1500
-      )
-
-
-`export default CopyToClipboard`
+  `export default CopyToClipboard`


Compiler: correction output path

Compiler: correction output path

--- lib/plugin/compilers/i18n.json.coffee
+++ lib/plugin/compilers/i18n.json.coffee
@@ -115,7 +115,7 @@
         """
       compiler_configuration.templates_registered_for.push helpers.getCompileStepArchAndPackage(compileStep)

-  output_path = input_path.replace /json$/, "js"
+  output_path = compileStep.rootOutputPath + compileStep.inputPath.replace /json$/, "js"
   compileStep.addJavaScript
     path: output_path,
     sourcePath: input_path,


add logging of docker options

add logging of docker options

--- services/clsi/app/coffee/DockerRunner.coffee
+++ services/clsi/app/coffee/DockerRunner.coffee
@@ -146,6 +146,7 @@
 				"SecurityOpt": ["no-new-privileges"]
 		if Settings.clsi.docker.seccomp_profile?
 			options.HostConfig.SecurityOpt.push "seccomp=#{Settings.clsi.docker.seccomp_profile}"
+		logger.log options:options, "options for running docker container"
 		return options

 	_fingerprintContainer: (containerOptions) ->


Don't set pdfDownloadDomain by default

Don't set pdfDownloadDomain by default

--- services/web/config/settings.defaults.coffee
+++ services/web/config/settings.defaults.coffee
@@ -334,7 +334,7 @@
 	# disablePerUserCompiles: true

 	# Domain the client (pdfjs) should download the compiled pdf from
-	pdfDownloadDomain: "http://compiles.sharelatex.test:3014"
+	# pdfDownloadDomain: "http://compiles.sharelatex.test:3014"

 	# Maximum size of text documents in the real-time editing system.
 	max_doc_length: 2 * 1024 * 1024 # 2mb


added a load more currencies

added a load more currencies

--- services/web/public/coffee/main/plans.coffee
+++ services/web/public/coffee/main/plans.coffee
@@ -69,6 +69,78 @@
 					professional:
 						monthly: "$34"
 						annual: "$408"
+
+				NOK:
+					symbol: "kr"
+					student:
+						monthly: "60 kr"
+						annual: "600 kr"
+					collaborator:
+						monthly: "110 kr"
+						annual: "1320 kr"
+					professional:
+						monthly: "220 kr"
+						annual: "2640 kr"
+
+				DKK:
+					symbol: "kr"
+					student:
+						monthly: "50 kr"
+						annual: "500 kr"
+					collaborator:
+						monthly: "90 kr"
+						annual: "1080 kr"
+					professional:
+						monthly: "180 kr"
+						annual: "2160 kr"
+
+				AUD:
+					symbol: "$"
+					student:
+						monthly: "$10"
+						annual: "$100"
+					collaborator:
+						monthly: "$18"
+						annual: "$216"
+					professional:
+						monthly: "$35"
+						annual: "$420"
+
+				NZD:
+					symbol: "$"
+					student:
+						monthly: "$10"
+						annual: "$100"
+					collaborator:
+						monthly: "$18"
+						annual: "$216"
+					professional:
+						monthly: "$35"
+						annual: "$420"
+
+				CHF:
+					symbol: "Fr"
+					student:
+						monthly: "CHF 8"
+						annual: "CHF 80"
+					collaborator:
+						monthly: "CHF 15"
+						annual: "CHF 180"
+					professional:
+						monthly: "CHF 30"
+						annual: "CHF 360"
+
+				SGD:
+					symbol: "$"
+					student:
+						monthly: "$12"
+						annual: "$120"
+					collaborator:
+						monthly: "$~"
+						annual: "$240"
+					professional:
+						monthly: "$40"
+						annual: "$480"
 		}


proper logging for join-patr

proper logging for join-patr

--- scripts/eslogger_watcher.coffee
+++ scripts/eslogger_watcher.coffee
@@ -53,12 +53,10 @@

     robot.enter (res) ->
       msg = res.envelope.message
-      line = "#{msg.user.name} left #{msg.room} (#{msg.text})"
-      console.log line
-      console.log msg.user
+      line = "#{msg.user.name} has joined #{msg.room} (#{msg.text})"
+      eslogger.logMessageES line, msg.room, res

     robot.leave (res) ->
       msg = res.envelope.message
-      line = "#{msg.user.name} joined #{msg.room}"
-      console.log line
-      console.log msg.user
+      line = "#{msg.user.name} has quit #{msg.room}"
+      eslogger.logMessageES line, msg.room, res


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_state_cameras.coffee
+++ src/game_state_cameras.coffee
@@ -2,7 +2,7 @@
   cameras = [Camera()]

   self.bind 'afterUpdate', ->
-    self.cameras().invoke 'trigger'
+    self.cameras().invoke 'trigger', "a"
       camera.trigger('afterUpdate')

   self.bind 'draw', (canvas) ->


Inlined more methods from IconButton

Inlined more methods from IconButton

--- js-library/app/js/jail_iframe/classes/icon_buttons.coffee
+++ js-library/app/js/jail_iframe/classes/icon_buttons.coffee
@@ -1,20 +1,8 @@
 class IconButton
-
-  constructor: ->
-    @frame = new FactlinkJailRoot.ControlIframe()
-    @frame.setContent($.parseHTML(@content.trim())[0])
-    @$el = $(@frame.frameBody.firstChild)
-
   destroy: =>
-    @frame.destroy()
-    @_robustHover.destroy()
     $(window).off 'resize', @_updatePosition
     clearInterval @_interval
     @$boundingBox?.remove()
-
-  _bindCallbacks: (callbacks) ->
-    @_robustHover = new FactlinkJailRoot.RobustHover @$el, callbacks
-    @$el.on 'click', -> callbacks.click?()

   # TODO: do all updates of different buttons in one pass
   _updatePositionRegularly: ->
@@ -27,15 +15,18 @@
   content: '

'

   constructor: (highlightElements, factId) ->
-    super
+    @frame = new FactlinkJailRoot.ControlIframe()
+    @frame.setContent($.parseHTML(@content.trim())[0])
+    $el = $(@frame.frameBody.firstChild)

     @$highlightElements = $(highlightElements)
     @_factId = factId

-    @_bindCallbacks
+    @_robustHover = new FactlinkJailRoot.RobustHover $el,
       mouseenter: @_onHover
       mouseleave: @_onUnhover
-      click:      @_onClick
+    $el.on 'click', @_onClick
+
     @$highlightElements.on 'mouseenter', @_onHover
     @$highlightElements.on 'mouseleave', @_onUnhover
     @$highlightElements.on 'click', @_onClick
@@ -45,11 +36,11 @@

   destroy: ->
     super
-
+    @_robustHover.destroy()
     @$highlightElements.off 'mouseenter', @_onHover
     @$highlightElements.off 'mouseleave', @_onUnhover
     @$highlightElements.off 'click', @_onClick
-
+    @frame.destroy()

   _onHover: =>
     @frame.addClass 'hovered'
@@ -86,15 +77,17 @@
 class ParagraphButton extends IconButton
   content: '
+
'

-  constructor: (options) ->
-    super
+  constructor: (paragraphElement) ->
+    @frame = new FactlinkJailRoot.ControlIframe()
+    @frame.setContent($.parseHTML(@content.trim())[0])
+    $el = $(@frame.frameBody.firstChild)

-    @_bindCallbacks
+    @_robustHover = new FactlinkJailRoot.RobustHover $el,
       mouseenter: => @frame.addClass 'hovered'
       mouseleave: => @frame.removeClass 'hovered'
-      click: => @_onClick()
+    $el.on 'click', @_onClick

-    @$paragraph = $(options.el)
+    @$paragraph = $(paragraphElement)

     @frame.fadeIn()
     @_updatePositionRegularly()
@@ -103,6 +96,8 @@

   destroy: ->
     super
+    @_robustHover.destroy()
+    @frame.destroy()
     FactlinkJailRoot.off 'factlink.factsLoaded factlinkAdded', @_destroyIfContainsFactlink

   _destroyIfContainsFactlink: =>
@@ -120,7 +115,7 @@
       @$boundingBox?.remove()
       @$boundingBox = FactlinkJailRoot.drawBoundingBox contentBox, 'green'

-  _onClick: ->
+  _onClick: =>
     text = @$paragraph.text()
     siteTitle = window.document.title
     siteUrl = FactlinkJailRoot.siteUrl()
@@ -146,7 +141,7 @@
   _addParagraphButton: (el) ->
     return unless @_paragraphHasContent(el)

-    @_paragraphButtons.push new ParagraphButton el: el
+    @_paragraphButtons.push new ParagraphButton el

   addParagraphButtons: ->
     return unless FactlinkJailRoot.can_haz.paragraph_icons


ensure message handlers bind to the meteor environment

--- source/infrastructure/message_handler.coffee
+++ source/infrastructure/message_handler.coffee
@@ -5,6 +5,7 @@
     eventBus: 'Space.cqrs.EventBus'
     commandBus: 'Space.cqrs.CommandBus'
     util: 'underscore'
+    meteor: 'Meteor'

   @toString: -> 'Space.cqrs.MessageHandler'

@@ -14,10 +15,12 @@
   onDependenciesReady: ->

     for type, eventHandler of @constructor._eventHandlers
-      @eventBus.subscribe type, @util.bind(eventHandler, this)
+      eventHandler = @util.bind(eventHandler, this)
+      @eventBus.subscribe type, @meteor.bindEnvironment(eventHandler)

     for type, commandHandler of @constructor._commandHandlers
-      @commandBus.registerHandler type, @util.bind(commandHandler, this)
+      commandHandler = @util.bind(commandHandler, this)
+      @commandBus.registerHandler type, @meteor.bindEnvironment(commandHandler)

   @handle: (messageType, handler) ->


Added url for NDPEvidenceCollection

Added url for NDPEvidenceCollection

--- core/app/backbone/collections/ndp_evidence_collection.coffee
+++ core/app/backbone/collections/ndp_evidence_collection.coffee
@@ -13,3 +13,6 @@
       ]

   comparator: (item) -> - item.get('impact')
+
+  url: ->
+    '/facts/#{@fact.id}/interactors'


Ignore discussions with no timestamp

Ignore discussions with no timestamp

--- resources/assets/coffee/_classes/beatmap-discussion-helper.coffee
+++ resources/assets/coffee/_classes/beatmap-discussion-helper.coffee
@@ -123,7 +123,7 @@
     nearby = {}

     for discussion in discussions
-      continue if discussion.message_type not in ['suggestion', 'problem']
+      continue if not discussion.timestamp or discussion.message_type not in ['suggestion', 'problem']

       distance = Math.abs(discussion.timestamp - timestamp)


Minor code improvements

Minor code improvements

--- server/controllers/deals.coffee
+++ server/controllers/deals.coffee
@@ -2,16 +2,13 @@
 async     = require "async"
 _         = require "underscore"
 Utilities = require "../tools/utilities"
-mongoose = require "mongoose"
-async    = require "async"
-_        = require "underscore"
-Path     = require "path"
-fs       = require "fs"
+Path      = require "path"
+fs        = require "fs"

-Deal     = mongoose.model "Deal"
-Shop     = mongoose.model "Shop"
-User     = mongoose.model "User"
-Reservation     = mongoose.model('Reservation')
+Deal        = mongoose.model "Deal"
+Shop        = mongoose.model "Shop"
+User        = mongoose.model "User"
+Reservation = mongoose.model "Reservation"

 exports.deal = (req, res, next, id) ->


Fix fragment_spec

Fix fragment_spec

--- spec_app/spec/javascripts/up/fragment_spec.js.coffee
+++ spec_app/spec/javascripts/up/fragment_spec.js.coffee
@@ -1665,8 +1665,9 @@

           it 'allows to refer to the replacement { origin } as "&" in the { failTarget } selector', asyncSpec (next) ->
             $origin = $fixture('.origin').text('origin text')
-            $failTarget = $fixture('.fail-target').text('old fail-target text')
-            up.change('.middle', url: '/path', failTarget: '.fail-target', reveal: false, failReveal: '&', origin: $origin)
+            $fixture('.target').text('old target text')
+            $fixture('.fail-target').text('old fail-target text')
+            up.change('.target', url: '/path', failTarget: '.fail-target', reveal: false, failReveal: '&', origin: $origin)

             next =>
               @respondWith


XY: Correct Protean message

XY: Correct Protean message

--- server/xy/data/abilities.coffee
+++ server/xy/data/abilities.coffee
@@ -117,7 +117,7 @@
     type = move.getType(@battle, user, targets[0])
     return  if user.types.length == 1 && user.types[0] == type
     user.types = [ type ]
-    @battle.message "#{user.name} changed into the #{type} type!"
+    @battle.message "#{user.name} transformed into the #{type} type!"

 makeAbility 'Stance Change', ->
   this::beforeMove = (move, user, targets) ->


Fix spec

Fix spec

--- app/assets/javascripts/issues-bulk-assignment.js.coffee
+++ app/assets/javascripts/issues-bulk-assignment.js.coffee
@@ -29,7 +29,7 @@
             data: @getFormDataAsObject()

     xhr.done (response, status, xhr) ->
-      Turbolinks.visit(location.href)
+      location.reload()

     xhr.fail ->
       console.error 'fail'


dashboard popup: removed leftover

dashboard popup: removed leftover

--- app/assets/javascripts/lib/views/constraint_view.coffee
+++ app/assets/javascripts/lib/views/constraint_view.coffee
@@ -36,10 +36,6 @@
     constraint_id = @model.get('id')
     key = @model.get('key')
     url = $(constraint).attr('href')
-    height = switch key
-      when 'net_energy_import' then 485
-      when 'targets_met' then 430
-      else 450
     $.fancybox.open
       autoSize: false
       href: url
@@ -47,7 +43,6 @@
       width: 600
       height: 480
       padding: 0
-

   # Formats the result of calculate_result() for the end-user
   format_result: () =>


EDSC-490: Do not treat mouseup after dragging as a click event.

--- app/assets/javascripts/modules/timeline/timeline.coffee
+++ app/assets/javascripts/modules/timeline/timeline.coffee
@@ -443,7 +443,7 @@
         overlay.appendChild(right)
       else
         root.trigger(@scopedEventName('focusremove'))
-
+      @_forceRedraw()
       null

     panToTime: (time) ->
@@ -491,6 +491,7 @@
       [@positionToTime(x0), @positionToTime(x1) - 1]

     _onLabelClick: (e) =>
+      return if @_dragging
       label = e.currentTarget
       [start, stop] = @_timespanForLabel(label)
       if @_canFocusTimespan(start, stop)
@@ -632,8 +633,10 @@
       self = this
       draggable = new TimelineDraggable(svg)
       draggable.on 'drag', ({dx}) =>
+        @_dragging = true if Math.abs(dx) > 5
         @_pan(dx, false)
       draggable.on 'dragend', ({dx}) =>
+        @_dragging = false
         @_pan(dx)

     _pan: (dx, commit=true) ->


don't send group creation request with no files

--- app/assets/javascripts/uploadcare/files/group.js.coffee
+++ app/assets/javascripts/uploadcare/files/group.js.coffee
@@ -126,13 +126,16 @@

     __createGroup: ->
       df = $.Deferred()
-      @__fileInfos (infos...) =>
-        data =
-          pub_key: @settings.publicKey
-        for info, i in infos
-          data["files[#{i}]"] = info.uuid
-        $.ajax("#{@settings.urlBase}/group/", {data, dataType: 'jsonp'})
-          .then(df.resolve, df.reject)
+      if @__files.length
+        @__fileInfos (infos...) =>
+          data =
+            pub_key: @settings.publicKey
+          for info, i in infos
+            data["files[#{i}]"] = info.uuid
+          $.ajax("#{@settings.urlBase}/group/", {data, dataType: 'jsonp'})
+            .then(df.resolve, df.reject)
+      else
+        df.reject()
       return df.promise()


refactored retrieveState method into multiple smaller methods for simplicity/clarity

--- public/dreamcatcher/controllers/entries_controller.coffee
+++ public/dreamcatcher/controllers/entries_controller.coffee
@@ -5,7 +5,7 @@
   userModel: Dreamcatcher.Models.User

   init: ->
-    @entryCookie = new Dreamcatcher.Classes.CookieHelper 'dc_new_entry',true
+    @entryCookie = new Dreamcatcher.Classes.CookieHelper "dc_new_entry",true
     @posted = false
     @retrieveState()
     @initSelectMenu()
@@ -15,32 +15,43 @@
     return if @posted
     entryBody = $('#entry_body').val().trim()
     if entryBody.length > 0
-      entry = {}
-      entry_type = $('#entryMode').data 'id'
-      if entry_type is 'new'
-        entry[field] = $(field).val() for field in @fields
-        entry['#currentImages'] = $('#currentImages').html()
-        @entryCookie.set entry
+      entry = {}
+      entry['type'] = $('#entryMode').data 'id'
+      entry['id'] = $('#entryId').data 'id'
+
+      entry[field] = $(field).val() for field in @fields
+      entry['#currentImages'] = $('#currentImages').html()
+      @entryCookie.set entry
     setTimeout =>
       @saveState()
     , @interval

   retrieveState: ->
     entry = @entryCookie.get()
-    if entry?
-      # log "entry[type] #{entry['type']} entry[id]: #{entry['id']}"
-      # populate form with saved state then confirm they want to use it
-      $(field).val entry[field] for field in @fields
-      $('#currentImages').html entry['#currentImages']
-      @stateRetrieved = true
+    currentEntryType = $('#entryMode').data 'id'
+    if entry?
+      if entry['type'] is currentEntryType  # make sure our cookie entry type matches new or edit like the current form
+        currentEntryId = parseInt($('#entryId').data('id')) if currentEntryType is 'edit'

-      # reset form unless confirmation to use saved state
-      unless confirm 'you have an unsaved entry.\n\nwould you like to use it?'
-        log 'clearing form'
-        $(field).val '' for field in @fields
-        $('#currentImages').html ''
+      @populateForm(entry) unless currentEntryType is 'edit' and currentEntryId isnt entry['id']
+      @clearState()

-    @clearState()
+      log "entry[type] #{entry['type']} entry[id]: #{entry['id']} currentEntryType: #{currentEntryType} currentEntryId: #{currentEntryId}"
+
+
+  # populate form with saved state so they can see it, then confirm they want to use it
+  populateForm: (entry) ->
+    $(field).val entry[field] for field in @fields
+    $('#currentImages').html entry['#currentImages']
+    @stateRetrieved = true
+    @clearForm() unless confirm 'you have an unsaved entry.\n\nwould you like to use it?' # reset form unless confirmation
+
+
+  clearForm: ->
+    log 'clearing form'
+    $(field).val '' for field in @fields
+    $('#currentImages').html ''
+    @stateRetrieved = false

   clearState: ->
     @entryCookie.clear()


Flip check?

Flip check?

--- resources/assets/coffee/_classes/forum-topic-reply.coffee
+++ resources/assets/coffee/_classes/forum-topic-reply.coffee
@@ -152,7 +152,7 @@

   toggle: =>
-    if @getState('active') == '0'
+    if @getState('active') == '1'
+      @deactivate()
+    else
       @activate()
-    else
-      @deactivate()


Link beatmap source to search

Link beatmap source to search

--- resources/assets/coffee/react/beatmapset-page/info.coffee
+++ resources/assets/coffee/react/beatmapset-page/info.coffee
@@ -159,7 +159,9 @@
               className: 'beatmapset-info__header'
               osu.trans 'beatmapsets.show.info.source'

-            div null, @props.beatmapset.source
+            a
+              href: laroute.route('beatmapsets.index', q: @props.beatmapset.source)
+              @props.beatmapset.source

         div className: 'beatmapset-info__half-box',
           div className: 'beatmapset-info__half-entry',


fix a issue that cannot unset infinite option

--- assets/javascripts/backbone/view/extension/store.coffee
+++ assets/javascripts/backbone/view/extension/store.coffee
@@ -76,7 +76,8 @@
       slice: 10

       onReset: (collection, options) ->
-        @remove(cid: cid) for cid, template of @views
+        for cid, template of @views
+          (template.$el || template).hide()
         @$container.scrollTop(0)
         @infinite.length = 0
         @infinite.models = if @infinite.attributes?
@@ -104,16 +105,21 @@
           height

         @infinite.height ||= @$selector.height() / @infinite.length
-        while getHeight() < @infinite.height * @options.slice
+        while getHeight() < @infinite.height * @options.slice and @infinite.length < @infinite.models.length
           @show(@infinite.length + @options.slice)

         @onScrollWorking = false

       onShow: (length) ->
-        return if length < @infinite.length
         models = @infinite.models[@infinite.length...length]
         @infinite.length = length
-        @add(model) for model in models
+        for model in models
+          template = @views[model.cid]
+          if template?
+            template.$el.appendTo(@$selector)
+            (template.$el || template).show()
+          else
+            @add(model)
         if @$suffix?
           height = (@infinite.models.length - @infinite.length) * @infinite.height
           height = if height > 0 then height else 0


nospace had a space

nospace had a space

--- src/parser/java-parser.coffee
+++ src/parser/java-parser.coffee
@@ -179,7 +179,7 @@
                   //...
                 }

-                switch (v) {
+                switch(v) {
                   //...
                 }
                 """


fix(skill list): sort skill list by rank in graph

This is better than general topsort, because it yields skills listed in
(roughly) the order students should be doing them.

--- src/app/skill/resource/skill.resource.coffee
+++ src/app/skill/resource/skill.resource.coffee
@@ -74,7 +74,24 @@
     list: () ->
       deferred = $q.defer()
       listPromise = listPromise.then ()->
-        deferred.resolve(graphlib.alg.topsort(graph))
+
+        list = []
+
+        addRank = (nodes)->
+          return if _.every(nodes, (node)->graph.outEdges(node).length == 0)
+
+          nodes = _.sortBy(nodes, (node)->-graph.outEdges(node).length)
+          for node in nodes
+            if list.indexOf(node) < 0
+              list.push(node)
+
+          nextRank = _.uniq(_.flatten(nodes.map((node)->graph.successors(node)),true))
+
+          addRank(nextRank)
+
+        addRank(graph.sources())
+
+        deferred.resolve(list)
       deferred.promise

     ###


Compiling issue with relative path name

Compiling issue with relative path name

I had an issue with "rake db:run" that begins:
ActionView::Template::Error: require_tree argument must be a relative path
  (in /home/max/Code/potlucky/spec/javascripts/spec_helper.js.coffee:5)
This change resolved that issue for me. Thanks for the great project!

--- lib/generators/ember_konacha/templates/spec_helper.js.coffee
+++ lib/generators/ember_konacha/templates/spec_helper.js.coffee
@@ -2,7 +2,7 @@
 #= require sinon

 # Load all testing support files
-#= require_tree support
+#= require_tree ./support

 #= require application

@@ -74,4 +74,4 @@

   # Restore XHR
   window.server.restore()
-)+)


Allow all the homepage rail!

Allow all the homepage rail!

--- src/desktop/apps/home/queries/initial.coffee
+++ src/desktop/apps/home/queries/initial.coffee
@@ -2,7 +2,8 @@
   query($showHeroUnits: Boolean!) {
     home_page {
       artwork_modules(
-        max_rails: 7,
+        max_rails: -1,
+        max_followed_gene_rails: -1,
         order: [
           ACTIVE_BIDS,
           RECENTLY_VIEWED_WORKS


Add specs for editor.scrollPastEnd behavior

--- spec/display-buffer-spec.coffee
+++ spec/display-buffer-spec.coffee
@@ -1100,6 +1100,33 @@
       expect(displayBuffer.setScrollTop(maxScrollTop + 50)).toBe maxScrollTop
       expect(displayBuffer.getScrollTop()).toBe maxScrollTop

+  describe "editor.scrollPastEnd", ->
+    describe "when editor.scrollPastEnd is false", ->
+      beforeEach ->
+        atom.config.set("editor.scrollPastEnd", false)
+        displayBuffer.manageScrollPosition = true
+        displayBuffer.setLineHeightInPixels(10)
+
+      it "does not add the height of the view to the scroll height", ->
+        lineHeight = displayBuffer.getLineHeightInPixels()
+        originalScrollHeight = displayBuffer.getScrollHeight()
+        displayBuffer.setHeight(50)
+
+        expect(displayBuffer.getScrollHeight()).toBe originalScrollHeight
+
+    describe "when editor.scrollPastEnd is true", ->
+      beforeEach ->
+        atom.config.set("editor.scrollPastEnd", true)
+        displayBuffer.manageScrollPosition = true
+        displayBuffer.setLineHeightInPixels(10)
+
+      it "adds the height of the view to the scroll height", ->
+        lineHeight = displayBuffer.getLineHeightInPixels()
+        originalScrollHeight = displayBuffer.getScrollHeight()
+        displayBuffer.setHeight(50)
+
+        expect(displayBuffer.getScrollHeight()).toEqual(originalScrollHeight + displayBuffer.height - (lineHeight * 3))
+
   describe "::setScrollLeft", ->
     beforeEach ->
       displayBuffer.manageScrollPosition = true


Fix specs

Fix specs

--- spec/fuzzy-provider-spec.coffee
+++ spec/fuzzy-provider-spec.coffee
@@ -39,44 +39,44 @@
       editor.moveToBeginningOfLine()
       provider = autocompleteManager.providerManager.fuzzyProvider

-      expect(provider.wordList.getToken('somethingNew')).toBeUndefined()
+      expect(provider.tokenList.getToken('somethingNew')).toBeUndefined()
       editor.insertText('somethingNew')
-      expect(provider.wordList.getToken('somethingNew')).toBe 'somethingNew'
+      expect(provider.tokenList.getToken('somethingNew')).toBe 'somethingNew'

     it 'removes words that are no longer in the buffer', ->
       editor.moveToBottom()
       editor.moveToBeginningOfLine()
       provider = autocompleteManager.providerManager.fuzzyProvider

-      expect(provider.wordList.getToken('somethingNew')).toBeUndefined()
+      expect(provider.tokenList.getToken('somethingNew')).toBeUndefined()
       editor.insertText('somethingNew')
-      expect(provider.wordList.getToken('somethingNew')).toBe 'somethingNew'
+      expect(provider.tokenList.getToken('somethingNew')).toBe 'somethingNew'

       editor.backspace()
-      expect(provider.wordList.getToken('somethingNew')).toBe undefined
-      expect(provider.wordList.getToken('somethingNe')).toBe 'somethingNe'
+      expect(provider.tokenList.getToken('somethingNew')).toBe undefined
+      expect(provider.tokenList.getToken('somethingNe')).toBe 'somethingNe'

     # Fixing This Fixes #76
     xit 'adds words to the wordlist with unicode characters', ->
       provider = autocompleteManager.providerManager.fuzzyProvider

-      expect(provider.wordList.indexOf('somēthingNew')).toEqual(-1)
+      expect(provider.tokenList.indexOf('somēthingNew')).toEqual(-1)
       editor.insertText('somēthingNew')
       editor.insertText(' ')
-      expect(provider.wordList.indexOf('somēthingNew')).not.toEqual(-1)
+      expect(provider.tokenList.indexOf('somēthingNew')).not.toEqual(-1)

     # Fixing This Fixes #196
     xit 'removes words from the wordlist when they no longer exist in any open buffers', ->
       # Not sure we should fix this; could have a significant performance impacts
       provider = autocompleteManager.providerManager.fuzzyProvider

-      expect(provider.wordList.indexOf('bogos')).toEqual(-1)
+      expect(provider.tokenList.indexOf('bogos')).toEqual(-1)
       editor.insertText('bogos = 1')
       editor.insertText(' ')
-      expect(provider.wordList.indexOf('bogos')).not.toEqual(-1)
-      expect(provider.wordList.indexOf('bogus')).toEqual(-1)
+      expect(provider.tokenList.indexOf('bogos')).not.toEqual(-1)
+      expect(provider.tokenList.indexOf('bogus')).toEqual(-1)
       editor.backspace() for [1..7]
       editor.insertText('us = 1')
       editor.insertText(' ')
-      expect(provider.wordList.indexOf('bogus')).not.toEqual(-1)
-      expect(provider.wordList.indexOf('bogos')).toEqual(-1)
+      expect(provider.tokenList.indexOf('bogus')).not.toEqual(-1)
+      expect(provider.tokenList.indexOf('bogos')).toEqual(-1)


IDE should use the machine instance provided by `IDEBecameReady` event

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -616,7 +616,7 @@
     @machineStateModal = new EnvironmentsMachineStateModal modalOptions, machineItem

     @machineStateModal.once 'KDObjectWillBeDestroyed', => @machineStateModal = null
-    @machineStateModal.once 'IDEBecameReady',          => @handleIDEBecameReady machineItem
+    @machineStateModal.once 'IDEBecameReady', @bound 'handleIDEBecameReady'

   collapseSidebar: ->
@@ -1459,8 +1459,8 @@
       kd.getSingleton('mainView').activitySidebar.initiateFakeCounter()

       # open README.md for the first time for newly registered users.
-      @machineStateModal.once 'IDEBecameReady', =>
-        machine = @mountedMachine
+      @machineStateModal.once 'IDEBecameReady', (machine) =>
+        machine or= @mountedMachine
         owner   = machine.getOwner()
         path    = "/home/#{owner}/README.md"
         file    = FSHelper.createFileInstance { path, machine }


Remove unused coordinates property from remote.peer

--- src/public/library/models/remote.peer.coffee
+++ src/public/library/models/remote.peer.coffee
@@ -53,7 +53,6 @@
 			@on('channel.opened', @_onChannelOpened)
 			@on('channel.closed', @_onChannelClosed)

-			@coordinates = new Vector(0, 0, 0)
 			@latency = 0

 			if instantiate


Fix copy/paste on mobile Safari

Fix copy/paste on mobile Safari

--- src/trix/controllers/input_controller.coffee
+++ src/trix/controllers/input_controller.coffee
@@ -346,8 +346,7 @@
       @setInputSummary(didDelete: true)

   serializeSelectionToDataTransfer: (dataTransfer) ->
-    return unless dataTransfer?.setData?
-
+    return unless dataTransferIsWritable(dataTransfer)
     document = @responder?.getSelectedDocument().toSerializableDocument()

     dataTransfer.setData("application/x-trix-document", JSON.stringify(document))
@@ -398,3 +397,12 @@
 pasteEventIsCrippledSafariHTMLPaste = (event) ->
   if types = event.clipboardData?.types
     "text/html" not in types and ("com.apple.webarchive" in types or "com.apple.flat-rtfd" in types)
+
+testTransferData = "application/x-trix-feature-detection": "test"
+
+dataTransferIsWritable = (dataTransfer) ->
+  return unless dataTransfer?.setData?
+  for key, value of testTransferData
+    dataTransfer.setData(key, value)
+    return unless dataTransfer.getData(key) is value
+  true


Fix check for showing pp

Fix check for showing pp

Base it on pp value instead of beatmapset status.

--- resources/assets/coffee/react/_components/play-detail.coffee
+++ resources/assets/coffee/react/_components/play-detail.coffee
@@ -68,7 +68,7 @@
                   osu.trans 'users.show.extra.top_ranks.weighted_pp',
                     percentage: "#{Math.round(score.weight.percentage)}%"
                     pp: osu.trans('users.show.extra.top_ranks.pp', amount: Math.round(score.weight.pp)).toLocaleString()
-              if score.beatmapset.status == 'ranked'
+              if score.pp > 0
                 osu.trans('users.show.extra.top_ranks.pp', amount: Math.round(score.pp).toLocaleString())
               else
                 span


Typo on DM check expression corrected

Typo on DM check expression corrected

--- src/timezone-converter.coffee
+++ src/timezone-converter.coffee
@@ -97,7 +97,7 @@
     if messageDate
       roomTimeZones = roomTimeZonesForName res.message.room

-      if roomTimeZones.length > 1 or res.message.room is robot.name
+      if roomTimeZones.length > 1 or res.message.room is res.message.user.name
         memberDates = for roomTimeZone in roomTimeZones
           moment.tz(messageDate, roomTimeZone.tz)
             .format HUBOT_TIMEZONE_CONVERTER_FORMAT.replace /\{(\w+)\}/g, (match, property) ->


make review check for multiparts stricter

--- src/components/task/review.cjsx
+++ src/components/task/review.cjsx
@@ -24,7 +24,7 @@

     stepsList = _.chain steps
       .uniq false, (step) ->
-        if step.content_url?
+        if step.is_in_multipart and step.content_url?
           step.content_url
         else
           step.id


fix tabbing

fix tabbing

--- services/filestore/app/coffee/FSPersistorManager.coffee
+++ services/filestore/app/coffee/FSPersistorManager.coffee
@@ -3,7 +3,7 @@
 LocalFileWriter = require("./LocalFileWriter")

 filterName = (key) ->
-	return key.replace /\//, "_"
+  return key.replace /\//, "_"

 module.exports =
@@ -11,8 +11,8 @@
     filteredTarget = filterName target
     logger.log location:location, target:filteredTarget, source:source, "sending file"
     fs.rename source, "#{location}/#{filteredTarget}", (err) ->
-			if err!=null
-				logger.err err:err, location:location, target:filteredTarget, source:source, "Error on put of file"
+      if err!=null
+        logger.err err:err, location:location, target:filteredTarget, source:source, "Error on put of file"
       callback err

   sendStream: ( location, target, sourceStream, callback = (err)->) ->


LEV-12 first fix try

LEV-12 first fix try

--- modules/levelcreator/app/assets/javascripts/level_creator/plugins/segment_importer.coffee
+++ modules/levelcreator/app/assets/javascripts/level_creator/plugins/segment_importer.coffee
@@ -27,6 +27,9 @@

     { segmentation, dimensions } = input

+    for i in [0...2] by 1
+      @smooth(dimensions, segmentation, 5)
+
     segments = @getSegments(segmentation, dimensions)

     @setAbsoluteCenter(segments)
@@ -53,7 +56,7 @@
         value = segmentation[i]
         i++

-        #continue if value is 0
+        continue if value is 0

         #is segment already there
         segment = _.detect(segments, (s) -> s.value is value)
@@ -240,3 +243,49 @@

     segment.artPath = path
+
+
+  smooth : (dimensions, segmentation, removeThreshold) ->
+
+    width = dimensions[0]
+    height = dimensions[1]
+
+    tempBuffer = new Uint16Array(segmentation.length)
+
+    for h in [0...height] by 1
+      for w in [0...width] by 1
+
+        base = (h * width + w)
+
+        a = segmentation[base]
+        if segmentation[base] is 0
+          continue
+
+        neighbours = 0
+
+        #left
+        neighbours++ if segmentation[base - 1] is a or w - 1 < 0
+        #left up
+        neighbours++ if segmentation[base + width - 1] is a or w - 1 < 0 or h + 1 > height
+        #up
+        neighbours++ if segmentation[base + width ] is a or h + 1 > height
+        #right up
+        neighbours++ if segmentation[base + width + 1] is a or h + 1 > height or w + 1 > width
+        #right
+        neighbours++ if segmentation[base + 1] is a or w + 1 > width
+        #right down
+        neighbours++ if segmentation[base - width + 1] is a or h - 1 < 0 or w + 1 > width
+        #down
+        neighbours++ if segmentation[base - width ] is a or h - 1 < 0
+        #left down
+        neighbours++ if segmentation[base - width - 1] is a or h - 1 < 0 or w - 1 < 0
+
+        if neighbours >= removeThreshold
+          tempBuffer[base] = segmentation[base]
+        else
+          tempBuffer[base] = 0
+
+    for i in [0...segmentation.length]
+      segmentation[i] = tempBuffer[i]
+
+    segmentation


Activity: kodingbot's interactive mode added

--- client/Social/Activity/views/privatemessage/pane.coffee
+++ client/Social/Activity/views/privatemessage/pane.coffee
@@ -1,8 +1,17 @@
 class PrivateMessagePane extends MessagePane

-  TEST_MODE = on
+  TEST_MODE        = on
+  INTERACTIVE_MODE = off
+  lastQuestion     = null
+  isFromBot        = (message, callback) ->
+    {_id} = message.account
+    KD.remote.cacheable 'JAccount', _id, (err, {profile})->
+      {nickname} = profile
+      callback nickname in ['kodingbot', 'kbot']
+

   constructor: (options = {}, data) ->
+
     options.wrapper     ?= yes
     options.lastToFirst  = yes

@@ -29,6 +38,7 @@
     @createParticipantsView()

     @input = new ReplyInputWidget {channel}
+    @kodingBot = new KodingBot delegate : this

     @bindInputEvents()

@@ -78,10 +88,21 @@
         PrivateMessageLoadTest.run this, interval, batchCount
       else if value.match /^\/analyzetheloremipsum/
         PrivateMessageLoadTest.analyze this
+    @applyInteractiveResponse value  if INTERACTIVE_MODE

     @input.reset yes
     @createFakeItemView value, timestamp
     @input.empty()
+
+
+  applyInteractiveResponse: (value) ->
+
+    if lastQuestion
+      message = lastQuestion.getData()
+      @kodingBot.process message, value
+
+
+    @setResponseMode off

   createFakeItemView: (value, timestamp) ->
@@ -121,13 +142,24 @@

     # insert the real message.
     @messageMap[message.id] = yes
-    @prependMessage message, @listController.getItemCount()
+    item = @prependMessage message, @listController.getItemCount()
+
+    isFromBot message, @bound 'setResponseMode'
+
+    return item

   removeMessage: (message) ->

     return  unless @messageMap[message.id]
     super message
+
+  setResponseMode: (mode) ->
+
+    if mode is on
+      lastQuestion = @listController.getListItems().last
+
+    INTERACTIVE_MODE = mode

   loadMessage: (message) -> @appendMessage message, 0


auto focus to recipients input when private message modal shown

--- client/Social/Activity/views/privatemessagemodal.coffee
+++ client/Social/Activity/views/privatemessagemodal.coffee
@@ -50,6 +50,7 @@
     @modalTabs.forms.Message.inputs.recipient.addSubView @chatHeads

     @createUserAutoComplete()
+    @setFocus()

     if @getOption 'arrowTop'
       @addSubView (new KDCustomHTMLView
@@ -58,6 +59,7 @@
           top    : @getOption 'arrowTop'
       ), 'kdmodal-inner'

+  setFocus : -> @autoComplete.getView().$input().focus()

   submitMessage : ->


Remove obsolete code block

Remove obsolete code block

--- client/app/lib/activity/sidebar/activitysideview.coffee
+++ client/app/lib/activity/sidebar/activitysideview.coffee
@@ -102,11 +102,6 @@
       bot =  kd.singletons.socialapi.getPrefetchedData "bot"
       items.unshift bot  if bot

-    # delete this line if `getPrefetchedeData 'bot'` returns an array.
-    # this is justn an assumption that the result will be a single social
-    # channel instance rather than an array. ~Umut
-    items = [items]  unless Array.isArray items
-
     if items?.length
     then @renderItems null, items
     else @reload()


Finder: Remove 'Workspace from here' menu item unless machine is mine.

--- client/ide/finder/idefindercontextmenucontroller.coffee
+++ client/ide/finder/idefindercontextmenucontroller.coffee
@@ -50,4 +50,7 @@
     else
       delete items.Collapse

+    unless fileData.machine.isMine()
+      delete items['Workspace from here']
+
     return items


recaptcha: load script each time to avoid errors

--- client/landing/site.landing/coffee/login/AppView.coffee
+++ client/landing/site.landing/coffee/login/AppView.coffee
@@ -408,19 +408,18 @@
     window.onRecaptchaloadCallback = (event) ->
       grecaptcha?.render 'recaptcha', sitekey : KD.config.recaptcha.key

-    @recaptcha = new KDCustomHTMLView
-      tagName    : 'script'
-      attributes :
-        src      : RECATCHA_JS
-        async    : yes
-        defer    : yes
-
     @signupModal.once 'viewAppended', =>

       if @recaptchaEnabled()
-        unless window.grecaptcha
-        then @recaptcha.appendToDomBody()
-        else grecaptcha?.render 'recaptcha', sitekey : KD.config.recaptcha.key
+        @recaptcha?.destroy()
+        @recaptcha = new KDCustomHTMLView
+          tagName    : 'script'
+          attributes :
+            src      : RECATCHA_JS
+            async    : yes
+            defer    : yes
+
+        @recaptcha.appendToDomBody()

       @signupModal.addSubView new KDCustomHTMLView
         partial : """
By creating an account, you accept Koding's  Terms of Service and Privacy Policy.
"""


Delete unused file

Delete unused file

--- app/assets/javascripts/actions/did_you_know_actions.coffee
+++ app/assets/javascripts/actions/did_you_know_actions.coffee
@@ -1,16 +0,0 @@
-McFly       = require 'mcfly'
-Flux        = new McFly()
-
-WeekActions = Flux.createActions
-  addWeek: ->
-    { actionType: 'ADD_WEEK' }
-  updateWeek: (week) ->
-    { actionType: 'UPDATE_WEEK', data: {
-      week: week
-    }}
-  deleteWeek: (week_id) ->
-    { actionType: 'DELETE_WEEK', data: {
-      week_id: week_id
-    }}
-
-module.exports = WeekActions


#1507 Bug fixes

#1507 Bug fixes

--- app/assets/javascripts/oxalis/model/binary/mappings.coffee
+++ app/assets/javascripts/oxalis/model/binary/mappings.coffee
@@ -9,7 +9,7 @@

     @mappings = _.keyBy(layer.mappings, "name")
     @baseUrl = "/data/datasets/#{datasetName}/layers/#{layer.name}/mappings/"
-    @doWithToken = layer.doWithToken
+    @doWithToken = layer.doWithToken.bind(layer)

   getMappingNames : ->
@@ -36,7 +36,7 @@
     if @mappings[mappingName].mappingObject?
       return Promise.resolve()

-    @doWithToken( (token) ->
+    @doWithToken( (token) =>
       Request.receiveJSON(
         @baseUrl + mappingName + "?token=#{token}"
       ).then(


Fixed role issue.

Fixed role issue.

--- app/assets/javascripts/views/clouds/chat/message.js.coffee
+++ app/assets/javascripts/views/clouds/chat/message.js.coffee
@@ -14,7 +14,7 @@
     @bindEvents()

   render: ->
-    $(@el).html(@template(model: @model))
+    $(@el).html(@template(model: @model)).addClass("role-#{@model.get('user').get('role')}")
     @appendContent(@model)
     this

@@ -23,11 +23,9 @@
       @refreshGfx()

   refreshGfx: ->
-    if @model.get('user')
-      $(@el).addClass("role-#{@model.get('user').get('role')}")
-      @.$('.chat-message-avatar a img').attr('src',@model.get('user').get('avatar').thumb)
-      @.$('.chat-message-head a').text(@model.get('user').get('name'))
-
+    @.$('.chat-message-avatar a img').attr('src',@model.get('user').get('avatar').thumb)
+    @.$('.chat-message-head a').text(@model.get('user').get('name'))
+
   # Appends content to the message
   appendContent: (message) ->


Added some characters to green text.

Added some characters to green text.

--- app/assets/javascripts/views/clouds/chat/message.js.coffee
+++ app/assets/javascripts/views/clouds/chat/message.js.coffee
@@ -34,7 +34,7 @@

     console.log "-------"
     content = content + "\\n"
-    content = content.replace(/((^>|\\n>)[\w\d\s\!\'\"\.\,\-\_\/\?\:\&\=\#\%\+\(\)]*\\n)/ig,"$1")
+    content = content.replace(/((^>|\\n>)[\w\d\s\!\'\"\.\,\-\_\/\?\:\&\=\#\%\+\*\~\\√\◊\(\)]*\\n)/ig,"$1")
     content = content.replace(/\\n/ig,"
")

     @.$('.chat-message-content').append("
#{content}
")


Input: Better fix to allow copy/cut/paste/select-all in textareas. See #54105

--- src/elements/Input/Input.coffee
+++ src/elements/Input/Input.coffee
@@ -272,8 +272,8 @@
 				if ev.keyCode() in [9, 16, 17, 18, 27, 33, 34, 35, 36, 38, 40]
 					return

-				# Return because it selects all.
-				if ev.getKeyboard() == "Control+A"
+				# Select all, copy, paste, cut.
+				if (ev.ctrlKey() or ev.metaKey()) and ev.keyCode() in [65, 67, 86, 88] # 'A', 'C', 'V', 'X'
 					return

 				if not @_textarea and ev.keyCode() == 13


Fix inspect/plot for model metrics

Fix inspect/plot for model metrics

--- src/ext/modules/routines.coffee
+++ src/ext/modules/routines.coffee
@@ -568,10 +568,17 @@

   extendPredictions = (opts, predictions) ->
     render_ predictions, -> H2O.PredictsOutput _, opts, predictions
-    inspect_ predictions,
-      predictions: inspectBinomialPredictions opts, predictions
-      #metrics: inspectBinomialMetrics opts, predictions
-      #scores: inspectBinomialScores opts, predictions
+    if (every predictions, (prediction) -> prediction.model_category is 'Binomial')
+      inspect_ predictions,
+        predictions: inspectBinomialPredictions opts, predictions
+        metrics: inspectBinomialMetrics opts, predictions
+        scores: inspectBinomialScores opts, predictions
+    else
+      inspect_ predictions,
+        predictions: inspectBinomialPredictions opts, predictions
+        #metrics: inspectBinomialMetrics opts, predictions
+        #scores: inspectBinomialScores opts, predictions
+

   inspectBinomialScores = (opts, predictions) -> ->


update sanitizePath regex

update sanitizePath regex

remove accidental inclusion of , and add null char \x00

--- app/coffee/RequestParser.coffee
+++ app/coffee/RequestParser.coffee
@@ -76,4 +76,4 @@

 	_sanitizePath: (path) ->
 		# See http://php.net/manual/en/function.escapeshellcmd.php
-		path.replace(/[\#\&\;\`\|\*\?\~\<\>\^\(\)\[\]\{\}\$\\\,\x0A\xFF]/g, "")+		path.replace(/[\#\&\;\`\|\*\?\~\<\>\^\(\)\[\]\{\}\$\\\x0A\xFF\x00]/g, "")


Add TODOs.

Add TODOs.

--- app/javascripts/clinical.coffee
+++ app/javascripts/clinical.coffee
@@ -20,8 +20,8 @@
         null: 'Not specified'
       }

-    # Test result categorized as positive or negative.
-    TEST_RESULTS =
+    # Lab result categorized as positive or negative.
+    POS_NEG_RESULTS =
       {
         false: 'negative'
         true: 'positive'
@@ -167,9 +167,47 @@
         else
           null

-      # TO DO - add a function to calculate overall FNCLCC grade.
-
+      # TODO - add a function to calculate overall FNCLCC grade.
+
+      # Determine the composite score and stage from the given TNM
+      # and grade. This function creates a staging object consisting
+      # of the following properties:
+      # * t_value - the tumor size (the TNM T parameter)
+      # * g_value - the TNM grade (the TNM G parameter)
+      # * tumor_score - the composite TNM score
+      # * tumor_stage - the I-IV stage defined in TUMOR_STAGES
+      #
+      # @param the TNM object
+      # @param grade the tumor type-specific detail grade, e.g.
+      #   Nottingham for Breast or FNCLCC for Sarcoma
+      # @returns the staging object
       getTumorStaging = (tnm, grade) ->
+        # TODO - Create a separate helper in this Clinical service for
+        # each tumor type, e.g.
+        #   Breast =
+        #     GRADES = ...
+        #     STAGES = ...
+        #     getOverallGrade: (tnm) -> ...
+        #     getStage: (tnm) ->
+        # Remove all type-specific references in this main function.
+        # All type-specific code should be contained in the respecive
+        # service, so calls are by tumor type lookup, e.g.:
+        #   FORMATTERS =
+        #     Breast: Breast
+        #     Sarcoma: Sarcoma
+        #   formatter = FORMATTERS[coll]
+        #   grade = formatter.getOverallGrade(tnm)
+        #   ...
+        # There should be no conditions like the following:
+        #   if coll == 'Breast'
+        #   else if coll == 'Sarcoma'
+        #   ...
+        #
+        # TODO - The REST TNM model size field will become a composite
+        # Size object. After this REST model change takes effect, remove
+        # parsing the size string value and instead get the values
+        # directly from the size object.
+        #
         # Breast or sarcoma collection.
         coll = subject.collection
         # Obtain the T value without any prefixes (i.e. c or p).
@@ -192,6 +230,7 @@
         # If metastasis exits (M1), it is stage IV. Otherwise,
         # breast cancer stage is determined by T and N scores
         # and sarcoma stage is determined by T, N, and G scores.
+        # TODO - delegate to the tumor type-specific service.
         if tnm.metastasis
           tumor_stage = 'IV'
         else if coll == 'Breast'
@@ -203,14 +242,23 @@
         # Return the composite TNM score and the stage.
         t_value: t_value, g_value: g_value, tumor_score: tumor_score, tumor_stage: tumor_stage

-      # Extend the subject encounters and outcomes.
+      # Extend the subject encounters and outcomes as follows:
+      # * Add the t and g value properties to a TNM.
+      # * If the outcome is a TNM or has a TNM, then add the tumor score
+      #   to the outcome.
+      # * Add the isStagingOrGradeData flag to the outcome.
+      # * Add the accordionOpen flag to the encounter
       for enc in subject.encounters
         for outcome in enc.outcomes
           # Add staging properties.
-          tnm = outcome.tnm
-          # Note: CoffeeScript '?' is specifically the equivalent of 'not null'.
-          # This is used to evaluate the data because for some properties,
-          # falsy values such as '0' or 'false' are valid data.
+          #
+          # TODO - generalize for non-pathology and Sarcoma
+          # (or any future tumor type). See the clinical-table.jade
+          # TODOs. The assignment below is a kludgy work-around.
+          tnm = if outcome._cls is 'TNM' then outcome else outcome.tnm
+          # Note: CoffeeScript '?' is specifically the equivalent of not null.
+          # This is used to evaluate the data because for some properties
+          # falsy values such as 0 or false are valid data.
           isStagingData = tnm? and _.some(_.values(tnm), (val) -> val?)
           if isStagingData
             # Add the overall Nottingham grade to the outcome.
@@ -231,6 +279,7 @@
           _.extend outcome, isStagingOrGradeData: isStagingData or isGradeData
         # Add the accordion control flag to the encounter.
         _.extend enc, accordionOpen: true
+
       # The subject encounters.
       encounters: subject.encounters
       # The demographic data.
@@ -305,7 +354,7 @@
             [
               {
                 label: 'Result'
-                accessor: (outcome) -> TEST_RESULTS[outcome.estrogen.positive]
+                accessor: (outcome) -> POS_NEG_RESULTS[outcome.estrogen.positive]
               }
               {
                 label: 'Intensity'
@@ -322,7 +371,7 @@
             [
               {
                 label: 'Result'
-                accessor: (outcome) -> TEST_RESULTS[outcome.progestrogen.positive]
+                accessor: (outcome) -> POS_NEG_RESULTS[outcome.progestrogen.positive]
               }
               {
                 label: 'Intensity'
@@ -343,7 +392,7 @@
               }
               {
                 label: 'HER2/neu FISH'
-                accessor: (outcome) -> TEST_RESULTS[outcome.her2_neu_fish]
+                accessor: (outcome) -> POS_NEG_RESULTS[outcome.her2_neu_fish]
               }
               {
                 label: 'Ki-67'


Fix the problem that do not work Turbolinks only in the theme editor

refs: https://github.com/rails/turbolinks/tree/1-3-stable#opting-out-of-turbolinks

--- backend/app/assets/javascripts/comable/admin/themes.coffee
+++ backend/app/assets/javascripts/comable/admin/themes.coffee
@@ -20,6 +20,14 @@
     $(this).find('[name=code]').val(text)
   )

+add_comable_file_tree_event = ->
+  $comable_file_tree = $('#comable-file-tree')
+  $comable_file_tree.find('a').click((event) ->
+    event.preventDefault()
+    path = $(this).attr('href')
+    Turbolinks.visit(path)
+  )
+
 resize_forms_height = ->
   header_height = parseInt($('.comable-page-body').css('padding-top'))
   footer_height = $('footer').outerHeight(true)
@@ -35,6 +43,7 @@
     add_comable_theme_editor_form_event()
   if can_use_comable_file_tree()
     resize_forms_height()
+    add_comable_file_tree_event()
 )

 $(window).resize(->


Fix zindex

Fix zindex

--- app/scripts/scenery/City.coffee
+++ app/scripts/scenery/City.coffee
@@ -4,7 +4,8 @@
 Game = @Game

 generator.defineElement 'cloud', ->
-  if Math.random() > .2
+  v = Math.random()
+  if v > .2
     y = (Math.random() * 20) + 100
     w = (Math.random() * 20) + 125
     h = (Math.random() * 10) + 50
@@ -13,14 +14,14 @@
       c1 = c1.flip('X')
     @addBackground(200, y, c1, .5)

-  if Math.random() < .5
-    s = (Math.random() * .30) + .15
+  if v < .6
+    s = (Math.random() * .20) + .25

     y = 230 - (s * 50)
     w = ((Math.random() * 10) + 70) - (s * 20)
     h = ((Math.random() * 5) + 20) - (s * 10)
     c2 = Crafty.e('2D, WebGL, cloud, Hideable, Horizon').attr(
-      z: -200,
+      z: -250 + (s * 20),
       w: w,
       h: h,
       topDesaturation: 1.0 - s,


Fix intro positioning

Fix intro positioning

--- app/scripts/scenes/Intro.coffee
+++ app/scripts/scenes/Intro.coffee
@@ -14,7 +14,7 @@
   offset = .15

   Crafty.e('2D, DOM, Text, Tween, Delay')
-    .attr(x: w * (.5 - offset), y: h * .4, w: 450)
+    .attr(x: w * (.5 - offset), y: h * .4, w: 400)
     .text('Speedlazer')
     .textColor('#0000ff')
     .textFont(
@@ -22,7 +22,7 @@
       weight: 'bold'
       family: 'Press Start 2P'
     ).delay ->
-      @tween({ x: (w * (.5 + offset)) - 450  }, 2000)
+      @tween({ x: (w * (.5 + offset)) - 400  }, 2000)
       @one('TweenEnd', ->
         @tween({ x: w * (.5 - offset) }, 2000)
       )


Increase anonymous interval a bit more

Increase anonymous interval a bit more

It's really not needed to be that fast.

--- app/scripts/views/header.coffee
+++ app/scripts/views/header.coffee
@@ -133,7 +133,7 @@
   SetRefreshAnonymousStatusInterval: ->
     if ViewHelpers.isLogged() and not @RefreshAnonymousStatusInterval
       @RefreshAnonymousStatusInterval =
-        setInterval @RefreshAnonymousStatus, 3000
+        setInterval @RefreshAnonymousStatus, 5000

   UnsetRefreshAnonymousStatusInteval: ->
     clearInterval @RefreshAnonymousStatusInterval


Add segnet network params

Add segnet network params

--- app/src/bitcoin/networks.coffee
+++ app/src/bitcoin/networks.coffee
@@ -18,6 +18,13 @@
       P2SH: 196
     bitcoinjs: bitcoin.networks.testnet
     ws_chain: 'testnet3'
+  segnet:
+    name: 'segnet'
+    ticker: 'segtest'
+    bip44_coin_type: '1'
+    version:
+      regular: 30
+      P2SH: 50
   litecoin:
     ticker: 'ltc'
     bip44_coin_type: '2'


refactor according to jQuery plugin authoring rules add support for log method using WebTrends

--- src/ioiojs/playerToolbar.coffee
+++ src/ioiojs/playerToolbar.coffee
@@ -5,85 +5,81 @@
 # http://wordlift.insideout.io/
 (($) ->

-  $.fn.playertools = (player, options) ->
-
-    # create a fake debug object.
-    if false is (debug = window.debug)?
-      debug = {log: ->}
-
-    debug.log "loading playertools."
-
-    settings = $.extend
-      screens:
-        normal:
-          width:640
-          height:480
-        larger:
-          width:960
-          height:720
-      urls: null
-    ,options
-
-    # create an instance with the methods.
-    playertools =
-      isFlash: (element) ->
-        return "OBJECT" is element.get(0).tagName
-      playPause: (player) ->
-        debug.log(player.getWidth() + "/" + player.getHeight())
-        # pause the play if the player is playing.
-        return player.pause(true) if "BUFFERING" is player.getState() or "PLAYING" is player.getState()
-
-        # play if the player is paused/stopped.
-        player.play(true)
-      share: ->
-        debug.log("share")
-
-
-    @each ->
-      element = $(@)
-
-      toolbar = $("

")
-        .width(player.getWidth())
-
-      playButton = $("

")
-        .appendTo(toolbar)
-        .click( -> playertools.playPause(player) )
-
-      timeSlider = $("

")
-        .appendTo(toolbar)
-
-      progressBar = $("

")
-        .appendTo(timeSlider)
-        .click( (event) ->
-          # get the max width.
-          maxWidth = progressBar.width()
-          # get the clicked X.
-          x = event.pageX - progressBar.offset().left
-          debug.log x
-          # get the duration.
-          duration = player.getDuration()
-          # get the clicked position.
-          position = x * (duration / maxWidth)
-          # set the position.
-          player.seek(position)
-        )
-
-      # add a separator.
-      $("

")
-        .appendTo(toolbar)
-
-      volumeButton = $("

")
-        .appendTo(toolbar)
-        .click( ->
-          player.setMute()
-        )
-
-      volumeButton.addClass("mute") if true is player.getMute()
-
-      volumeSlider = $("

")
-        .appendTo(toolbar)
-
-      volumeSlider.find("#volume-slider").slider(
+  # create a fake debug object.
+  if false is (debug = window.debug)?
+    debug = {log: ->}
+
+  methods =
+    init: (options) ->
+
+      settings = $.extend
+          screens:
+            normal:
+              width:640
+              height:480
+            larger:
+              width:960
+              height:720
+          urls: null
+          player: null
+          webtrends: { host: null, path: null, title: null }
+        , options
+
+      player = settings.player
+
+      debug.log( "PlayerTools initializing.", player, settings )
+
+      @each (index, item) ->
+        # ...
+        element = $(item)
+
+        # save the settings.
+        element.data
+          settings: settings
+
+        toolbar = $("

")
+          .width(player.getWidth())
+
+        playButton = $("

")
+          .appendTo(toolbar)
+          .click ->
+            # pause the play if the player is playing.
+            return player.pause(true) if "BUFFERING" is player.getState() or "PLAYING" is player.getState()
+            # play if the player is paused/stopped.
+            player.play(true)
+
+        timeSlider = $("

")
+          .appendTo(toolbar)
+
+        progressBar = $("

")
+          .appendTo(timeSlider)
+          .click (event) ->
+            # get the max width.
+            maxWidth = progressBar.width()
+            # get the clicked X.
+            x = event.pageX - progressBar.offset().left
+            # get the duration.
+            duration = player.getDuration()
+            # get the clicked position.
+            position = x * (duration / maxWidth)
+            # set the position.
+            player.seek(position)
+
+        # add a separator.
+        $("

")
+          .appendTo(toolbar)
+
+        volumeButton = $("

")
+          .appendTo(toolbar)
+          .click ->
+            player.setMute()
+
+        volumeButton.addClass("mute") if true is player.getMute()
+
+        volumeSlider = $("

")
+          .appendTo(toolbar)
+
+        volumeSlider.find("#volume-slider").slider
           min:0
           max:100
           range:"min"
@@ -91,84 +87,75 @@
           slide: (event, ui) ->
             # debug.log(event)
             # debug.log(ui)
-            player.setVolume(ui.value)
-        )
-
-      # add a separator.
-      $("

")
-        .appendTo(toolbar)
-
-      normalScreenButton = $("

")
-        .appendTo(toolbar)
-        .click( ->
-          element.trigger("playertools.willresize", "normal")
-
-          player.resize( settings.screens.normal.width, settings.screens.normal.height)
-          normalScreenButton.addClass("selected")
-          largerScreenButton.removeClass("selected")
-          fullScreenButton.removeClass("selected")
-
-          element.trigger("playertools.resize", "normal")
-        )
-
-      $(document.documentElement).keyup (event) ->
-        normalScreenButton.trigger("click") if 27 is event.keyCode
-
-      largerScreenButton = $("

")
-        .appendTo(toolbar)
-        .click( ->
-          element.trigger("playertools.willresize", "large")
-
-          player.resize( settings.screens.larger.width, settings.screens.larger.height)
-          largerScreenButton.addClass("selected")
-          normalScreenButton.removeClass("selected")
-          fullScreenButton.removeClass("selected")
-
-          element.trigger("playertools.resize", "large")
-        )
-
-      fullScreenButton = $("

")
-        .appendTo(toolbar)
-        .click( ->
-          element.trigger("playertools.willresize", "full")
-
-          width = $(window).width()
-          height = $(window).height() - toolbar.height() - element.position().top
-          player.resize( width, height )
-          # if playertools.isFlash(element)
-          #   ...
-          # else
-          #   player.setFullscreen(true)
-
-          fullScreenButton.addClass("selected")
-          largerScreenButton.removeClass("selected")
-          normalScreenButton.removeClass("selected")
-
-          element.trigger("playertools.resize", "full")
-        )
-
-      qualitySelector = $("

")
-        .appendTo(toolbar)
-
-      qualitySelector
-        .mouseenter( (event) ->
-          width = qualitySelector.width()
-          qualitySelector.find("li").show()
-          add = qualitySelector.width() - width
-          qualitySelector.css("margin-left", -add)
-        )
-        .mouseleave( (event) ->
-          qualitySelector.find("li").not(".selected").hide()
-          qualitySelector.css("margin-left", 0)
-        )
-        .find(".item")
-          .click( (event) ->
+            player.setVolume( ui.value )
+
+        # add a separator.
+        $("

")
+          .appendTo(toolbar)
+
+        normalScreenButton = $("

")
+          .appendTo(toolbar)
+          .click ->
+            element.trigger("playertools.willresize", "normal")
+
+            player.resize( settings.screens.normal.width, settings.screens.normal.height)
+            normalScreenButton.addClass("selected")
+            largerScreenButton.removeClass("selected")
+            fullScreenButton.removeClass("selected")
+
+            element.trigger("playertools.resize", "normal")
+
+        $(document.documentElement).keyup (event) ->
+          normalScreenButton.trigger("click") if 27 is event.keyCode
+
+        largerScreenButton = $("

")
+          .appendTo(toolbar)
+          .click ->
+            element.trigger("playertools.willresize", "large")
+
+            player.resize( settings.screens.larger.width, settings.screens.larger.height)
+            largerScreenButton.addClass("selected")
+            normalScreenButton.removeClass("selected")
+            fullScreenButton.removeClass("selected")
+
+            element.trigger("playertools.resize", "large")
+
+        fullScreenButton = $("

")
+          .appendTo(toolbar)
+          .click ->
+            element.trigger("playertools.willresize", "full")
+
+            width = $(window).width()
+            height = $(window).height() - toolbar.height() - element.position().top
+            player.resize( width, height )
+            fullScreenButton.addClass("selected")
+            largerScreenButton.removeClass("selected")
+            normalScreenButton.removeClass("selected")
+
+            element.trigger("playertools.resize", "full")
+
+        qualitySelector = $("

")
+          .appendTo(toolbar)
+
+        qualitySelector
+          .mouseenter (event) ->
+            width = qualitySelector.width()
+            qualitySelector.find("li").show()
+            add = qualitySelector.width() - width
+            qualitySelector.css("margin-left", -add)
+
+          .mouseleave (event) ->
+            qualitySelector.find("li").not(".selected").hide()
+            qualitySelector.css("margin-left", 0)
+
+          .find(".item")
+          .click (event) ->
             target = $(event.target)

             target
               .addClass("selected")
               .siblings()
-                .removeClass("selected")
+              .removeClass("selected")

             qualitySelector.trigger("mouseleave")

@@ -183,86 +170,110 @@

             # get the current position.
             position = player.getPosition()
-
+
             # play the url.
             player.load
-                file:url
-                start:position
-          )
+              file:url
+              start:position
           .not(".selected")
             .hide()

-      # add a separator.
-      $("

")
-        .appendTo(toolbar)
-
-      shareButton = $("

")
-        .appendTo(toolbar)
-        .click( ->
-          playertools.share()
-        )
-
-      switchToPause =  ->
-        playButton
-          .removeClass("play")
-          .addClass("pause")
-
-      switchToPlay = ->
-        playButton
-          .removeClass("pause")
-          .addClass("play")
-
-      updateProgress = (event) =>
-        return if @updateProgressTimeout?
-        @updateProgressTimeout = setTimeout( =>
-            # get the max position
-            maxWidth = timeSlider.children(".progress-container").width()
-            timeSlider.find(".progress").width( event.position * (maxWidth / event.duration) )
-            @updateProgressTimeout = null
-        ,500)
-
-      updateMute = (event) ->
-        if true is event.mute
-          volumeButton.addClass("mute")
-        else
-          volumeButton.removeClass("mute")
-
-      sizeTimeSlider = ->
-        playerWidth = player.getWidth()
-        toolbar.width(playerWidth)
-
-        sliderWith = playerWidth
-        toolbar.children().not(".slider.time").each (index, item) ->
-          sliderWith -= $(item).outerWidth(true)
-
-        timeSlider
-          .width( sliderWith )
-            # playerWidth \
-            #  - playButton.outerWidth(true) \
-            #  - volumeButton.outerWidth(true) \
-            #  - volumeSlider.outerWidth(true) \
-            #  # - separator.outerWidth(true) \
-            #  - normalScreenButton.outerWidth(true) \
-            #  - largerScreenButton.outerWidth(true) \
-            #  - fullScreenButton.outerWidth(true) \
-            #  - qualitySelector.outerWidth(true) \
-            #  - shareButton.outerWidth(true))
-        margin = parseInt( progressBar.css('margin-left'), 10) + parseInt( progressBar.css('margin-right') )
-        progressBar.width(timeSlider.width() - margin)
-        updateProgress({position:player.getPosition(), duration:player.getDuration()})
-
-      element
-        .after(toolbar)
-
-      sizeTimeSlider()
-
-      player
-        .onPlay( -> switchToPause() )
-        .onPause( -> switchToPlay() )
-        .onBuffer( -> switchToPause() )
-        .onIdle( -> switchToPlay() )
-        .onResize( -> sizeTimeSlider() )
-        .onTime( -> updateProgress(arguments...) )
-        .onMute( -> updateMute(arguments...) )
+        # add a separator.
+        $("

")
+          .appendTo(toolbar)
+
+        shareButton = $("

")
+          .appendTo(toolbar)
+          .click ->
+            playertools.share()
+
+        switchToPause =  ->
+          playButton
+            .removeClass("play")
+            .addClass("pause")
+
+        switchToPlay = ->
+          playButton
+            .removeClass("pause")
+            .addClass("play")
+
+        updateProgress = (event) =>
+          return if @updateProgressTimeout?
+          @updateProgressTimeout = setTimeout =>
+              # get the max position
+              maxWidth = timeSlider.children(".progress-container").width()
+              timeSlider.find(".progress").width( event.position * (maxWidth / event.duration) )
+              @updateProgressTimeout = null
+
+              # save the current position.
+              element.data
+                position: event.position
+
+            , 500
+
+        updateMute = (event) ->
+          if true is event.mute
+            volumeButton.addClass("mute")
+          else
+            volumeButton.removeClass("mute")
+
+        sizeTimeSlider = ->
+          sliderWidth = playerWidth = player.getWidth()
+          toolbar.width(playerWidth)
+
+          toolbar.children().not(".slider.time").each (index, item) ->
+            sliderWidth -= $(item).outerWidth(true)
+
+          timeSlider
+            .width( sliderWidth )
+
+          margin = parseInt( progressBar.css('margin-left'), 10) + parseInt( progressBar.css('margin-right') )
+          progressBar.width(timeSlider.width() - margin)
+          updateProgress
+            position:player.getPosition()
+            duration:player.getDuration()
+
+        element
+          .after(toolbar)
+
+        sizeTimeSlider()
+
+        player
+          .onPlay( -> switchToPause() )
+          .onPause( -> switchToPlay() )
+          .onBuffer( -> switchToPause() )
+          .onIdle( -> switchToPlay() )
+          .onResize( -> sizeTimeSlider() )
+          .onTime( -> updateProgress(arguments...) )
+          .onMute( -> updateMute(arguments...) )
+
+        $(window).unload ->
+          element.playertools( "log" )
+
+    log: ->
+
+      @each (index, item) ->
+        element = $( item )
+        position = element.data( "position" )
+        settings = element.data( "settings" )
+        console.log( "will log [position :: #{position}][WebTrends :: #{Webtrends?}]", settings.webtrends)
+
+        Webtrends?.multiTrack
+          args:
+            "DCS.dcssip": settings.webtrends.host
+            "DCS.dcsuri": settings.webtrends.path
+            "WT.clip_n": settings.webtrends.title
+            "WT.dl": "7"
+            "WT.clip_ev": "#{position}SEC"
+
+    close: ->
+
+      @playertools( "log" )
+
+  $.fn.playertools = ( method ) ->
+    # Method calling logic
+    return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )) if methods[method]?
+    return methods.init.apply( this, arguments ) if "object" is typeof method or not method?
+    $.error( "Method #{method} does not exist on playertools" )

 )(jQuery)


Switched literal 'or' to '||'

Switched literal 'or' to '||'

--- vendor/assets/javascripts/brainstem/brainstem-sync.coffee
+++ vendor/assets/javascripts/brainstem/brainstem-sync.coffee
@@ -39,7 +39,7 @@
     data.include = Brainstem.Utils.extractArray("include", options).join(",")
     data.filters = Brainstem.Utils.extractArray("filters", options).join(",")

-    _.extend(data, options.params or {})
+    _.extend(data, options.params || {})

     params.data = JSON.stringify(data)


fix behavior of links in all feeds mode

fix behavior of links in all feeds mode

--- src/main/coffee/NR.Views.coffee
+++ src/main/coffee/NR.Views.coffee
@@ -84,12 +84,12 @@
             e.preventDefault()

     @event "click", "#markAllReadLink", (e) ->
-        if $("#feedLink").parent().hasClass("disabled")
+        if $("#markAllReadLink").parent().hasClass("disabled")
             e.preventDefault()
         else
             window.app.markAllRead()
     @event "click", "#showOnlyUnreadLink", (e) ->
-        if $("#feedLink").parent().hasClass("disabled")
+        if $("#showOnlyUnreadLink").parent().hasClass("disabled")
             e.preventDefault()
         else
             window.app.toggleShowUnread()


Remove trailing spaces add require JUser

--- workers/social/lib/social/models/emailconfirmation.coffee
+++ workers/social/lib/social/models/emailconfirmation.coffee
@@ -1,14 +1,16 @@
 jraphical = require 'jraphical'

+JUser = require './user'
+
 module.exports = class JEmailConfirmation extends jraphical.Module
-
+
   crypto      = require 'crypto'
   createSalt  = require 'hat'
-
+
   Emailer = require '../emailer'
-
+
   @share()
-
+
   @set
     sharedMethods:
       static    : ['confirmByToken']
@@ -25,7 +27,7 @@
           'invalid status code'
           ['unconfirmed','confirmed']
         ]
-
+
   @confirmByToken = (token, callback)->
     @one {token}, (err, confirmation)->
       if err or !confirmation?
@@ -40,7 +42,7 @@
                 callback err
               else
                 user.confirmEmail callback
-
+
   @create =(user, callback)->
     email = user.getAt('email')
     salt = createSalt()
@@ -61,30 +63,30 @@
             callback err
           else
             callback null, confirmation
-
+
   getSubject:-> '[Koding] Please confirm your email address.'
-
+
   getTextBody:->
     {host, protocol} = require('../config.email')
     url = "#{protocol}//#{host}/verify/#{encodeURIComponent @getAt('token')}"
-
+
     #
     # chris: you can do this at some point, i did setup kd.io/ domain.
     #
     # bitly.shorten url,(err,res)->
     #   unless err
     #     url = res.data.url
-
+
     """
     Hi #{@getAt('username')},
-
+
     Please confirm your email address in order to fully-activate your new Koding account.
-
+
     #{url}
     """
-
+
   confirm:(callback)-> @update {$set: status: 'confirmed'}, callback
-
+
   send:(callback)->
     Emailer.send
       To        : @getAt('email')


JReferralCampaign: use permissions instead manual permission checker

--- workers/social/lib/social/models/referral/campaign.coffee
+++ workers/social/lib/social/models/referral/campaign.coffee
@@ -1,16 +1,28 @@
 {Model}   = require 'bongo'
 jraphical = require 'jraphical'
+
 module.exports = class JReferralCampaign extends jraphical.Module

   {signature, secure} = require 'bongo'
+
+  @trait __dirname, '../../traits/protected'
+
+  {permit} = require '../group/permissionset'
+
   @share()

   @set
+
+    permissions              :
+      'manage campaign'      : []
+
     sharedEvents             :
       static                 : []
       instance               : []
+
     indexes                  :
       name                   : 'unique'
+
     schema                   :
       name                   : String
       slug                   : String
@@ -58,33 +70,27 @@
           (signature Object, Function)
         ]

-  @create = secure (client, data, callback) ->
-    checkPermission client, (err, res)=>
-      return callback err if err
+  @create = permit 'manage campaign',
+
+    success: (client, data, callback) ->
+
       campaign = new JReferralCampaign data
       campaign.save (err)->
-        return callback err if err
-        return callback null, campaign
+        return callback err  if err
+        callback null, campaign

-  checkPermission: checkPermission = (client, callback)->
-    {context:{group}} = client
-    JGroup = require "../group"
-    JGroup.one {slug:group}, (err, group)=>
-      return callback err if err
-      return callback new Error "group not found" unless group
-      group.canEditGroup client, (err, hasPermission)=>
-        return callback err if err
-        return callback new Error "Can not edit group" unless hasPermission
-        return callback null, yes

-  update$: secure (client, data, callback)->
-    @checkPermission client, (err, res)=>
-      return callback err if err
-      @update {$set:data}, callback
+  update$: permit 'manage campaign',

-  remove$: secure (client, callback)->
-    @checkPermission client, (err, res)=>
-      return callback err if err
+    success: (client, data, callback)->
+
+      @update $set: data, callback
+
+
+  remove$: permit 'manage campaign',
+
+    success: (client, data, callback)->
+
       @remove callback

   REGISTER_CAMPAIGN = "register"


Social: use class level doRequest function

--- workers/social/lib/social/models/socialapi/message.coffee
+++ workers/social/lib/social/models/socialapi/message.coffee
@@ -60,7 +60,7 @@
     success: (client, data, callback)->
       SocialMessage.ensureGroupChannel client, (err, socialApiChannelId)->
         data.channelId = socialApiChannelId
-        SocialMessage.doRequest 'postToChannel', client, data, callback
+        doRequest 'postToChannel', client, data, callback

   @reply = permit 'create posts',
     success: (client, data, callback)->


Social: implement pinning messages

Social: implement pinning messages

--- workers/social/lib/social/models/socialapi/message.coffee
+++ workers/social/lib/social/models/socialapi/message.coffee
@@ -25,6 +25,8 @@
         unlike :
           (signature Object, Function)
         listPinnedMessages:
+          (signature Object, Function)
+        pinMessage   :
           (signature Object, Function)

     schema          :
@@ -121,6 +123,19 @@
         data.groupName = context.group
         {listPinnedMessages} = require './requests'
         listPinnedMessages data, callback
+
+  @pinMessage = permit 'pin posts',
+    success:  (client, data, callback)->
+      {connection:{delegate}, context} = client
+      delegate.createSocialApiId (err, socialApiId)=>
+        return callback err if err
+        unless data.messageId
+          return callback { message: "Message id is not set for pinning requests "}
+
+        data.groupName = context.group
+        data.accountId = socialApiId
+        {pinMessage} = require './requests'
+        pinMessage data, callback
   @ensureGroupChannel = (client, callback)->
     fetchGroup client, (err, group)->
       return callback err  if err


Fixed reply action - show/hide to/cc field.

--- app/assets/javascripts/app/controllers/agent_ticket_zoom.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_zoom.js.coffee
@@ -367,6 +367,7 @@
     # preselect article type
     @el.find('[name="ticket_article_type_id"]').find('option:selected').removeAttr('selected')
     @el.find('[name="ticket_article_type_id"]').find('[value="' + article_type.id + '"]').attr('selected',true)
+    @el.find('[name="ticket_article_type_id"]').trigger('change')

     # empty form
     @el.find('[name="to"]').val('')


Update lastDigits when card number changes

--- app/assets/javascripts/sprangular/directives/creditCardForm.coffee
+++ app/assets/javascripts/sprangular/directives/creditCardForm.coffee
@@ -22,5 +22,6 @@
     currentYear = (new Date).getFullYear()
     $scope.years = [currentYear .. currentYear+15]

-    $scope.$watch 'creditCard.number', ->
+    $scope.$watch 'creditCard.number', (number) ->
+      $scope.creditCard.lastDigits = number.substr(-4)
       $scope.creditCard.determineType()


Better zoom level on shops map

Better zoom level on shops map

--- app/assets/javascripts/views/organizations/content/where.js.coffee
+++ app/assets/javascripts/views/organizations/content/where.js.coffee
@@ -14,22 +14,11 @@
     createMap(40.111689,-96.81839,4);

   updateMapLocation: (event) ->
-    # event.preventDefault()
+    event.preventDefault()
     target = $(event.target)
     $('body').animate(
       scrollTop: ($('#where-bike-index').offset().top - 20), 'fast'
     )
     latLng = new google.maps.LatLng(target.attr('data-lat'), target.attr('data-long'))
-    window.map.setZoom(8)
-    window.map.panTo(latLng)
-
-
-  # initialize
-  #   if(navigator.geolocation) {
-  #     success = function(position) {
-  #       createMap(position.coords.latitude, position.coords.longitude,12);
-  #     };
-  #     error = function() {createMap(41.869561,-87.495117,6); }
-  #     navigator.geolocation.getCurrentPosition(success, error);
-  #   }
-  #   else {+    window.map.setZoom(13)
+    window.map.panTo(latLng)


reactivity: turn off suggestion highlighting if Algolia doesnt return highlightResult

--- client/activity/lib/components/suggestionitem/index.coffee
+++ client/activity/lib/components/suggestionitem/index.coffee
@@ -23,6 +23,7 @@
     { suggestion }  = @props
     message         = suggestion.get 'message'
     highlightResult = suggestion.get 'highlightResult'
+    messageBody     = highlightResult?.getIn(['body', 'value']) ? message.get('body')

     { makeAvatar, makeProfileLink, makeInfoText } = helper

@@ -31,7 +32,7 @@
         {makeAvatar message.get('account')}

-        <SuggestionMessageBody source={highlightResult.getIn(['body', 'value'])} />
+        <SuggestionMessageBody source={messageBody} />


Client: Activity: Mover base modal functionalities to base ActivityModal.

--- client/app/lib/components/activitypromptmodal/index.coffee
+++ client/app/lib/components/activitypromptmodal/index.coffee
@@ -13,26 +13,13 @@

 module.exports = class ActivityPromptModal extends React.Component

-  @defaultProps =
-    onClose             : noop
-    closeOnEsc          : yes
-    closeOnOutsideClick : yes
-    hasOverlay          : yes
-    isOpen              : no
-    className           : ''
-
-  onClose: (args...) -> @props.onClose? args...
-
+module.exports = class ActivityPromptModal extends ActivityModal

   renderModal: ->

     return null  unless @props.isOpen

-    portalProps =
-      isOpened            : @props.isOpen
-      onClose             : @bound 'onClose'
-      closeOnEsc          : @props.closeOnEsc
-      closeOnOutsideClick : @props.closeOnOutsideClick
+    portalProps = @getPortalProps()

     return (
       <Portal {...portalProps}>
@@ -53,22 +40,3 @@
       </Portal>
     )

-
-  renderOverlay: ->
-    return null  unless @props.isOpen and @props.hasOverlay
-
-    return (
-      <Portal isOpened={@props.isOpen}>
-        <ModalOverlay />
-      </Portal>
-    )
-
-
-
-  render: ->
-    <div className="ActivityPromptModal-container">
-      {@renderOverlay()}
-      {@renderModal()}
-    </div>
-
-


First define function before calling it

First define function before calling it

--- core/app/assets/javascripts/base/placeholder_fix_ie.coffee
+++ core/app/assets/javascripts/base/placeholder_fix_ie.coffee
@@ -1,6 +1,4 @@
 # This only affects the inputs/textareas that aren't loaded through Backbone Views because they don't exist here
-$ ->
-  resetPlaceholders()

 window.resetPlaceholders = ->
   $('.placeholder')
@@ -16,3 +14,6 @@

   $('input, textarea')
     .placeholder()
+
+$ ->
+  resetPlaceholders()


fix mode switch and allowedModes, do not push undefined

--- app/assets/javascripts/oxalis/controller.coffee
+++ app/assets/javascripts/oxalis/controller.coffee
@@ -75,12 +75,12 @@
     for allowedMode in @model.settings.allowedModes

       if @model.getColorBinaries()[0].cube.BIT_DEPTH == 8
-        @allowedModes.push switch allowedMode
-          when "flight" then constants.MODE_ARBITRARY
-          when "oblique" then constants.MODE_ARBITRARY_PLANE
-
-      @allowedModes.push switch allowedMode
-        when "volume" then constants.MODE_VOLUME
+        switch allowedMode
+          when "flight" then @allowedModes.push(constants.MODE_ARBITRARY)
+          when "oblique" then @allowedModes.push(constants.MODE_ARBITRARY_PLANE)
+
+      switch allowedMode
+        when "volume" then @allowedModes.push(constants.MODE_VOLUME)

     if not @model.volumeTracing?
       # Plane tracing mode is always allowed (except in VOLUME mode)
@@ -189,7 +189,7 @@

         "m" : => # rotate allowed modes

-          index = (@allowedModes.indexOf(@model.mode) + 1) % @allowedModes.length
+          index = (@allowedModes.indexOf(@model.get("mode")) + 1) % @allowedModes.length
           @model.setMode(@allowedModes[index])

         "super + s, ctrl + s" : (event) =>


Full account reload when visiting /account

--- app/assets/javascripts/sprangular/routes.coffee
+++ app/assets/javascripts/sprangular/routes.coffee
@@ -14,7 +14,7 @@
       templateUrl: 'account/show.html'
       resolve:
         user: (Account) ->
-          Account.reload().then -> Account.user
+          Account.reload('full').then -> Account.user

     .when '/products',
       controller: 'ProductListCtrl'


include analytics js that was not merged

--- app/assets/javascripts/teacher_tracks.js.coffee
+++ app/assets/javascripts/teacher_tracks.js.coffee
@@ -27,4 +27,13 @@
         ui.placeholder.height(ui.item.height())
         ui.placeholder.width(ui.item.width())
       update: ->
-        $.post($(this).data('url'), $(this).sortable('serialize'))+        $.post($(this).data('url'), $(this).sortable('serialize'))
+
+  # Phase select
+  $('#phase-selector select').on 'change', ->
+    $(this).closest('form').submit()
+
+  # Show students that haven't voted
+  $('.students-toggle').on 'click', (e) ->
+    e.preventDefault()
+    $(this).closest('.row').find('.hasnt-voted').toggleClass('hasnt-voted-show')


remove _configure method, provided by BackBone 1.1.0

--- app/assets/javascripts/ultimate/flash.js.coffee
+++ app/assets/javascripts/ultimate/flash.js.coffee
@@ -70,6 +70,7 @@
     _events

   initialize: (options) ->
+    @_initTranslations options
     # init flashes come from server in page
     @jFlashes().each (index, flash) =>
       jFlash = $(flash)
@@ -88,9 +89,6 @@
         @ajaxSuccess a.data, a.jqXHR

-  _configure: (options) ->
-    super
-    @_initTranslations()

   locale: 'en'
   translations: null
@@ -106,9 +104,9 @@
       formFieldsError: 'Форма заполнена с ошибками'

   # use I18n, and modify locale and translations
-  _initTranslations: ->
+  _initTranslations: (options) ->
     @translations ||= {}
-    if not @options['locale'] and I18n?.locale of @constructor.defaultLocales
+    if not options['locale'] and I18n?.locale of @constructor.defaultLocales
       @locale = I18n.locale
     _.defaults @translations, @constructor.defaultLocales[@locale]


Change dependancy to use public/library/* instead of public/

--- src/tests/spec/public/models/remote.peer.spec.coffee
+++ src/tests/spec/public/models/remote.peer.spec.coffee
@@ -3,7 +3,7 @@
 		'public': '../../public'

 require [
-	'public/models/remote.peer'
+	'public/library/models/remote.peer'
 	], ( Peer ) ->

 	describe 'Remote.Peer', ->


FIX: Use target grade name, not index, for target grade analytics

--- src/app/projects/partials/directives/project-contexts.coffee
+++ src/app/projects/partials/directives/project-contexts.coffee
@@ -17,7 +17,7 @@
         $scope.project.target_grade = project.target_grade
         $scope.project.burndown_chart_data = project.burndown_chart_data
         projectService.updateTaskStats $scope.project, project.stats
-        analyticsService.event "Student Feedback View - Progress Tab", "Grade Changed", idx
+        analyticsService.event "Student Feedback View - Progress Tab", "Grade Changed", $scope.grades[idx]
         $rootScope.$broadcast "TargetGradeUpdated"

     $scope.taskCount = () ->


member create contentdisplay fix

member create contentdisplay fix

--- client/app/Applications/Members.kdapplication/AppController.coffee
+++ client/app/Applications/Members.kdapplication/AppController.coffee
@@ -189,13 +189,11 @@
   #   contentDisplay = controller.getView()
   #   contentDisplayController.emit "ContentDisplayWantsToBeShown", contentDisplay

-  createContentDisplay:(account, doShow = yes)->
-    controller = new ContentDisplayControllerMember null, account
+  createContentDisplay:(account, callback)->
+    controller     = new ContentDisplayControllerMember null, account
     contentDisplay = controller.getView()
-    if doShow
-      @showContentDisplay contentDisplay
-
-    return contentDisplay
+    @showContentDisplay contentDisplay
+    @utils.defer -> callback contentDisplay

   showContentDisplay:(contentDisplay)->
     contentDisplayController = @getSingleton "contentDisplayController"


fix addParticipant error

fix addParticipant error

--- client/views/common/addParticipantModal/addParticipantModal.coffee
+++ client/views/common/addParticipantModal/addParticipantModal.coffee
@@ -41,7 +41,7 @@
 								for time in numbers when time not in user.profile.available[day]
 									available = false

-							if available
+							if available && user.profile.vacations?
 								for vacation in user.profile.vacations
 									now = parseInt shift.date
 									start = parseInt vacation.start


accounts for items that do not have course them or course type selected

--- src/core/javascripts/controllers/event_list.js.coffee
+++ src/core/javascripts/controllers/event_list.js.coffee
@@ -356,7 +356,7 @@
             for i in item.chain.extra[name]
               filter = ($scope.dynamic_filters.values[dynamic_filter.name] and i is $scope.dynamic_filters.values[dynamic_filter.name].name) or !$scope.dynamic_filters.values[dynamic_filter.name]?
               break if filter
-          else if (item.chain.extra[name] is undefined && !item.chain)
+          else if (item.chain.extra[name] is undefined && !item.chain?)
             filter = true;
           result = result and filter
       else


Lightbox update

Lightbox update

--- vendor/assets/javascripts/lightbox.js.coffee
+++ vendor/assets/javascripts/lightbox.js.coffee
@@ -8,7 +8,7 @@
 Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
 - free for use in both personal and commercial projects
 - attribution requires leaving author name, author link, and the license info intact
-
+
 Thanks
 - Scott Upton(uptonic.com), Peter-Paul Koch(quirksmode.com), and Thomas Fuchs(mir.aculo.us) for ideas, libs, and snippets.
 - Artemy Tregubenko (arty.name) for cleanup and help in updating to latest proto-aculous in v2.05.
@@ -45,12 +45,12 @@

 class LightboxOptions
   constructor: ->
-    @fileLoadingImage = '/assets/lightbox/loading.gif'
-    @fileCloseImage = '/assets/lightbox/close.png'
+    @fileLoadingImage = 'images/loading.gif'
+    @fileCloseImage = 'images/close.png'
     @resizeDuration = 700
     @fadeDuration = 500
     @labelImage = "Image" # Change to localize to non-english language
-    @labelOf = "of"
+    @labelOf = "of"

 class Lightbox
@@ -58,8 +58,8 @@
     @album = []
     @currentImageIndex = undefined
     @init()
-
-
+
+
   init: ->
     @enable()
     @build()
@@ -76,37 +76,8 @@
   # Build html for the lightbox and the overlay.
   # Attach event handlers to the new DOM elements. click click click
   build: ->
-    $("
", id: 'lightboxOverlay' ).after(
-      $('
', id: 'lightbox').append(
-        $('
', class: 'lb-outerContainer').append(
-          $('
', class: 'lb-container').append(
-            $('', class: 'lb-image'),
-          $('
',class: 'lb-nav').append(
-            $('', class: 'lb-prev'),
-          $('', class: 'lb-next')
-          ),
-          $('
', class: 'lb-loader').append(
-            $('', class: 'lb-cancel').append(
-              $('', src: @options.fileLoadingImage)
-            )
-          )
-          )
-        ),
-      $('
', class: 'lb-dataContainer').append(
-        $('
', class: 'lb-data').append(
-          $('
', class: 'lb-details').append(
-            $('', class: 'lb-caption'),
-          $('', class: 'lb-number')
-          ),
-        $('
', class: 'lb-closeContainer').append(
-          $('', class: 'lb-close').append(
-            $('', src: @options.fileCloseImage)
-          )
-        )
-        )
-      )
-      )
-    ).appendTo $('body')
+    $("

").appendTo($('body'));
+

     # Attach event handlers to the newly minted DOM elements
     $('#lightboxOverlay')
@@ -116,21 +87,21 @@
         return false

     $lightbox = $('#lightbox')
-
+
     $lightbox
       .hide()
       .on 'click', (e) =>
         if $(e.target).attr('id') == 'lightbox' then @end()
         return false
-
+
     $lightbox.find('.lb-outerContainer').on 'click', (e) =>
       if $(e.target).attr('id') == 'lightbox' then @end()
       return false
-
+
     $lightbox.find('.lb-prev').on 'click', (e) =>
       @changeImage @currentImageIndex - 1
       return false
-
+
     $lightbox.find('.lb-next').on 'click', (e) =>
       @changeImage @currentImageIndex + 1
       return false
@@ -164,7 +135,7 @@
         if $(a).attr('href') == $link.attr('href')
           imageNumber = i

-    # Position lightbox
+    # Position lightbox
     $window = $(window)
     top = $window.scrollTop() + $window.height()/10
     left = $window.scrollLeft()
@@ -173,32 +144,32 @@
       .css
         top: top + 'px'
         left: left + 'px'
-    .fadeIn( @options.fadeDuration)
-
+      .fadeIn( @options.fadeDuration)
+
     @changeImage(imageNumber)
     return
-
+

   # Hide most UI elements in preparation for the animated resizing of the lightbox.
   changeImage: (imageNumber) ->
-
-    @disableKeyboardNav()
+
+    @disableKeyboardNav()
     $lightbox = $('#lightbox')
     $image = $lightbox.find('.lb-image')

     @sizeOverlay()
     $('#lightboxOverlay').fadeIn( @options.fadeDuration )
-
+
     $('.loader').fadeIn 'slow'
     $lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide()

     $lightbox.find('.lb-outerContainer').addClass 'animating'
-
+
     # When image to show is preloaded, we send the width and height to sizeContainer()
     preloader = new Image
     preloader.onload = () =>
       $image.attr 'src', @album[imageNumber].link
-
+
       # set image size limit
       innerWidth  = window.innerWidth  || document.documentElement.clientWidth
       innerHeight = window.innerHeight || document.documentElement.clientHeight
@@ -223,7 +194,7 @@

     preloader.src = @album[imageNumber].link
     @currentImageIndex = imageNumber
-    return
+    return

   # Stretch overlay to fit the document
@@ -231,8 +202,8 @@
     $('#lightboxOverlay')
       .width( $(document).width())
       .height( $(document).height() )
-
-
+
+
   # Animate the size of the lightbox to fit the image we are showing
   sizeContainer: (imageWidth, imageHeight) ->
     $lightbox = $('#lightbox')
@@ -245,11 +216,11 @@
     containerTopPadding = parseInt $container.css('padding-top'), 10
     containerRightPadding = parseInt $container.css('padding-right'), 10
     containerBottomPadding = parseInt $container.css('padding-bottom'), 10
-    containerLeftPadding = parseInt $container.css('padding-left'), 10
+    containerLeftPadding = parseInt $container.css('padding-left'), 10

     newWidth = imageWidth + containerLeftPadding + containerRightPadding
     newHeight = imageHeight + containerTopPadding + containerBottomPadding
-
+
     # Animate just the width, just the height, or both, depending on what is different
     if newWidth != oldWidth && newHeight != oldHeight
       $outerContainer.animate
@@ -272,11 +243,11 @@
       $lightbox.find('.lb-nextLink').height(newHeight)
       @showImage()
       return
-    , @options.resizeDuration
-
-    return
-
-
+    , @options.resizeDuration
+
+    return
+
+
   # Display the image and it's details and begin preload neighboring images.
   showImage: ->
     $lightbox = $('#lightbox')
@@ -298,11 +269,11 @@
     if @currentImageIndex > 0 then $lightbox.find('.lb-prev').show();
     if @currentImageIndex < @album.length - 1 then $lightbox.find('.lb-next').show();
     return
-
-  # Display caption, image number, and closing button.
+
+  # Display caption, image number, and closing button.
   updateDetails: ->
     $lightbox = $('#lightbox')
-
+
     if typeof @album[@currentImageIndex].title != 'undefined' && @album[@currentImageIndex].title != ""
       $lightbox.find('.lb-caption')
         .html( @album[@currentImageIndex].title)
@@ -312,26 +283,26 @@
       $lightbox.find('.lb-number')
         .html( @options.labelImage + ' ' + (@currentImageIndex + 1) + ' ' + @options.labelOf + '  ' + @album.length)
         .fadeIn('fast')
-    else
+    else
       $lightbox.find('.lb-number').hide()

     $lightbox.find('.lb-outerContainer').removeClass 'animating'
-
+
     $lightbox.find('.lb-dataContainer')
       .fadeIn @resizeDuration, () =>
         @sizeOverlay()
     return
-
-
-  # Preload previos and next images in set.
+
+
+  # Preload previos and next images in set.
   preloadNeighboringImages: ->
-    if @album.length > @currentImageIndex + 1
+   if @album.length > @currentImageIndex + 1
       preloadNext = new Image
       preloadNext.src = @album[@currentImageIndex + 1].link

     if @currentImageIndex > 0
       preloadPrev = new Image
-      preloadPrev.src = @album[@currentImageIndex - 1].link
+      preloadPrev.src = @album[@currentImageIndex - 1].link
     return

@@ -339,12 +310,12 @@
     $(document).on 'keyup.keyboard', $.proxy( @keyboardAction, this)
     return

-
+
   disableKeyboardNav: ->
     $(document).off '.keyboard'
     return
-
-
+
+
   keyboardAction: (event) ->
     KEYCODE_ESC = 27
     KEYCODE_LEFTARROW = 37
@@ -357,13 +328,13 @@
       @end()
     else if key == 'p' || keycode == KEYCODE_LEFTARROW
       if @currentImageIndex != 0
-        @changeImage @currentImageIndex - 1
+          @changeImage @currentImageIndex - 1
     else if key == 'n' || keycode == KEYCODE_RIGHTARROW
       if @currentImageIndex != @album.length - 1
-        @changeImage @currentImageIndex + 1
-    return
-
-
+          @changeImage @currentImageIndex + 1
+    return
+
+
   # Closing time. :-(
   end: ->
     @disableKeyboardNav()
@@ -371,8 +342,8 @@
     $('#lightbox').fadeOut @options.fadeDuration
     $('#lightboxOverlay').fadeOut @options.fadeDuration
     $('select, object, embed').css visibility: "visible"
-
-
+
+
 $ ->
   options = new LightboxOptions
-  lightbox = new Lightbox options+  lightbox = new Lightbox options


Make cancel and save buttons work when creating new keys.

--- client/landing/app/Applications/Account.kdapplication/account/accSettingsDevSshKeys.coffee
+++ client/landing/app/Applications/Account.kdapplication/account/accSettingsDevSshKeys.coffee
@@ -109,12 +109,19 @@

     @addSubView swappable,".swappable-wrapper"

-    form.on "FormCancelled", @bound "swapSwappable"
+    form.on "FormCancelled", @bound "cancelItem"
     form.on "FormSaved", @bound "saveItem"
     form.on "FormDeleted", @bound "deleteItem"

   swapSwappable:->
     @swappable.swapViews()
+
+  cancelItem:->
+    @data = @form.keyTextarea.getValue()
+    if @data
+      @swappable.swapViews()
+    else
+      @deleteItem()

   deleteItem:->
     {controller} = @getDelegate()
@@ -124,9 +131,13 @@
   saveItem:->
     {controller} = @getDelegate()
     @data = @form.keyTextarea.getValue()
-    @info.$('div.darkText').text @data
-    @swappable.swapViews()
-    controller.emit "UpdatedItems"
+    if @data
+      @info.$('div.darkText').text @data
+      @swappable.swapViews()
+      controller.emit "UpdatedItems"
+    else
+      new KDNotificationView
+        title : "Key shouldn't be empty."

   partial:(data)->
     """


Auction clock label copy change

Auction clock label copy change

--- apps/artwork_2/components/banner/helpers.coffee
+++ apps/artwork_2/components/banner/helpers.coffee
@@ -4,9 +4,9 @@
 module.exports =
   countdownLabel: (startAt) ->
     if moment(startAt).isAfter()
-      'Auction closes in'
+      'Bidding closes in'
     else
-      'Auction opens in'
+      'Bidding opens in'

   countdownClock: (startAt, endAt) ->
     if moment(startAt).isAfter()