Scroll window when rendering new posts from pool: last new post is visible

--- assets/javascripts/views/fetch_posts_pool.js.coffee
+++ assets/javascripts/views/fetch_posts_pool.js.coffee
@@ -39,6 +39,9 @@
   emptyPool: =>
     posts_feed_view = Marbles.View.instances.all[@posts_feed_view_cid]
     return unless posts_feed_view
+
+    last_post_cid = _.last(@posts_collection.model_ids)
+
     posts_feed_view.prependRender(@posts_collection.models())
     posts_feed_view.posts_collection.prependModels(@posts_collection.model_ids)
     @posts_collection.empty()
@@ -48,6 +51,10 @@
       posts_feed_view.updateProfileCursor(@posts_collection)

     @render()
+
+    if last_post_el = Marbles.DOM.querySelector("[data-post_cid='#{last_post_cid}']", posts_feed_view.el)
+      postition = last_post_el.offsetTop - 20 # 20 is an arbitrary padding amount
+      window.scrollTo(window.scrollX, postition)

   context: =>
     posts_count: @posts_collection.model_ids.length


do not reuse loop index var

do not reuse loop index var

--- bokehjs/src/coffee/renderer/glyph/multi_line.coffee
+++ bokehjs/src/coffee/renderer/glyph/multi_line.coffee
@@ -21,17 +21,17 @@
         [sx, sy] = @plot_view.map_to_screen(x, @glyph_props.xs.units, y, @glyph_props.ys.units)

         glyph_props.line_properties.set_vectorize(ctx, i)
-        for i in [0..sx.length-1]
-          if i == 0
+        for j in [0..sx.length-1]
+          if j == 0
             ctx.beginPath()
-            ctx.moveTo(sx[i], sy[i])
+            ctx.moveTo(sx[j], sy[j])
             continue
-          else if isNaN(sx[i]) or isNaN(sy[i])
+          else if isNaN(sx[j]) or isNaN(sy[j])
             ctx.stroke()
             ctx.beginPath()
             continue
           else
-            ctx.lineTo(sx[i], sy[i])
+            ctx.lineTo(sx[j], sy[j])
         ctx.stroke()

     draw_legend: (ctx, x1, x2, y1, y2) ->


Interpret MSSQL -1 char length as 'max'

Interpret MSSQL -1 char length as 'max'

--- src/dialects/tsql/tsql-utils.coffee
+++ src/dialects/tsql/tsql-utils.coffee
@@ -59,6 +59,8 @@
             callback(err, null) if err
             for r in rows
                 r.isNullable = r.isNullable == 'YES'
+                if r.maxLength == -1
+                    r.maxLength = 'max'
             callback(null, rows)
         )


Fix auth page template specs

Fix auth page template specs

--- mobile/apps/auth/test/templates.coffee
+++ mobile/apps/auth/test/templates.coffee
@@ -43,7 +43,7 @@
     html = render('call_to_action')(
       action: 'artwork-save'
       redirectTo: 'artsy.net/artwork/some-artwork'
-      sd: { AP: {}, CURRENT_PATH: '/sign_up' }
+      sd: { AP: { signupPagePath: '/sign_up' }, CURRENT_PATH: '/sign_up' }
       asset: (->)
     )

@@ -69,7 +69,7 @@
     html = render('call_to_action')(
       action: 'artwork-save'
       redirectTo: 'artsy.net/artwork/some-artwork'
-      sd: { AP: {}, CURRENT_PATH: '/log_in' }
+      sd: { AP: { loginPagePath: '/log_in' }, CURRENT_PATH: '/log_in' }
       asset: (->)
     )


allow to locally null adapter

allow to locally null adapter

--- src/observer.coffee
+++ src/observer.coffee
@@ -12,7 +12,7 @@
   # Parses the keypath using the interfaces defined on the view. Sets variables
   # for the tokenized keypath, as well as the end key.
   parse: =>
-    interfaces = (k for k, v of @view.adapters)
+    interfaces = (k for k, v of @view.adapters when v)

     if @keypath[0] in interfaces
       root = @keypath[0]


Escape \

Escape \

--- services/web/public/coffee/ide/human-readable-logs/HumanReadableLogsRules.coffee
+++ services/web/public/coffee/ide/human-readable-logs/HumanReadableLogsRules.coffee
@@ -50,7 +50,7 @@
 		regexToMatch: /No positions in optional float specifier/
 		extraInfoURL: "https://www.sharelatex.com/learn/Errors/No_positions_in_optional_float_specifier"
 		humanReadableHint: """
-			You have forgotten to include a float specifier, which tells LaTeX where to position your figure. To fix this, either insert a float specifier inside the square brackets (e.g. \begin{figure}[h]), or remove the square brackets (e.g. \begin{figure}). Find out more about float specifiers here.
+			You have forgotten to include a float specifier, which tells LaTeX where to position your figure. To fix this, either insert a float specifier inside the square brackets (e.g. \\begin{figure}[h]), or remove the square brackets (e.g. \\begin{figure}). Find out more about float specifiers here.
 		"""
 	,
 		regexToMatch: /Undefined control sequence/


Client: Account: Updated AccountSessionListWrapper to create list from KodingListController instead of AccountSessinonListController.

--- client/account/lib/accountsessionlistwrapper.coffee
+++ client/account/lib/accountsessionlistwrapper.coffee
@@ -1,10 +1,10 @@
-kd             = require 'kd'
-KDView         = kd.View
-KDHeaderView   = kd.HeaderView
-
-AccountSessionList           = require './accountsessionlist'
-AccountSessionListController = require './views/accountsessionlistcontroller'
-
+kd                     = require 'kd'
+KDView                 = kd.View
+KDHeaderView           = kd.HeaderView
+AccountSessionListItem = require './accountsessionlistitem'
+KodingListController   = require 'app/kodinglist/kodinglistcontroller'
+kookies                = require 'kookies'
+whoami                 = require 'app/util/whoami'

 module.exports = class AccountSessionListWrapper extends KDView

@@ -18,12 +18,21 @@
     @top.addSubView @header = new KDHeaderView
       title : 'Active Sessions'

-    @listController = new AccountSessionListController
-      view                    : new AccountSessionList
-      limit                   : 8
-      useCustomScrollView     : yes
-      lazyLoadThreshold       : 8
+    @listController = new KodingListController
+      limit             : 8
+      lazyLoadThreshold : 8
+      noItemFoundText   : 'You have no active session.'
+      itemClass         : AccountSessionListItem
+      fetcherMethod     : (query, options, callback) ->
+        whoami().fetchMySessions options, (err, sessions)  -> callback err, sessions
+
+    @listController.on 'ItemDeleted', (item) ->
+      session  = item.getData()
+      clientId = kookies.get 'clientId'
+
+      # if the deleted session is the current one logout user immediately
+      if clientId is session.clientId
+        kookies.expire 'clientId'
+        global.location.replace '/'

     @addSubView @listController.getView()
-
-


Workspace: Added headerHint to instance.

--- client/app/CommonViews/workspace/panel/panel.coffee
+++ client/app/CommonViews/workspace/panel/panel.coffee
@@ -50,7 +50,7 @@
       @header.addSubView buttonView

   createHeaderHint: ->
-    @header.addSubView new KDCustomHTMLView
+    @header.addSubView @headerHint = new KDCustomHTMLView
       cssClass  : "help"
       tooltip   :
         title   : "Need help?"


crud model was not returning correct name

--- server/models/crud.coffee
+++ server/models/crud.coffee
@@ -104,4 +104,4 @@
   create: create
   update: update
   destroy: destroy
-  name: Resource.name+  name: Resource.modelName


Using default action name 'index' when no action is provided.

--- www/src/theoricus/core/router.coffee
+++ www/src/theoricus/core/router.coffee
@@ -78,7 +78,9 @@

       # if none is found, tries to render based on default
       # controller/action settings
-      [controller_name, action_name] = (url.replace /^\//m, '').split '/'
+      url_parts = (url.replace /^\//m, '').split '/'
+      controller_name = url_parts[0]
+      action_name = url_parts[1] or 'index'

       @the.factory.controller controller_name, (controller)=>


marketing: make it available for super admin only

--- client/app/lib/marketing/marketingcontroller.coffee
+++ client/app/lib/marketing/marketingcontroller.coffee
@@ -9,8 +9,9 @@

 ###*
  * A controller for managing marketing snippets.
- * By default snippets are available for super admin
- * but it's also possible to show snippet by command from console
+ * Snippets are available for super admin only
+ *
+ * @class
 ###
 module.exports = class MarketingController extends KDController

@@ -22,6 +23,17 @@

     @snippets      = null
     @shownSnippets = {}
+
+    @loadConfig()
+
+
+  ###*
+    Method loads snippets config from the server.
+    It works if current user is super admin
+  ###
+  loadConfig: ->
+
+    return  unless @isEnabled()

     $.ajax
       url      : @buildUrl 'config.json'
@@ -67,7 +79,7 @@
   ###
   getRandomSnippet: (callback) ->

-    return  unless @snippets and @isEnabled()
+    return  unless @snippets? and @isEnabled()

     ranges = null
     sum    = 0
@@ -106,7 +118,7 @@
   ###
   getSnippet: (name, callback) ->

-    return  unless @snippets[name]
+    return  unless @snippets?[name]

     { type, file } = @snippets[name]

@@ -128,16 +140,16 @@

   ###*
-   * Method loads snippet data and when it's ready emits event to tell that snippet needs to be shown with specified data.
-   * It doesn't have a check that current user is admin and usually it's called
-   * from console to debug snippets on UI
+   * Method loads snippet data and when it's ready emits event to tell that snippet needs to be shown
    *
    * @param {string} name - name of snippet
    * @emits SnippetNeedsToBeShown
   ###
   show: (name) ->

+    return kd.log "MarketingController: snippets are not available"  unless @snippets? and @isEnabled()
     return kd.log "MarketingController: couldn't show unknown snippet '#{name}'"  unless @snippets[name]
+
     @getSnippet name, (snippet) => @emit 'SnippetNeedsToBeShown', snippet


Oh my.

Oh my.

I assume this was left over from when ldap wasn't working

--- server/models/user.coffee
+++ server/models/user.coffee
@@ -99,7 +99,6 @@
   model.canBeEditedBy(@)

 userSchema.methods.isLDAPAccount = ->
-  return false if "production" is process.env.NODE_ENV
   return @distinguishedName?

 userSchema.methods.loginFromLocalDb = (password, callback) ->


Allow creating VMs if they are free.

Allow creating VMs if they are free.

--- client/landing/app/MainApp/paymentcontroller.coffee
+++ client/landing/app/MainApp/paymentcontroller.coffee
@@ -304,7 +304,7 @@
     if needBilling
       modal.buttons.Billing.show()
     else
-      if subscription.status is 'canceled'
+      if subscription.status is 'canceled' and amount > 0
         modal.buttons.ReActivate.show()
       else
         modal.buttons.Yes.show()


Tests: Collaboration: Helpers: Fixed PR.

--- client/test/lib/helpers/collaborationhelpers.coffee
+++ client/test/lib/helpers/collaborationhelpers.coffee
@@ -35,14 +35,15 @@

   endSessionFromStatusBar: (browser) ->

-    statusBarSelector = '.status-bar .collab-status'
+    statusBarSelector       = '.status-bar .collab-status'
+    buttonContainerSelector = statusBarSelector + ' .button-container'

     browser
       .waitForElementVisible  statusBarSelector, 20000
       .waitForElementVisible  statusBarSelector + ' span', 20000
       .click                  statusBarSelector + ' span'
-      .waitForElementVisible  '.button-container', 20000
-      .click                  '.button-container button.compact'
+      .waitForElementVisible  buttonContainerSelector, 20000
+      .click                  buttonContainerSelector + ' button.end-session'

     @endSessionModal(browser)

@@ -60,12 +61,12 @@

   endSessionModal: (browser) ->

-    endSessionButton = '.with-buttons .kdmodal-buttons'
+    buttonsSelector = '.kdmodal .kdmodal-buttons'

     browser
       .waitForElementVisible  '.with-buttons', 20000
-      .waitForElementVisible  endSessionButton, 20000
-      .click                  endSessionButton + ' button.green'
+      .waitForElementVisible  buttonsSelector, 20000
+      .click                  buttonsSelector + ' button.green'
       .waitForElementVisible  notStartedButtonSelector, 20000 # Assertion


Add option comments

Add option comments

--- src/reporter.coffee
+++ src/reporter.coffee
@@ -8,8 +8,8 @@
 # TextReporter
 #
 # options.reporterOptions
-#   satisfactory -
-#   critical -
+#   satisfactory - Satisfactory code coverage of value
+#   critical - Critical code coverage of value
 #
 class TextReporter extends Base
   constructor: (runner, options) ->


Document the resource-path option to atom executable

--- src/browser/main.coffee
+++ src/browser/main.coffee
@@ -80,6 +80,7 @@
     opened or a new window if it hasn't.
   """
   options.alias('d', 'dev').boolean('d').describe('d', 'Run in development mode.')
+  options.alias('r', 'resource-path').string('r').describe('r', 'Set the path from which Atom loads internal resources and enable dev-mode.')
   options.alias('f', 'foreground').boolean('f').describe('f', 'Keep the browser process in the foreground.')
   options.alias('h', 'help').boolean('h').describe('h', 'Print this usage message.')
   options.alias('l', 'log-file').string('l').describe('l', 'Log all output to file.')


Export SelectList from core

Export SelectList from core

--- exports/atom-api.coffee
+++ exports/atom-api.coffee
@@ -12,5 +12,6 @@
   fs: require '../src/fs-utils'
   RootView: require '../src/root-view'
   ScrollView: require '../src/scroll-view'
+  SelectList: require '../src/select-list'
   Subscriber: require '../src/subscriber'
   View: View


Add undo stack for block attributes modifications

Fix for #628

--- src/trix/controllers/editor_controller.coffee
+++ src/trix/controllers/editor_controller.coffee
@@ -8,7 +8,7 @@
 #= require trix/models/attachment_manager
 #= require trix/models/selection_manager

-{rangeIsCollapsed, rangesAreEqual, objectsAreEqual} = Trix
+{rangeIsCollapsed, rangesAreEqual, objectsAreEqual, getBlockConfig} = Trix

 class Trix.EditorController extends Trix.Controller
   constructor: ({@editorElement, document, html}) ->
@@ -387,8 +387,9 @@
     @render()

   recordFormattingUndoEntry: (attributeName) ->
+    blockConfig = getBlockConfig(attributeName)
     locationRange = @selectionManager.getLocationRange()
-    unless rangeIsCollapsed(locationRange)
+    if blockConfig or not rangeIsCollapsed(locationRange)
       @editor.recordUndoEntry("Formatting", context: @getUndoContext(), consolidatable: true)

   recordTypingUndoEntry: ->


Fixed bug with country state

Fixed bug with country state

--- src/jquery.mobilePhoneNumber.coffee
+++ src/jquery.mobilePhoneNumber.coffee
@@ -126,8 +126,8 @@
   format = formatForPhone_(phone, @data('allowPhoneWithoutPrefix'))
   country = null
   country = format.country if format
-  if @mobilePhoneCountry != country
-    @mobilePhoneCountry = country
+  if @data('mobilePhoneCountry') != country
+    @data('mobilePhoneCountry', country)
     @trigger('country.mobilePhoneNumber', country)

 mobilePhoneNumber = {}


some improvements to "spawn interpreter" UI

--- client/connect.coffee
+++ client/connect.coffee
@@ -53,9 +53,9 @@
         <legend>Spawn an interpreter</legend>

           Port <input id="spawn-port" class="text-field" value="#{DEFAULT_PORT}" size="5">
-          Spawn
+          Spawn
+

-

       </fieldset>
       <fieldset>
         <legend>Listen for connections from interpreter</legend>
@@ -90,7 +90,7 @@
         $list.trigger 'sel'
         return
       .on 'keydown', '.fav', 'ctrl+a', -> $('.fav').addClass 'sel'; $list.trigger 'sel'; false
-      .on 'keydown', '.fav', 'space ctrl+space', -> $t.toggleClass 'sel'; $list.trigger 'sel'; false
+      .on 'keydown', '.fav', 'space ctrl+space', -> $(@).toggleClass 'sel'; $list.trigger 'sel'; false
       .on 'keydown', '.fav', 'return', -> $connect.click(); false
       .on 'keydown', '.fav', 'insert', -> $new.click(); false
       .on 'keydown', '.fav', 'del', -> $delete.click(); false
@@ -139,32 +139,35 @@
       else $name.add($host).add($port).val ''
       return
     $host.add($port).add($name).on 'keydown', null, 'return', -> $save.click(); false
-    $save.click -> $('.fav.sel').focus().text fmtFav host: $host.val(), port: $port.val(), name: $name.val(); storeFavs(); return
-    $cancel.click -> x = parseFav $('.fav.sel').text(); $host.val x.host; $port.val x.port; $name.val x.name; return
+    $save.click -> $('.fav.sel').focus().text fmtFav host: $host.val(), port: $port.val(), name: $name.val(); storeFavs(); false
+    $cancel.click -> x = parseFav $('.fav.sel').text(); $host.val x.host; $port.val x.port; $name.val x.name; false
     $spawn.click ->
       port = +$spawnPort.val()
       if 0 < port < 0x10000
-        $spawnStatus.text 'Spawning...'; $spawn.button 'enable'; $spawnPort.attr 'disabled', false
+        $spawnStatus.text 'Spawning...'; $spawn.button 'disable'; $spawnPort.attr 'disabled', true
         Dyalog.socket.emit '*spawn', {port}
       else
         $spawnStatus.text 'Invalid port'; $spawnPort.focus()
-      return
-    $spawnPort.on 'keydown', null, 'return', -> console.info 'port!'; $spawn.click(); false
+      false
+    $spawnPort.on 'keydown', null, 'return', -> $spawn.click(); false
     $listenPort.on 'keydown', null, 'return', -> $listen.click(); false
     $list.sortable cursor: 'move', revert: true, tolerance: 'pointer', containment: 'parent', axis: 'y', update: storeFavs

     favs = try JSON.parse localStorage.favs catch then []
     $list.html favs.map((x) -> "#{fmtFav x}").join ''
-    $('.fav').eq(0).focus(); $list.trigger 'sel'
+    if $('.fav.sel').length then $('.fav.sel').eq(0).focus() else $('.fav').eq(0).focus().addClass 'sel'
+    $list.trigger 'sel'

     Dyalog.socket
       .on '*connected', -> Dyalog.idePage(); return
       .on '*disconnected', -> $.alert 'Interpreter disconnected'; return
       .on '*connectError', ({err}) -> $.alert err, 'Cannot connect'; return
       .on '*spawned', ({pid}) -> $spawnStatus.text "PID: #{pid}"; return
-      .on '*spawnedError', ({err}) -> $spawnStatus.text err; $('#spawn, #spawn-port').button 'disable'; return
+      .on '*spawnedError', ({err}) ->
+        $spawnStatus.text err
+        $spawn.button 'enable'; $spawnPort.attr 'disabled', false; return
       .on '*spawnedExited', ({code, signal}) ->
         $spawnStatus.text(if code? then "exited with code #{code}" else "received #{signal}")
-        $('#spawn, #spawn-port').button 'disable'; return
+        $spawn.button 'enable'; $spawnPort.attr 'disabled', false; return
     return
   return


BSR: strip trailing whitespace.

BSR: strip trailing whitespace.

--- src/sailthru.coffee
+++ src/sailthru.coffee
@@ -61,9 +61,9 @@
             else
                 # handle error
                 return false
-
+
         log2 method + ' Request'
-
+
         req = http_protocol.request options, (res) ->
             body = ''
             res.setEncoding 'utf8'
@@ -107,7 +107,7 @@
         @api_url = 'https://api.sailthru.com' if @api_url is false
         @request = new SailthruRequest

-
+
     ###
     prepare JSON payload
     ###
@@ -122,7 +122,7 @@

     ###
     Unified function for making request to API request
-    Doesn't handle multipart request
+    Doesn't handle multipart request
     ###
     _apiRequest: (action, data, method, callback) ->
         _url = url.parse @api_url
@@ -255,7 +255,7 @@
         data.copy_template = template
         data.list = list
         data.schedule_time = scheduleTime
-
+
         @apiPost 'blast', data, callback

     scheduleBlast: (name, list, scheduleTime, fromName, fromEmail, subject, contentHtml, contentText, callback, options = null) ->
@@ -268,7 +268,7 @@
         data.subject = subject
         data.content_html = contentHtml
         data.content_text = contentText
-
+
         @apiPost 'blast', data, callback

     # Template API Call
@@ -298,7 +298,7 @@
     deleteTemplate: (template, callback) ->
         @apiDelete 'template', {template: template}, callback

-
+
     # List API Call
     getLists: (callback) ->
         data =
@@ -400,4 +400,3 @@

 exports.createClient = (args...) ->
     new SailthruClient args...
-


revert line in session: esc or shift-esc

--- client/session.coffee
+++ client/session.coffee
@@ -33,14 +33,14 @@
       "'\uf81f'": -> histMove -1
       'Shift-Ctrl-Backspace': -> histMove 1
       'Shift-Ctrl-Enter': -> histMove -1
-      Esc: ->
-        console.info 'esc'
+      Esc: revertLine = ->
         l = cm.getCursor().line
         if mod[l]?
           cm.replaceRange mod[l], {line: l, ch: 0}, {line: l, ch: cm.getLine(l).length}, 'Dyalog'
           delete mod[l]
           cm.removeLineClass l, 'background', 'modified'
         return
+      'Shift-Esc': revertLine

   exec = (trace) ->
     a = [] # pairs of [lineNumber, contentToExecute]


prevent multiple content displays from opening when switching videos

--- client/landing/app/Applications/Activity.kdapplication/ContentDisplays/tutorial.coffee
+++ client/landing/app/Applications/Activity.kdapplication/ContentDisplays/tutorial.coffee
@@ -214,10 +214,12 @@
         if @after.length >0
           @listAnchorNext.addSubView new TutorialListSwitchBox
             direction:"next"
+            delegate:@
           , @after[0]
         if @before.length >0
           @listAnchorPrevious.addSubView new TutorialListSwitchBox
             direction:"previous"
+            delegate:@
           , @before[@before.length-1]

@@ -336,6 +338,7 @@

     @outgoingContainer = new KDView
       click:=>
+        @getSingleton("contentDisplayController").emit "ContentDisplayWantsToBeHidden", @getDelegate()
         appManager.tell "Activity", "createContentDisplay", @getData()


Matchmake by region (forced ALL REGIONS)

--- public/js/services.coffee
+++ public/js/services.coffee
@@ -117,6 +117,7 @@
   startMatchmake: (mods)->
     @call "matchmake",
       mods: mods
+      region: 0

   stopFinding: ->
     @call "stopqueue", null


Csv library was turning cells that begin with numbers into numeric values. Addresses TP #464. A form does not load completely if question label starts with a number

--- dkobo/koboform/static/js/xlform/model_xlform.coffee
+++ dkobo/koboform/static/js/xlform/model_xlform.coffee
@@ -609,26 +609,14 @@
   "createSurveyFromCsv"
 ###
 XLF.createSurveyFromCsv = (csv_repr)->
-  opts = {}
-  $settings   = opts.settings || {}
-  # $launchEditor = if "launchEditor" in opts then opts.launchEditor else true
-  $elemWrap   = $(opts.elemWrap || '#main')
-  $publishCb  = opts.publish || ->
-
-  if csv_repr
-    if opts.survey or opts.choices
-      throw new XlformError """
-      [csv_repr] will cause other options to be ignored: [survey, choices]
-      """
-    cobj = csv.sheeted(csv_repr)
-    $survey = if (sht = cobj.sheet "survey") then sht.toObjects() else []
-    $choices = if (sht = cobj.sheet "choices") then sht.toObjects() else []
-
-    if (settingsSheet = cobj.sheet "settings")
-      $settings = settingsSheet.toObjects()[0]
-  else
-    $survey   = opts.survey || []
-    $choices  = opts.choices || []        # settings: $settings
+  csv.settings.parseFloat = false
+
+  cobj = csv.sheeted(csv_repr)
+  $survey = if (sht = cobj.sheet "survey") then sht.toObjects() else []
+  $choices = if (sht = cobj.sheet "choices") then sht.toObjects() else []
+
+  if (settingsSheet = cobj.sheet "settings")
+    $settings = settingsSheet.toObjects()[0]

   new XLF.Survey(survey: $survey, choices: $choices, settings: $settings)


PR #330: Switch if conditions

PR #330: Switch if conditions

--- src/middleware/authorisation.coffee
+++ src/middleware/authorisation.coffee
@@ -90,8 +90,8 @@
               # deny access to channel if the content type isn't set
               continue

-          # now check if message content matches and that the status is 'enabled'
-          if matchContent(channel, ctx.body) and isChannelEnabled channel
+          # now check that the status is 'enabled' and if the message content matches
+          if isChannelEnabled(channel) and matchContent(channel, ctx.body)
             ctx.authorisedChannel = channel
             logger.info "The request, '" + ctx.request.url + "' is authorised to access " + ctx.authorisedChannel.name
             return done()


show nav on up scroll

show nav on up scroll

--- src/components/pinned-header-footer-card/index.cjsx
+++ src/components/pinned-header-footer-card/index.cjsx
@@ -14,6 +14,10 @@

   getDefaultProps: ->
     buffer: 60
+    scrollSpeedBuffer: 20
+
+  getInitialState: ->
+    pinned: false

   mixins: [ScrollListenerMixin]

@@ -27,8 +31,24 @@
   componentWillUnmount: ->
     document.body.classList.remove(@documentBodyClass)

-  shouldPinHeader: (scrollTop) ->
-    scrollTop > @props.buffer
+  isScrollingSlowed: (prevScrollTop, currentScrollTop) ->
+    Math.abs(prevScrollTop - currentScrollTop) <= @props.scrollSpeedBuffer
+
+  isScrollingUp: (prevScrollTop, currentScrollTop) ->
+    (prevScrollTop - currentScrollTop) > @props.scrollSpeedBuffer
+
+  shouldPinHeader: (prevScrollTop, currentScrollTop) ->
+    if @isScrollingSlowed(prevScrollTop, currentScrollTop)
+      # keep as previous pinned state
+      shouldPinHeader = @state.pinned
+    else if @isScrollingUp(prevScrollTop, currentScrollTop)
+      # unpinned on upscroll
+      shouldPinHeader = false
+    else
+      # otherwise, on down scroll, check if the scroll position is past buffer
+      shouldPinHeader = (currentScrollTop > @props.buffer)
+
+    shouldPinHeader

   shouldComponentUpdate: (nextProps, nextState) ->
     # ignore scrolling state changes when checking should component update
@@ -36,7 +56,7 @@
     nextStateNoScroll = _.omit(nextState, 'isScrolling', 'scrollTop')

     # manually check if should pin has changed
-    didShouldPinChange = not @shouldPinHeader(@state.scrollTop) is @shouldPinHeader(nextState.scrollTop)
+    didShouldPinChange = not @state.pinned is @shouldPinHeader(@state.scrollTop, nextState.scrollTop)
     # check props and non-scroll states
     didPropsUpdate = not _.isEqual(@props, nextProps)
     didStateUpdate = not _.isEqual(stateNoScroll, nextStateNoScroll)
@@ -49,7 +69,8 @@
       'add' # add class if shouldPinHeader is true
     ]

-    shouldPinHeader = @shouldPinHeader(@state.scrollTop) * 1
+    @setState(pinned : @shouldPinHeader(prevState.scrollTop, @state.scrollTop))
+    shouldPinHeader = @state.pinned * 1
     classAction = addOrRemove[shouldPinHeader]
     document.body.classList[classAction]('pinned-on')


Refactor to send notification

Refactor to send notification

--- app/assets/javascripts/kiitos/webnotifications/create.js.coffee
+++ app/assets/javascripts/kiitos/webnotifications/create.js.coffee
@@ -9,10 +9,8 @@

   create::newAlert = ->
     engine = new webNotification.getEngine()
-    notification = if engine.name is 'webkit'
-      window.webkitNotifications.createNotification @icon, @title, @message
-    else
-      Notification.call @title, @message
+    notification =
+      new Notification @title, { title: @title, icon: @icon, body: @message }
     notification.show()
     setInterval (-> notification.cancel()), 5000


Better formatted toString

Better formatted toString

--- lib/generators/nas_extjs/templates/public/app/model/Base.coffee
+++ lib/generators/nas_extjs/templates/public/app/model/Base.coffee
@@ -38,7 +38,7 @@
             prx.setAssociations = names.slice(0)

     toString:->
-        this.$className + ' ' + ( if this.phantom then "(new)" else String(this.getId()) )
+        Util.baseClassName(this) + ' ' + ( if this.phantom then "(new)" else String(this.recordIdentifier()) )

     copyFrom: (sourceRecord) ->
         this.callParent( arguments )


Don't start an already running timer.

Don't start an already running timer.

--- app/assets/javascripts/modules/timelet/models/timelet.js.coffee
+++ app/assets/javascripts/modules/timelet/models/timelet.js.coffee
@@ -39,6 +39,7 @@
   #
   start: ->
     return unless @isValid()
+    return if @isRunning()

     @set running: true
     @runner = setInterval @tick, 1000


bad indent

bad indent

--- src/openpgp/packet/keymaterial.iced
+++ src/openpgp/packet/keymaterial.iced
@@ -480,7 +480,7 @@
         expire_in = sig.get_key_expires()
         sig_generated = sig.when_generated()
         if not uid_winner? or uid_winner.sig_generated < sig_generated
-            uid_winner = { expire_in, sig_generated }
+          uid_winner = { expire_in, sig_generated }

       if uid_winner?
         uid_expire_in = uid_winner.expire_in or 0


Batman.DOM.querySelector* should default to document if no node is given.

--- src/platform/lib/dom_helpers.coffee
+++ src/platform/lib/dom_helpers.coffee
@@ -1,11 +1,11 @@
 Batman.extend Batman.DOM,
   querySelectorAll: if window?.document?.querySelectorAll?
-      (node, selector) -> node.querySelectorAll(selector)
+      (node = document, selector) -> node.querySelectorAll(selector)
     else
       -> Batman.developer.error("Please include either jQuery or a querySelectorAll polyfill, or set Batman.DOM.querySelectorAll to return an empty array.")

   querySelector: if window?.document?.querySelector?
-      (node, selector) -> node.querySelector(selector)
+      (node = document, selector) -> node.querySelector(selector)
     else
       -> Batman.developer.error("Please include either jQuery or a querySelector polyfill, or set Batman.DOM.querySelector to an empty function.")


Code cleanup in writeTeX() call

Code cleanup in writeTeX() call

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -1226,7 +1226,7 @@
           mappings.push input
         else if input instanceof Drawing or input instanceof Drawings
           filenames = input.writeSVG mappings
-          input.writeTeX mappings if Symbol.texText
+          input.writeTeX() if Symbol.texText
           for format in formats
             if typeof filenames == 'string'
               jobs.push convertSVG format, filenames, sync


only cache elements once while initializing (coffee)

--- Resources/halloplugins/image.coffee
+++ Resources/halloplugins/image.coffee
@@ -548,7 +548,7 @@
                                 stop: dnd.handleStopEvent
                                 disabled: not editable.hasClass('inEditMode')
                                 cursorAt: {top: 50, left: 50}
-                        draggables.push elem
+                            draggables.push elem

                     jQuery(".rotationWrapper img", widgetOptions.dialog).each (index, elem) ->
                         initDraggable(elem) if not elem.jquery_draggable_initialized


Respect authentication port given

Respect authentication port given

--- src/scripts/wowser/ui/screens/authentication.coffee
+++ src/scripts/wowser/ui/screens/authentication.coffee
@@ -18,7 +18,7 @@
       @session.auth.disconnect()

   connect: ->
-    @session.auth.connect(@host)
+    @session.auth.connect(@host, @port)

   authenticate: ->
     @session.auth.authenticate(@username, @password)


CredentialsListController: Copy and class name changes.

--- client/account/lib/views/accountcredentiallistcontroller.coffee
+++ client/account/lib/views/accountcredentiallistcontroller.coffee
@@ -181,9 +181,9 @@
       view.addSubView view.intro = new kd.CustomHTMLView
         cssClass  : 'credential-creation-intro'
         partial   : '''
-
Here is how you add your AWS keys
+
Add your AWS credentials

-
Login to AWS console

+
Create an AWS user

Attach the AdministratorAccess policy

Add the Access Key ID and Secret here

@@ -192,6 +192,7 @@

     { ui }    = kd.singletons.computeController
     view.form = ui.generateAddCredentialFormFor options
+    view.form.setClass 'credentials-form-view'

     view.form.on "Cancel", ->
       view.unsetClass "form-open"


Use fetch method of envDataProvider before navigation.

--- client/app/lib/activity/sidebar/sidebarmachinesharepopup.coffee
+++ client/app/lib/activity/sidebar/sidebarmachinesharepopup.coffee
@@ -159,7 +159,8 @@

         return @destroy()  if wasApproved

-        doNavigation()
+        envDataProvider.fetch =>
+          doNavigation()

       { channelId } = @getOptions()


No need this anymore since we can handle broken symlinks

--- client/landing/app/MainApp/filetree/itemviews/folderitem.coffee
+++ client/landing/app/MainApp/filetree/itemviews/folderitem.coffee
@@ -11,8 +11,3 @@
     if data.getExtension() is "kdapp"
       data.on "fs.delete.finished", =>
         @getSingleton("kodingAppsController").refreshApps()
-
-        # Thats for broken symlinks under application temp folder
-        # Maybe we can change its place, keep it for now
-        {nickname} = KD.whoami().profile
-        KD.utils.removeBrokenSymlinksUnder "/home/#{nickname}/Sites/#{nickname}.koding.com/website/.applications/"


groups: login/register  fix (will not hide landing page anymore)

--- client/landing/app/MainApp/maincontroller/maincontroller.coffee
+++ client/landing/app/MainApp/maincontroller/maincontroller.coffee
@@ -124,7 +124,7 @@

   handleLoginScreenRoute:(action, path)->
     ldc = @getSingleton 'lazyDomController'
-    ldc.hideLandingPage() if path and path != 'Logout'
+    ldc.hideLandingPage() if path and path not in ['Logout', 'Login', 'Register']
     @loginScreen.animateToForm action

   doJoin:->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -21,7 +21,7 @@
   # => 13
 </pre></code>

-@name Cooldown
+@name Cont
 @module
 @constructor
 @param {Object} I Instance variables


Fix: github.comのリポジトリだと説明が読み込めないのを修正

Fix: github.comのリポジトリだと説明が読み込めないのを修正

--- src/core/request.coffee
+++ src/core/request.coffee
@@ -36,7 +36,9 @@
 getDetailUrl = (repo, id, lang) ->
   switch repo.type
     when "remote"
-      names = repo.name.split("/")
+      m = /^https?:\/\/github\.com\/(.+?)\/(.+?)\/raw\/master$/.exec(repo.name)
+      if m?
+        return "https://cdn.rawgit.com/#{m[1]}/#{m[2]}/master/Detail/#{lang}/#{id}.html"
       return "#{repo.name}/Detail/#{lang}/#{id}.html"
     when "local"
       return "file://" + path.join(repo.name, "Detail", lang, "#{id}.html")


Remove .coffee version of dbController

Remove .coffee version of dbController

--- src/dbController.coffee
+++ src/dbController.coffee
@@ -1,38 +0,0 @@
-mongo = require 'mongoskin'
-uuid = require 'uuid'
-DB = process.env.DBPORT || 'users'
-db = mongo.db("mongodb://localhost:27017/#{DB}", {native_parser: true})
-
-db.bind('user').bind({
-    getByID: (repoID, callback) ->
-        this.findOne({repoID: repoID}, callback)
-})
-
-# Class for interracting with MongoDB using Mongoskin
-# @see https://github.com/kissjs/node-mongoskin Mongoskin reference
-class DBController
-    # Retrive repository data from database
-    #
-    # @example
-    #   dbController.findRepo(repoID, (err, data) ->
-    #       console.error if err
-    #       console.log data
-    #   )
-    # @param [String] repoID Key that stored in DB for specific repository
-    # @param [Function] cb Callback that will be called with two params: error and data
-    # @author g07cha
-    @findRepo: (repoID, cb) ->
-        db.user.getByID(repoID, cb)
-
-    # Add repository to db
-    #
-    # @param [String] repository "username/name" string
-    # @return [String] repoID UUID for future queries
-    # @author g07cha
-    @addRepo: (repository) ->
-        if repository
-            repoID = uuid.v1();
-            db.user.insertOne({repository: repository, repoID: repoID})
-            repoID
-
-module.exports = DBController


use docker inspect output

use docker inspect output

--- src/docker-utils.coffee
+++ src/docker-utils.coffee
@@ -64,10 +64,15 @@
 			# Cleanup containers first, so that they don't block image removal.
 			docker.listContainersAsync(all: true)
 			.filter (containerInfo) ->
-				isUserApp = _.contains(apps, containerInfo.Image)
-				isSupervisor = _.contains(supervisorImages, containerInfo.Image)
-				isRunning = not containerInfo.Status.match(/^Exited/)
-				not (isUserApp or (isSupervisor and isRunning))
+				# Do not remove user apps.
+				if _.contains(apps, containerInfo.Image)
+					return false
+				# Do not remove running supervisors.
+				else if _.contains(supervisorImages, containerInfo.Image)
+					return containerHasExited(containerInfo.Id)
+				# Remove everything else.
+				else
+					return true
 			.map (containerInfo) ->
 				docker.getContainer(containerInfo.Id).removeAsync()
 				.then ->
@@ -87,6 +92,11 @@
 						console.log('Deleted image:', image.Id, image.RepoTags)
 					.catch (err) ->
 						console.log('Error deleting image:', image.Id, image.RepoTags, err)
+
+	exports.containerHasExited = (id) ->
+		docker.getContainer(id).inspectAsync()
+		.then (data) ->
+			return not data.Status.Running

 	# Return true if an image exists in the local docker repository, false otherwise.
 	exports.imageExists = imageExists = (imageId) ->


Start converting chat event handlers

Start converting chat event handlers

--- Web/js/classes/chat.coffee
+++ Web/js/classes/chat.coffee
@@ -1,13 +1,49 @@
 class window.ChatManager
 	constructor: (@rohbot)->
-		#beep boop
+		@chat  = $ '#chat'
+		@input = $ '#messageBox'
+		@send  = $ '#send'
+
+		@input.on 'keydown', @processEnter
+		@send.on  'click',   @processSend
+
+	processSend: =>
+		message = @input.val()
+		return unless message.length
+
+		unless @processCommand message
+			@rohbot.sendMessage message
+
+		@input.val ''
+
+	processEnter: (event) =>
+		# Converted from http://stackoverflow.com/a/3533099/1056845
+		return unless event.keyCode == 13
+		unless event.ctrlKey
+			@input.blur().focus()
+			@send.click()
+			return
+		dom = @input[0]
+		if typeof dom.selectionStart == "number" and typeof dom.selectionEnd == "number"
+			start = dom.selectionStart
+			val = dom.value
+			dom.value = val.slice( 0, start ) + "\n" + val.slice( dom.selectionEnd )
+			dom.selectionStart = dom.selectionEnd = start + 1
+		else if document.selection && document.selection.createRange
+			dom.focus()
+			range = document.selection.createRange()
+			range.text = "\r\n"
+			range.collapse(false)
+			range.select()
+
+	clearChat: -> @chat.empty();

 	processCommand: (text) ->
 		return false unless ! text.indexOf('~') || ! text.indexOf('/')
 		command = text.substr( 1 ).toLowerCase();

 		if ! command.indexOf 'clear'
-			window.$('chat').empty();
+			@clearChat()
 		else if ! command.indexOf 'logout'
 			@rohbot.login("guest", null, null);
 		else if ! command.indexOf 'password'


use default printer

use default printer

--- commands/print.coffee
+++ commands/print.coffee
@@ -52,7 +52,7 @@
         if ext in "doc|ppt|pps|xls|odt|ods|odp".split('|')
             command = "openoffice -headless -norestore -p '#{path}'"
         else
-            command = "lpr -P nhonho '#{path}'"
+            command = "lpr '#{path}'"

         florinda.say "Printing #{filename}.#{ext}"
         exec command, (err, stdout, stderr) ->


Fix autoopen functionality

Fix autoopen functionality

--- lib/context/context-variable-list-view.coffee
+++ lib/context/context-variable-list-view.coffee
@@ -12,7 +12,7 @@
           @span class: 'type php', params.summary
         @ul outlet: "contextVariableList"

-  initialize: ({@variables}) ->
+  initialize: ({@variables,@autoopen}) ->
     @render()

   render: ->


re-introduce alignment graph

re-introduce alignment graph

--- src/app/units/partials/directives/unit-outcome-alignment.coffee
+++ src/app/units/partials/directives/unit-outcome-alignment.coffee
@@ -4,7 +4,7 @@
   replace: true
   restrict: 'E'
   templateUrl: 'units/partials/templates/unit-outcome-alignment.tpl.html'
-  controller: ($scope, $filter, currentUser, unitService, alertService, gradeService, LearningAlignments, projectService, taskService) ->
+  controller: ($scope, $filter, currentUser, unitService, alertService, gradeService, LearningAlignments, projectService, taskService, Visualisation) ->

     if $scope.project?
       $scope.source = $scope.project
@@ -100,6 +100,35 @@
             alertService.add("danger", "Error: " + response.data.error, 6000)
       )

+    xFn = (d) -> d.label
+    yFn = (d) -> d.value
+
+    $scope.options = Visualisation 'multiBarChart', {
+      clipEdge: yes
+      stacked: yes
+      height: 440
+      width: 600
+      margin:
+        left: 75
+        right: 50
+      rotateLabels: 30
+      duration: 500
+      x: xFn
+      y: yFn
+      forceY: 0
+      showYAxis: no
+      # xAxis:
+      #   axisLabel: "Outcomes"
+        # tickFormat: xAxisTickFormatDateFormat
+      # yAxis:
+      #   axisLabel: "Tasks Remaining"
+        # tickFormat: yAxisTickFormatPercentFormat
+      # color: colorFunction
+      # legendColor: colorFunction
+      # yDomain: [0, ]
+      # xDomain: [+new Date($scope.unit.start_date).getTime() / 1000, lateEndDate()]
+    }
+
     $scope.visualisationData = []

     $scope.calculateAlignmentVisualisation = (source) ->
@@ -128,7 +157,7 @@
       _.each outcomes, (outcome, key) ->
         _.each outcome, (tmp, key1) ->
           scale = Math.pow(2, parseInt(key1,10))
-          values[key1].push [ $scope.unit.outcome(parseInt(key,10)).name,  _.reduce(tmp, ((memo, num) -> memo + num), 0) * scale ]
+          values[key1].push { label: $scope.unit.outcome(parseInt(key,10)).name, value:  _.reduce(tmp, ((memo, num) -> memo + num), 0) * scale }

       _.each values, (vals, idx) ->
         result.push { key: gradeService.grades[idx], values: vals }
@@ -136,13 +165,4 @@
       result

     $scope.visualisationData = $scope.calculateAlignmentVisualisation($scope.source)
-
-    $scope.yFunction = () ->
-      (d) ->
-        d[1]
-
-    $scope.xFunction = () ->
-      (d) ->
-        d[0]
-
 )


Fixed double sent login data.

Fixed double sent login data.

--- app/assets/javascripts/app/controllers/login.js.coffee
+++ app/assets/javascripts/app/controllers/login.js.coffee
@@ -52,9 +52,10 @@
     # scroll to top
     @scrollTo()

-
   login: (e) ->
     e.preventDefault()
+    e.stopPropagation()
+
     @formDisable(e)
     params = @formParam(e.target)


Fixing ambiguous coffeescript that was generating incorrect js

--- app/assets/javascripts/jquery.infinite-pages.js.coffee
+++ app/assets/javascripts/jquery.infinite-pages.js.coffee
@@ -80,8 +80,8 @@
         @_loading()

         $.getScript(@$container.find(@options.navSelector).attr('href'))
-          .done (=> @_success())
-          .fail (=> @_error())
+          .done(=> @_success())
+          .fail(=> @_error())

     _loading: ->
       @options.state.loading = true


Add 10% padding on top of both overview and detail graph.

--- app/assets/javascripts/pgdatagraph/datagraph.js.coffee
+++ app/assets/javascripts/pgdatagraph/datagraph.js.coffee
@@ -140,6 +140,8 @@
       series: series
       renderer: "multi"
       interpolation: "linear"
+      padding:
+        top: 0.1
       dotSize: 2
       unstack: @options.unstack

@@ -185,6 +187,8 @@
       series: series
       renderer: "multi"
       interpolation: "linear"
+      padding:
+        top: 0.1
       unstack: @options.unstack
       ticksTreatment: "glow"


Add test for extracting nested errors in Query service

--- test/unit/service_interface/query.spec.coffee
+++ test/unit/service_interface/query.spec.coffee
@@ -104,6 +104,16 @@
       expect(response.error.message).toEqual(null)
       expect(response.data).toEqual(null)

+    it 'extracts error when <Error> is nested', ->
+      extractError """
+      <SomeResponse>
+        <Error>
+          code<Message>msg</Message>
+        </Error>
+      </SomeResponse>"""
+      expect(response.error.code).toEqual('code')
+      expect(response.error.message).toEqual('msg')
+
   describe 'extractData', ->
     extractData = (body) ->
       response.httpResponse.statusCode = 200


Remove unused variable

Remove unused variable

--- resources/assets/coffee/react/beatmap-discussions/new-discussion.coffee
+++ resources/assets/coffee/react/beatmap-discussions/new-discussion.coffee
@@ -287,8 +287,6 @@

     type = e.currentTarget.dataset.type

-    userCanResetNominations = currentUser.is_admin || currentUser.is_nat || currentUser.is_bng
-
     if type == 'problem'
       problemType = @problemType()


rewrite this to be more friendly for variadic function signatures

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -368,8 +368,8 @@
               else callback null, arr

   fetchMembers$: permit 'list members',
-    success:(client, selector, options, callback)->
-      [selector, options, callback] = Module.limitEdges 100, [selector, options, callback]
+    success:(client, rest...)->
+      [selector, options, callback] = Module.limitEdges 100, rest
       @fetchMembers selector, options, callback

   # fetchMyFollowees: permit 'list members'


media attaches as featured image

media attaches as featured image

--- src/adaptor/ewao_wordpress.coffee
+++ src/adaptor/ewao_wordpress.coffee
@@ -42,9 +42,7 @@
     #   @writeMedia data, type

     @writeMedia url: article.image
-      .then (imageId) =>
-        imageUrl = "/xxx/#{imageId}"
-
+      .then (media) =>
         data =
           post_type:    'post'
           post_status:  'publish'
@@ -53,8 +51,7 @@
           post_title:   article.title
           post_author:  1 #@authors[article.author]
           post_excerpt: article.description
-          # post_format: 'image'
-          # post_thumbnail: imageUrl
+          post_thumbnail: media.id
           post_status: if article.draft then 'draft' else 'publish'
           post_name: article.slug
           # terms_names:
@@ -85,15 +82,16 @@
         type: type
         bits: new Buffer data, 'base64'
         overwrite: false
+        url: "/wp-content/uploads/2015/10/#{filename}"

       log "writing media: #{media.name}, #{media.type}"
-      @wordpress.uploadFile media, (error, id) ->
+      @wordpress.uploadFile media, (error, result) ->
         if error
-          console.error red error, "\nfilename: ", media.name
+          console.error red error, "\nfilename: ", result.file
           reject error
         else
-          debug "created media: #{id}"
-          resolve id
+          log "created media: #{JSON.stringify result}"
+          resolve result

   _getRemoteMedia: (url) ->
     url = @_makeSaneUrl url


added Channel endpoint

added Channel endpoint

--- src/model/statsd.coffee
+++ src/model/statsd.coffee
@@ -12,75 +12,84 @@
 # Overall Metrics

 exports.retrieveTransactionCountPerHour = `function *() {
-    var path = "/render?target=transformNull(summarize(stats.counters." + domain + ".Channels.count,'1hour'))&from=-1days&format=json";
-    var data = [];
-    var raw =  yield fetchData(path);
+  var path = "/render?target=transformNull(summarize(stats.counters." + domain + ".Channels.count,'1hour'))&from=-1days&format=json";
+  var data = [];
+  var raw = yield fetchData(path);

-    _.forEach(raw.data, function (item){
-        data.push({
-          load: item[0],
-          timestamp: moment.unix(item[1])
-        });
+  _.forEach(raw.data, function (item) {
+    data.push({
+      load: item[0],
+      timestamp: moment.unix(item[1])
     });
-    this.body = data
+  });
+  this.body = data
 }`

 exports.retrieveAverageLoadTimePerHour = `function *() {
-    var path = "/render?target=transformNull(summarize(stats.timers." + domain + ".Channels.mean,'1hour'))&from=-1days&format=json";
-    var data = [];
-    var raw = yield fetchData(path);
+  var path = "/render?target=transformNull(summarize(stats.timers." + domain + ".Channels.mean,'1hour'))&from=-1days&format=json";
+  var data = [];
+  var raw = yield fetchData(path);

-    _.forEach(raw.data, function (item){
-        data.push({
-          avgResp: item[0],
-          timestamp: moment.unix(item[1])
-        });
+  _.forEach(raw.data, function (item) {
+    data.push({
+      avgResp: item[0],
+      timestamp: moment.unix(item[1])
     });
-    this.body = data
+  });
+  this.body = data
 }`

-exports.retrieveChannelMetrics = `function *(type,channelId){
-    var path = "/render?target=transformNull(summarize(stats.counters." + domain + ".Channels." + channelId + ".count,'1day'))&from=-1weeks&format=json";
-    var data = [];
-    var raw =  yield fetchData(path);
+exports.retrieveChannelMetrics = `function *(type, channelId) {
+  var path = "/render?target=transformNull(summarize(stats.counters." + domain + ".Channels." + channelId + ".count,'1day'))&from=-1weeks&format=json";
+  path += "&target=transformNull(summarize(stats.timers." + domain + ".Channels." + channelId + ".mean,'1day'))";
+  var data = [];
+  var raw = yield fetchData(path);

-    _.forEach(raw.data, function (item){
-        data.push({
-            load: item[0],
-            timestamp: moment.unix(item[1])
-        });
+  _.forEach(raw.data, function (item) {
+    data.push({
+      load: item[0],
+      timestamp: moment.unix(item[1])
     });
-    this.body = data
+  });
+  this.body = data
 }`

 exports.retrieveSumOfTransactionsPerPeriod = `function *(period) {
-    var path = '/render?target=integral(stats.counters.' + domain + '.Channels.count)&from=' + period + '&format=json';
-    this.body = yield fetchData(path);
+  var path = '/render?target=integral(stats.counters.' + domain + '.Channels.count)&from=' + period + '&format=json';
+  this.body = yield fetchData(path);
 }`

 exports.transactionsPerChannelPerHour = `function *(period) {
   var path = "/render?target=summarize(stats.counters." + domain + ".Channels.count,'1hour')&from=-1days&format=json";
   path = "/render?target=summarize(stats.counters.OpenHIM-core-js-preprod.Production.Channels.count,'1hour')&from=-1days&format=json";
   var data = [];
-  var raw =  yield fetchData(path);
-
-  _.forEach(raw.data, function (item){
-      data.push({
-        load: item[0],
-        timestamp: moment.unix(item[1])
-      });
+  var raw = yield fetchData(path);
+  var i = 0;
+  _.forEach(raw.data, function (item) {
+    data.push({
+      load: item[0],
+      avgResp: raw.data1[i][0],
+      timestamp: moment.unix(item[1])
+    });
+    i++;
   });
   this.body = data
 }`

-fetchData =  `function *fetchData(path) {
+fetchData = `function *fetchData(path) {
   var options = {
-      url: 'http://' + statsd_server.host + path
+    url: 'http://' + statsd_server.host + path
   };
   var response = yield request(options);
   var info = JSON.parse(response.body);
-  var data = {
-    data: info[0].datapoints
-  };
+  var data = {};
+
+  if (info[0]) {
+    data.data = info[0].datapoints
+  }
+
+  if (info[1]) {
+    data.data1 = info[1].datapoints
+  }
   return data;
 }`


Restore cache

Restore cache

--- src/module-cache.coffee
+++ src/module-cache.coffee
@@ -123,8 +123,8 @@

   originalResolveFilename = Module._resolveFilename
   Module._resolveFilename = (relativePath, parentModule) ->
-    # resolvedPath = getCachedModulePath(relativePath, parentModule)
-    originalResolveFilename(relativePath, parentModule)
+    resolvedPath = getCachedModulePath(relativePath, parentModule)
+    resolvedPath ? originalResolveFilename(relativePath, parentModule)
   registered = true

 exports.add = (directoryPath) ->


:racehorse: Join paths manually

:racehorse: Join paths manually

path.join calls path.normalize and with the number of paths
being pushed through the cache it is faster to require them
manually since they are already normalized

--- src/module-cache.coffee
+++ src/module-cache.coffee
@@ -222,26 +222,29 @@
   return

 exports.add = (directoryPath, metadata) ->
+  # path.join isn't used in this function for speed since path.join calls
+  # path.normalize and all the paths are already normalized here.
+
   unless metadata?
     try
-      metadata = require(path.join(directoryPath, 'package.json'))
+      metadata = require("#{directoryPath}#{path.sep}package.json")
     catch error
       return

   cacheToAdd = metadata?._atomModuleCache
   for dependency in cacheToAdd?.dependencies ? []
     cache.dependencies[dependency.name] ?= {}
-    cache.dependencies[dependency.name][dependency.version] ?= path.join(directoryPath, dependency.path)
+    cache.dependencies[dependency.name][dependency.version] ?= "#{directoryPath}#{path.sep}#{dependency.path}"

   for entry in cacheToAdd?.folders ? []
     for folderPath in entry.paths
-      cache.folders[path.join(directoryPath, folderPath)] = entry.dependencies
+      cache.folders["#{directoryPath}#{path.sep}#{folderPath}"] = entry.dependencies

   if directoryPath is cache.resourcePath
     for extension, paths of cacheToAdd?.extensions
       cache.extensions[extension] ?= new Set()
       for filePath in paths
-        cache.extensions[extension].add(path.join(directoryPath, filePath))
+        cache.extensions[extension].add("#{directoryPath}#{path.sep}#{filePath}")

   return


add settings to show side comments

add settings to show side comments

--- server/boot.coffee
+++ server/boot.coffee
@@ -6,6 +6,8 @@
   settings:
     adminRole: null
     adminGroup: null
+    comments:
+      useSideComments: null
     rss:
       title: ''
       description: ''
@@ -72,3 +74,7 @@
           return false

       true
+
+    showSideComments: () ->
+      console.log Blog.settings.comments.useSideComments
+      Blog.settings.comments.useSideComments


不要なタスクを削除

不要なタスクを削除

--- grunt/browserify.coffee
+++ grunt/browserify.coffee
@@ -16,13 +16,6 @@
 				]
 			]
 		]
-	libs:
-		options:
-			plugin: []
-		files:
-			"<%= DEV_PC_PATH %><%= JS_DIR %>libs-for-legacy.js": [
-				"<%= DEV_PC_PATH %><%= COFFEE_DIR %>LibsForLegacy.coffee"
-			]
 	dev_pc:
 		files:
 			"<%= DEV_PC_PATH %><%= JS_DIR %>app.js": [
@@ -64,4 +57,4 @@
 		files:
 			"<%= PRODUCTION_SP_PATH %><%= JS_DIR %>app.js": [
 				"<%= DEV_PC_PATH %><%= COFFEE_DIR %>**/*.coffee"
-			]+			]


Add ability to find limited number of events

--- lib/event_store/mongodb.coffee
+++ lib/event_store/mongodb.coffee
@@ -75,6 +75,9 @@

   findAllEventsByAggregateUid: (aggregateUid, callback) ->
     @_find aggregateUid: aggregateUid, callback
+
+  findSomeEventsByAggregateUidAfterVersion: (aggregateUid, version, eventCount, callback) ->
+    @_findLimited { aggregateUid: aggregateUid }, version, eventCount, callback

   findAllEventsByAggregateUidAfterVersion: (aggregateUid, version, callback) ->
     @_find aggregateUid: aggregateUid, version: { $gt: version }, callback
@@ -155,6 +158,20 @@
         @logger.log "MongoDbEventStore#saveSnapshot", "saved snapshot for aggregate \"#{snapshot.aggregateUid}\""
       callback? err

+  _findLimited: (params, version, eventCount, callback) ->
+    p = new Profiler "MongoDbEventStore#_findLimited(db request)", @logger
+    p.start()
+    params.version = { $gt: version }
+    @eventCollection.find(params).sort("version":1).limit(eventCount).toArray (err, items) =>
+      p.end()
+
+      if err?
+        callback err
+      else if not items?
+        callback null, []
+      else
+        @_instantiateEventsFromRows items, callback
+
   _find: (params, callback) ->
     p = new Profiler "MongoDbEventStore#_find(db request)", @logger
     p.start()


Add the attributes of the model as data attribute in case there is a need for more in the HTML.

--- lib/helpers/view_helper.coffee
+++ lib/helpers/view_helper.coffee
@@ -140,6 +140,15 @@
   #console.log "domain", domain
   minimalHtml = if opt.minimalHtml? then opt.minimalHtml else false
   noGrid = if opt.noGrid then opt.noGrid else false
+  autoCompleteFields = (context)->
+    subHTML = ""
+    if opt.autoCompleteFields
+      fieldNames = opt.autoCompleteFields.split(',')
+      fieldNames.forEach((fieldName)->
+        if context[fieldName]
+          subHTML = subHTML + "data-#{fieldName}='#{context[fieldName]}'"
+      )
+    return subHTML
   isDisplayRequired = false
   isRequired = ()=>
     isDisplayRequired = false
@@ -254,7 +263,7 @@

                    #{icon()}
-                  <input id='#{property}' #{decorator()} class='"
+                  <input id='#{property}' #{decorator()} #{autoCompleteFields(@)} class='"
       if(dataType != "checkbox") then html +="form-control "
       html += "input-sm' data-name='#{property}' type='#{dataType}' #{inputAttributes} #{placeholder} #{propertyValue()} #{readonly} #{disabled}/>
                   #{symbol()}"


removed easily distiguishable chars, added <> like

--- lib/highlight-bad-chars.coffee
+++ lib/highlight-bad-chars.coffee
@@ -1,28 +1,28 @@

 chars = [
   # https://github.com/possan/sublime_unicode_nbsp/blob/master/sublime_unicode_nbsp.py
-  '\x82', # High code comma
-  '\x84', # High code double comma
-  '\x85', # Tripple dot
-  '\x88', # High carat
-  '\x91', # Forward single quote
-  '\x92', # Reverse single quote
-  '\x93', # Forward double quote
-  '\x94', # Reverse double quote
-  '\x95', # <control> Message Waiting
-  '\x96', # High hyphen
-  '\x97', # Double hyphen
-  '\x99', # <control>
+  '\x82', # Break Permitted Here [high code comma]
+  '\x84', # Control 0084 [high code double comma]
+  '\x85', # Next Line (Nel) [tripple dot]
+  '\x88', # Character Tabulation Set [high caret]
+  '\x91', # Private Use One [forward single quote]
+  '\x92', # Private Use Two [reverse single quote]
+  '\x93', # Set Transmit State [forward double quote]
+  '\x94', # Cancel Character [reverse double quote]
+  '\x95', # Message Waiting [middle dot]
+  '\x96', # Start Of Guarded Area [high hyphen]
+  '\x97', # End Of Guarded Area [double hyphen]
+  '\x99', # Control 0099 [TM]
   '\xA0', # No-break space
-  '\xA6', # Split vertical bar
-  '\xAB', # Double less than
-  '\xBB', # Double greater than
-  '\xBC', # one quarter
-  '\xBD', # one half
-  '\xBE', # three quarters
-  '\xBF', # c-single quote
-  '\xA8', # modifier - under curve
-  '\xB1', # modifier - under line
+  '\xBC', # Vulgar Fraction One Quarter
+  '\xBD', # Vulgar Fraction One Half
+  '\xBE', # Vulgar Fraction Three Quarters
+  # '\xA6', # Broken Bar [split vertical bar]
+  # '\xAB', # Left-Pointing Double Angle Quotation Mark
+  # '\xBB', # Right-Pointing Double Angle Quotation Mark
+  # '\xBF', # Inverted Question Mark
+  # '\xA8', # Diaeresis [high double dot]
+  # '\xB1', # Plus-minus Sign

   # https://www.cs.tut.fi/~jkorpela/chars/spaces.html
   #'\u00A0', # no-break space
@@ -66,6 +66,8 @@
   '\u2223',  # divides
   '\u0003',  # Start of text
   '\u201A',  # Low quotation mark - looks like comma
+  '\u02C2',  # modifier letter left arrowhead (< doppelganger)
+  '\u02C3',  # modifier letter right arrowhead (> doppelganger)
 ]

 charRegExp = '[' + chars.join('') + ']'


Implements a first batch version of the editor rendering

--- lib/minimap-editor-view.coffee
+++ lib/minimap-editor-view.coffee
@@ -3,7 +3,7 @@
 Debug = require 'prolix'

 module.exports =
-class MinimapEditorView extends ScrollView
+class MinimapPaneView extends ScrollView
   Emitter.includeInto(this)
   Debug('minimap').includeInto(this)

@@ -44,17 +44,11 @@

   update: =>
     return unless @editorView?
-    return if @frameRequested

-    @frameRequested = true
-    webkitRequestAnimationFrame =>
-      @frameRequested = false
-      if @bufferChanges.length > 0
-        @updateMinimapWithBufferChanges()
-      else
-        @rebuildMinimap()
-
-      @emit 'minimap:updated'
+    if @bufferChanges.length > 0
+      @updateMinimapWithBufferChanges()
+    else
+      @rebuildMinimap()

   updateMinimapWithBufferChanges: ->
     @startBench()
@@ -70,10 +64,12 @@
         @deleteRowsAtRange(oldScreenRange)
         @createRowsAtRange(newScreenRange)
         @markIntermediateTime("update buffer change")
+
       catch e
         continue

     @endBench('complete update')
+    @emit 'minimap:updated'

   deleteRowsAtRange: (range) ->
     linesWrapper = @lines[0].childNodes[0]
@@ -98,8 +94,11 @@

   rebuildMinimap: ->
+    return if @rebuilding
+
     @startBench()

+    @rebuilding = true
     lines = @lines[0]
     if lines?
       child = lines.childNodes[0]
@@ -108,18 +107,28 @@
     @lines.css fontSize: "#{@editorView.getFontSize()}px"

     @markIntermediateTime('cleaning')
-    # FIXME: If the file is very large, the tokenizes doesn't generate
-    # completely, so doesn't have the syntax highlight until a new view
-    # is activated in the same pane.
+
     numLines = @editorView.getModel().displayBuffer.getLines().length
-    lines = @editorView.buildLineElementsForScreenRows(0, numLines)

-    @markIntermediateTime('lines building')
     wrapper = $('<div/>')
-    wrapper.append lines
     @lines.append wrapper

-    @endBench('minimap update')
+    batchSize = 100
+    batch = (start=0) =>
+      end = Math.min(start + batchSize, numLines)
+      @log start, end
+      lines = @editorView.buildLineElementsForScreenRows(start, end)
+
+      @markIntermediateTime('batch finished')
+      wrapper.append lines
+      if end is numLines
+        @rebuilding = false
+        @endBench('minimap update')
+        @emit 'minimap:updated'
+      else
+        webkitRequestAnimationFrame -> batch(end + 1)
+
+    batch()

   getClientRect: ->
     sv = @scrollView[0]


Always show view code button

Always show view code button

If atom is meant to be a hackable editor why would you hide the button to edit the source code?
There's a bug report by someone else here https://github.com/atom/settings-view/issues/617.

--- lib/package-detail-view.coffee
+++ lib/package-detail-view.coffee
@@ -152,9 +152,6 @@
       @startupTime.html("This #{@type} added #{@getStartupTime()}ms to startup time.")
     else
       @startupTime.hide()
-      @openButton.hide()
-
-    @openButton.hide() if atom.packages.isBundledPackage(@pack.name)

     @renderReadme()


Be defensive with atom.project.

Be defensive with atom.project.

The tests might not have a project

--- lib/project/result-view.coffee
+++ lib/project/result-view.coffee
@@ -14,7 +14,7 @@
       @div outlet: 'pathDetails', class: 'path-details list-item', =>
         @span class: 'disclosure-arrow'
         @span class: iconClass + ' icon', 'data-name': fileBasename
-        @span class: 'path-name bright', filePath.replace(atom.project.getPaths()[0] + path.sep, '')
+        @span class: 'path-name bright', filePath.replace(atom.project?.getPaths()[0] + path.sep, '')
         @span outlet: 'description', class: 'path-match-number'
       @ul outlet: 'matches', class: 'matches list-tree'


Fix some misnomer vars

Fix some misnomer vars

--- library/membrane/visual.coffee
+++ library/membrane/visual.coffee
@@ -203,10 +203,10 @@
     # creates a proper empty context that conforms to the necessary
     # restrictions. Then it runs the internal machinery to fill the
     # context with the service and request related data and events.
-    assembleContext: (symbol, request, asm, baked, receive) ->
+    assembleContext: (symbol, request, asm, stock, receive) ->
         noPrelude = "no prelude method detected"
         assert _.isFunction(@prelude), noPrelude
-        assert _.isObject context = baked or Object()
+        assert _.isObject context = stock or {}
         append = -> _.extend context, arguments...
         append styles: [], sheets: [], changes: []
         append externals: [], invokes: [], sources: []


fix can't set header error

fix can't set header error

--- api/src/models/daily-report.coffee
+++ api/src/models/daily-report.coffee
@@ -19,6 +19,8 @@
       next()
     .catch next

+    return
+
   DailyReport.getUserDailyReport = (req, next) ->
     DailyReport.app.models.ScrumbleUser.findById(req.accessToken.userId)
     .then (user) ->


object merge now copies static d3plus items

--- src/object/merge.coffee
+++ src/object/merge.coffee
@@ -11,12 +11,12 @@
 ###
 module.exports = (obj1, obj2) ->

-  copyObject = (obj, ret) ->
+  copyObject = (obj, ret, plus) ->
     for k, v of obj
-      unless typeof v is "undefined" or k.indexOf("d3plus") is 0
-        if validate v
+      unless typeof v is "undefined"
+        if !plus and validate v
           ret[k] = {} if typeof ret[k] isnt "object"
-          copyObject v, ret[k]
+          copyObject v, ret[k], k is "d3plus"
         else if not d3selection(v) and v instanceof Array
           ret[k] = v.slice 0
         else


Change to the uploader to make it easier to work with

--- lib/written/app/assets/javascripts/written/uploaders/aws.coffee
+++ lib/written/app/assets/javascripts/written/uploaders/aws.coffee
@@ -43,7 +43,7 @@
   process: (file, node) =>
     caption = node.querySelector('figcaption')
     progress = node.querySelector('div.progress')
-    filename = @hash(file.name)
+    filename = [Images.settings.namespace, @hash(file.name)].join('/')

     image = Images.get(filename)
     if !image


Fixing IE DontEnum bug in Scope. Ignoring everywhere else.

--- scope.coffee
+++ scope.coffee
@@ -44,7 +44,7 @@

   # Test variables and return true the first time fn(v, k) returns true
   any: (fn) ->
-    for v, k of @variables when fn(v, k)
+    for v in @allVariables() when fn(v[0], v[1])
       return true
     return false

@@ -80,6 +80,15 @@
   # (or at the top-level scope, if requested).
   assign: (name, value) ->
     @variables[name] = value: value, assigned: true
+
+  # Gets around a bug in IE where it won't enumerate of redefined prototype properties.
+  allVariables: ->
+    vars = [v, k] for v, k of @variables
+    if v.__defineGetter__ then vars
+    else vars.concat(for v in   '''toString toLocaleString
+        hasOwnProperty valueOf constructor propertyIsEnumerable
+        isPrototypeOf'''.split(' ') when @variables.hasOwnProperty v
+      [v, @variables[v]])

   # Does this scope reference any variables that need to be declared in the
   # given function body?
@@ -93,12 +102,12 @@

   # Return the list of variables first declared in this scope.
   declaredVariables: ->
-    (key for key, val of @variables when val in ['var', 'reuse']).sort()
+    (v[0] for v in @allVariables() when v[1] in ['var', 'reuse']).sort()

   # Return the list of assignments that are supposed to be made at the top
   # of this scope.
   assignedVariables: ->
-    "#{key} = #{val.value}" for key, val of @variables when val.assigned
+    "#{v[0]} = #{v[1].value}" for v in @allVariables() when v[1].assigned

   # Compile the JavaScript for all of the variable declarations in this scope.
   compiledDeclarations: ->


KodingProvider: add fetchUsage implementation which returns following:

{ total: total number of machines from koding provider
  alwaysOn: total number of alwaysOn machines
  storage: total number of (in GB) storage used in machines }

--- workers/social/lib/social/models/computeproviders/koding.coffee
+++ workers/social/lib/social/models/computeproviders/koding.coffee
@@ -1,7 +1,6 @@

 ProviderInterface = require './providerinterface'
 KodingError       = require '../../error'
-JVM               = require '../vm'

 {argv}            = require 'optimist'
 KONFIG            = require('koding-config-manager').load("main.#{argv.c}")
@@ -27,6 +26,31 @@
     callback null, { meta, credential: client.r.user.username }

+  @fetchUsage = (client, options, callback)->
+
+    JMachine  = require './machine'
+
+    { r: { group, user } } = client
+
+    selector        = { provider: "koding" }
+    selector.users  = $elemMatch: id: user.getId()
+    selector.groups = $elemMatch: id: group.getId()
+
+    JMachine.some selector, limit: 30, (err, machines)->
+
+      return callback err  if err?
+
+      total    = machines.length
+      alwaysOn = 0
+      storage  = 0
+
+      machines.forEach (machine)->
+        alwaysOn++  if machine.meta.alwaysOn
+        storage += machine.meta.storage_size ? 3
+
+      callback null, { total, alwaysOn, storage }
+
+
   @fetchAvailable = (client, options, callback)->

     callback null, [


Fix processing regexp in outline

Fix processing regexp in outline

--- src/qeda-element.coffee
+++ src/qeda-element.coffee
@@ -266,7 +266,7 @@
         unless @housing[k]? then @housing[k] = v
       else
         unless sk? then return
-        re = new RegExp k
+        re = new RegExp '^' + k + '$'
         if re.test(sk) then @_copyFromOutline outline[k], subkeys

   #


Clearing instances when switching site

Clearing instances when switching site

--- app/assets/javascripts/home.coffee
+++ app/assets/javascripts/home.coffee
@@ -91,6 +91,7 @@
         'selected_site',
         (newSite, oldSite) ->
           return if !newSite
+          $scope.instances = []
           $http.get('/sites/' + newSite + '.json').then(
             (response) -> $scope.site_data = response.data.site,
             (response) ->


moved coordinateAxes more to center

moved coordinateAxes more to center

--- app/assets/javascripts/view.coffee
+++ app/assets/javascripts/view.coffee
@@ -94,7 +94,7 @@

 			# rotate the axis mini-map according to the cube's rotation and translate it
 			rotMatrix = cam.getMatrix()
-			rotMatrix[12] = -150 #axisMinimap[0]
+			rotMatrix[12] = -100 #axisMinimap[0]
 			rotMatrix[13] = 0 #axisMinimap[1]
 			rotMatrix[14] = -100


Use RootView::openSync in specs

Use RootView::openSync in specs

--- spec/snippets-spec.coffee
+++ spec/snippets-spec.coffee
@@ -7,7 +7,7 @@
   beforeEach ->
     atom.activatePackage('javascript-tmbundle', sync: true)
     window.rootView = new RootView
-    rootView.open('sample.js')
+    rootView.openSync('sample.js')

     packageWithSnippets = atom.loadPackage("package-with-snippets")


Convert appCtrl indentation to spaces

Convert appCtrl indentation to spaces

--- app/scripts/controllers/app.coffee
+++ app/scripts/controllers/app.coffee
@@ -1,7 +1,7 @@
 'use strict'

 angular.module('portfolioApp')
-.controller 'AppCtrl', ['$scope', 'linkedIn', ($scope, linkedIn) ->
-	$scope.initLinkedIn = ->
-		linkedIn.log()
-]+  .controller 'AppCtrl', ['$scope', 'linkedIn', ($scope, linkedIn) ->
+    $scope.initLinkedIn = ->
+      linkedIn.log()
+  ]


remove section from test

remove section from test

--- api/apps/articles/test/model.coffee
+++ api/apps/articles/test/model.coffee
@@ -347,6 +347,7 @@

     it 'saves an article to a gravity post', (done) ->
       article = _.extend fixtures().articles, gravity_id: null
+      article.sections[0] = { type: 'text', body: '' }
       article.sections[3].ids = ['foo', 'bar']
       Article.syncToPost article, 'foo-token', (err, post) ->
         post.title.should.equal article.title


Favor complete over incomplete sessions in CampaignView flag status

--- app/views/play/CampaignView.coffee
+++ app/views/play/CampaignView.coffee
@@ -427,7 +427,8 @@
   onSessionsLoaded: (e) ->
     return if @editorMode
     for session in @sessions.models
-      @levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started'
+      unless @levelStatusMap[session.get('levelID')] is 'complete'  # Don't overwrite a complete session with an incomplete one
+        @levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started'
       @levelDifficultyMap[session.get('levelID')] = session.get('state').difficulty if session.get('state')?.difficulty
     @render()
     @loadUserPollsRecord() unless me.get 'anonymous'


Clean up

Clean up

--- app/assets/javascripts/components/moving_records/new_moving_record_form.js.coffee
+++ app/assets/javascripts/components/moving_records/new_moving_record_form.js.coffee
@@ -7,6 +7,28 @@
     room:        ""
     category:    ""
     description: ""
+
+  componentDidMount: ->
+    @updateAutocomplete()
+
+  componentDidUpdate: ->
+    @updateAutocomplete()
+
+  componentWillUnmount: ->
+    # get rid of the extra HTML that jQuery UI autocomplete creates.
+    $(React.findDOMNode(@refs.room)).autocomplete('destroy')
+    $(React.findDOMNode(@refs.category)).autocomplete('destroy')
+
+  updateAutocomplete: ->
+    $(React.findDOMNode(@refs.room)).autocomplete
+      source: @props.roomSuggestions
+      select: (e, ui) =>
+        @setState room: ui.item.value
+
+    $(React.findDOMNode(@refs.category)).autocomplete
+      source: @props.categorySuggestions
+      select: (e, ui) =>
+        @setState category: ui.item.value

   handleChange: (e) ->
     name = e.target.name
@@ -37,14 +59,10 @@
   handleClear: (e) ->
     @setState @getInitialState()  # Restore component's initial UI.

-  capitalize: (string) ->
-    string.charAt(0).toUpperCase() + string.slice(1)
-
+  # field validations
   valid: ->
     @validName() && @validVolume() && @validQuantity() &&
     @validRoom() && @validCategory() && @validDescription()
-
-  # validators for individual fields
   validName: ->
     @state.name && @state.name.length <= 50
   validVolume: ->
@@ -57,39 +75,11 @@
     @state.category && @state.category.length <= 50
   validDescription: ->
     @state.description.length <= 200
-
   fieldColor: (validator) ->
     if validator then 'has-success' else 'has-warning'

-  componentDidMount: ->
-    @updateAutocomplete()
-
-  componentDidUpdate: ->
-    @updateAutocomplete()
-
-  componentWillUnmount: ->
-    # get rid of the extra HTML that jQuery UI autocomplete creates.
-    $(React.findDOMNode(@refs.room)).autocomplete('destroy')
-    $(React.findDOMNode(@refs.category)).autocomplete('destroy')
-
-  updateAutocomplete: ->
-    $(React.findDOMNode(@refs.room)).autocomplete
-      source: @props.roomSuggestions
-      select: (e, ui) =>
-        @setState room: ui.item.value
-        # debug
-        # $.growl.notice title: "selected", message: ""
-        # console.log document.activeElement.name
-        # console.log ui.item.value
-
-    $(React.findDOMNode(@refs.category)).autocomplete
-      source: @props.categorySuggestions
-      select: (e, ui) =>
-        @setState category: ui.item.value
-        # debug
-        # $.growl.notice title: "selected", message: ""
-        # console.log document.activeElement.name
-        # console.log ui.item.value
+  capitalize: (string) ->
+    string.charAt(0).toUpperCase() + string.slice(1)

   render: ->
     R = React.DOM
@@ -170,7 +160,6 @@
           R.div
             className: 'form-group col-sm-4'
             R.button
-              type:      'submit'
               className: "btn btn-default btn-block"
               onClick: @handleClear
               'Clear'


simplified the download button component

--- app/assets/javascripts/species/views/documents/batch_download_component.js.coffee
+++ app/assets/javascripts/species/views/documents/batch_download_component.js.coffee
@@ -1,13 +1,13 @@
 Species.BatchDownloadComponent = Ember.Component.extend
-  tagName: 'i'
-  classNames: ['fa', 'fa-download']
+  tagName: 'a'
+  template: Ember.Handlebars.compile('<i class="fa fa-download"></i>')

   click: (event) ->
-    button = event.target
-    checked = $(button).closest('.inner-table-container').
-      find('.table-body tbody tr input:checked')
-    document_ids = []
-    checked.each( ->
-      document_ids.push($(this).closest('tr').data('document-id'))
+    checked = $(event.target).closest('.inner-table-container').
+      find('td.download-col input:checked')
+    documentIds = checked.map( ->
+      $(@).closest('tr').data('document-id')
+    ).toArray()
+    @.$().prop('href', '/api/v1/documents/download_zip?ids=' +
+      documentIds.join()
     )
-    $(button).closest('a').prop('href', '/api/v1/documents/download_zip?ids='+document_ids.join())


client: add text-center class to Leave Shared Vm text

--- client/app/lib/components/sidebarmachineslistitem/leavesharedmachinewidget.coffee
+++ client/app/lib/components/sidebarmachineslistitem/leavesharedmachinewidget.coffee
@@ -16,7 +16,7 @@

     modal = new ContentModal
       title   : 'Are you sure?'
-      content : "
This will remove the shared VM from your sidebar. You won't be able to access it unless you are invited again.
"
+      content : "
This will remove the shared VM from your sidebar. You won't be able to access it unless you are invited again.
"
       cssClass : 'content-modal'
       buttons :
         No         :


Renable css color highlighting for Atom

Renable css color highlighting for Atom

--- apps/atom/atom.symlink/config.cson
+++ apps/atom/atom.symlink/config.cson
@@ -16,7 +16,6 @@
   'projectHome': '/Users/MoOx/Sync/Development'
   'disabledPackages': [
     'sublime-tabs'
-    'css-color-highlighting'
   ]
   'themes': [
     'unity-dark-ui'


Fix context menu not working in devtools

--- atom/renderer/lib/inspector.coffee
+++ atom/renderer/lib/inspector.coffee
@@ -27,7 +27,9 @@
           label: item.label
           enabled: item.enabled
       if item.id?
-        transformed.click = -> DevToolsAPI.contextMenuItemSelected item.id
+        transformed.click = ->
+          DevToolsAPI.contextMenuItemSelected item.id
+          DevToolsAPI.contextMenuCleared()
       template.push transformed
   template

@@ -37,9 +39,7 @@

   menu = Menu.buildFromTemplate convertToMenuTemplate(items)
   # The menu is expected to show asynchronously.
-  setImmediate ->
-    menu.popup remote.getCurrentWindow()
-    DevToolsAPI.contextMenuCleared()
+  setTimeout -> menu.popup remote.getCurrentWindow()

 showFileChooserDialog = (callback) ->
   {remote} = require 'electron'


update: 大会回毎に利用するjavascriptを変更

update: 大会回毎に利用するjavascriptを変更

処理の効率化とソースの整理を兼ねて試合詳細などでのUI制御を大会回(contest_nth)にロードさせる。将来はロードされる部分を大会回毎のファイルに分割したい。

--- app/assets/javascripts/games.coffee
+++ app/assets/javascripts/games.coffee
@@ -30,24 +30,26 @@
     replay_labels()

   # when judge/progress cheched/unchecked
-  $('form').on 'change', (event) ->
-    if /^game_game_detail29ths_attributes_\d+_judge$/.test(event.target.id)
-      i = event.target.id.match(/_\d+_/)[0].replace(/_/g, '')
-      if $('#game_game_detail29ths_attributes_' + i + '_judge')[0].checked
-        $('.judge_score_' + i).toggleClass('hidden')
-      else
-        $('.judge_score_' + i).toggleClass('hidden')
-        $('#game_game_detail29ths_attributes_' + i +
-          '_judge_to_me').val('')
-        $('#game_game_detail29ths_attributes_' + i +
-          '_judge_to_opponent').val('')
-    if /^game_game_detail29ths_attributes_\d+_progress$/.test(event.target.id)
-      i = event.target.id.match(/_\d+_/)[0].replace(/_/g, '')
-      if $('#game_game_detail29ths_attributes_' + i + '_progress')[0].checked
-        $('.progress_' + i).toggleClass('hidden')
-      else
-        $('.progress_' + i).toggleClass('hidden')
-        $('#game_game_detail29ths_attributes_' + i +
-          '_my_progress').val('')
-        $('#game_game_detail29ths_attributes_' + i +
-          '_opponent_progress').val('')
+  switch gon.contest_nth
+    when 29
+      $('form').on 'change', (event) ->
+        if /^game_game_detail29ths_attributes_\d+_judge$/.test(event.target.id)
+          i = event.target.id.match(/_\d+_/)[0].replace(/_/g, '')
+          if $('#game_game_detail29ths_attributes_' + i + '_judge')[0].checked
+            $('.judge_score_' + i).toggleClass('hidden')
+          else
+            $('.judge_score_' + i).toggleClass('hidden')
+            $('#game_game_detail29ths_attributes_' + i +
+              '_judge_to_me').val('')
+            $('#game_game_detail29ths_attributes_' + i +
+              '_judge_to_opponent').val('')
+        if /^game_game_detail29ths_attributes_\d+_progress$/.test(event.target.id)
+          i = event.target.id.match(/_\d+_/)[0].replace(/_/g, '')
+          if $('#game_game_detail29ths_attributes_' + i + '_progress')[0].checked
+            $('.progress_' + i).toggleClass('hidden')
+          else
+            $('.progress_' + i).toggleClass('hidden')
+            $('#game_game_detail29ths_attributes_' + i +
+              '_my_progress').val('')
+            $('#game_game_detail29ths_attributes_' + i +
+              '_opponent_progress').val('')


Minor cosmetic changes on common directives.

--- app/coffee/directives/common.coffee
+++ app/coffee/directives/common.coffee
@@ -19,8 +19,7 @@

 GmBreadcrumbDirective = ($rootScope) ->
-    return (scope, element, attrs) ->
-
+    link: (scope, element, attrs) ->
         scope.$watch "pageBreadcrumb", (breadcrumb) ->
             if breadcrumb is undefined
                 return
@@ -29,7 +28,6 @@
             element.empty()

             items = []
-
             for item, index in breadcrumb
                 if item[1] == null
                     li = angular.element('

').text(item[0])
@@ -431,7 +429,8 @@
                     .replace('e','6')
                     .replace('f','7')

-                $(search_choice).css('background', "##{color}")
+                angular.element(search_choice)
+                       .css('background', "##{color}")

         element.on "change", (e) ->
             colorizeTags()


Handle empty responses in POST requests

Handle empty responses in POST requests

--- app/coffee/modules/base/http.coffee
+++ app/coffee/modules/base/http.coffee
@@ -61,6 +61,7 @@

         options.data = data if data
         options.params = params if params
+        options.responseType = 'text'

         return @.request(options)


change date type in query

change date type in query

--- app/scripts/controllers/main.coffee
+++ app/scripts/controllers/main.coffee
@@ -15,7 +15,7 @@
           month : month
           year : year
         # Loads data from API
-        filter = '?where={"ref_dates.date":"' + year + '","note":2.0}'
+        filter = '?where={"ref_dates.date":' + year + ',"note":2.0}'
         (do (Restangular.all "articles#{filter}").getList).then (data) =>
           $scope.days = []
           _.map data._items, (article) =>


End the request later

End the request later

--- scripts/github-pull-request-notifier.coffee
+++ scripts/github-pull-request-notifier.coffee
@@ -30,8 +30,6 @@
   robot.router.post "/hubot/gh-pull-requests", (req, res) ->
     query = querystring.parse(url.parse(req.url).query)

-    res.end
-
     data = req.body
     room = query.room
     console.log("going to push to room #{room}")
@@ -42,6 +40,8 @@
         robot.messageRoom user.room, what
     catch error
       console.log "github pull request notifier error: #{error}. Request: #{req.body}"
+
+    res.end ""

 announcePullRequest = (data, cb) ->


Attributing stories to Kanye West. This feature probably shouldn't be here

--- src/scripts/bark.coffee
+++ src/scripts/bark.coffee
@@ -21,7 +21,7 @@
   robot.respond /tell me a story/i, (res) ->
     https.get('https://yepi.io/api/quote', (r) ->
       r.on "data", (chunk) ->
-        res.send((chunk + '').trim())
+        res.send("\"#{(chunk + '').trim()}\" -Kanye West")
     )
     .on 'error', (err) ->
       Logger.error err


Default to using JIRA API v2

Default to using JIRA API v2

Avoids https://github.com/github/hubot-scripts/issues/1088 for the majority of new users (who should be on 5.0 or higher, seeing as it's been out for almost 2 years and Atlassian is now gearing up to release 6.1).

--- src/scripts/jira.coffee
+++ src/scripts/jira.coffee
@@ -9,7 +9,7 @@
 #   HUBOT_JIRA_USER
 #   HUBOT_JIRA_PASSWORD
 #   Optional environment variables:
-#   HUBOT_JIRA_USE_V2
+#   HUBOT_JIRA_USE_V2 (defaults to "true", set to "false" for JIRA earlier than 5.0)
 #   HUBOT_JIRA_MAXLIST
 #   HUBOT_JIRA_ISSUEDELAY
 #   HUBOT_JIRA_IGNOREUSERS
@@ -90,7 +90,7 @@
 module.exports = (robot) ->
   filters = new IssueFilters robot

-  useV2 = process.env.HUBOT_JIRA_USE_V2 || false
+  useV2 = process.env.HUBOT_JIRA_USE_V2 != "false"
   # max number of issues to list during a search
   maxlist = process.env.HUBOT_JIRA_MAXLIST || 10
   # how long (seconds) to wait between repeating the same JIRA issue link


Reuse generic methods of saving selection+scroll

--- js-library/app/js/jail_iframe/util/highlight.coffee
+++ js-library/app/js/jail_iframe/util/highlight.coffee
@@ -52,20 +52,19 @@

   results

+
+searchAlgorithm =
+  if window.find
+    searchWithWindowFind
+  else
+    searchWithFindText
+
 # Function to search the page
 search = (searchString) ->
-  return false unless window.find?
+  ensureScrollSaved ->
+    ensureSelectionSaved ->
+      searchAlgorithm(searchString.trim())

-  # Store scroll settings to reset to afterwards
-  scrollTop = window.document.body.scrollTop
-  scrollLeft = window.document.body.scrollLeft
-
-  results = searchWithWindowFind(searchString)
-
-  # Scroll back to previous location
-  window.scroll scrollLeft, scrollTop
-
-  results

 # Adapted from https://github.com/okfn/annotator/blob/master/src/annotator.coffee
 highlightNode = (node, id) ->


:shirt: Fixed linter error

:shirt: Fixed linter error

--- spec/tool-bar-spec.coffee
+++ spec/tool-bar-spec.coffee
@@ -82,7 +82,7 @@
         # jasmine.attachToDOM(toolBar)
         toolBar.firstChild.click()
         expect(spy).toHaveBeenCalled()
-        expect(spy.mostRecentCall.args[0]).toEqual('foo');
+        expect(spy.mostRecentCall.args[0]).toEqual('foo')
       it 'and restores focus after click', ->
         toolBarAPI.addButton
           icon: 'octoface'


atom.packages.activatePackage now returns a promise

--- spec/vim-mode-spec.coffee
+++ spec/vim-mode-spec.coffee
@@ -1,18 +1,21 @@
 {WorkspaceView} = require 'atom'

 describe "VimMode", ->
-  [editor] = []
+  [editorView] = []

   beforeEach ->
     atom.workspaceView = new WorkspaceView
     atom.workspaceView.openSync()
     atom.workspaceView.simulateDomAttachment()
-    atom.packages.activatePackage('vim-mode', immediate: true)

-    editor = atom.workspaceView.getActiveView()
-    editor.enableKeymap()
+    waitsForPromise ->
+      atom.packages.activatePackage('vim-mode')
+
+    runs ->
+      editorView = atom.workspaceView.getActiveView()
+      editorView.enableKeymap()

   describe "initialize", ->
     it "puts the editor in command-mode initially", ->
-      expect(editor).toHaveClass 'vim-mode'
-      expect(editor).toHaveClass 'command-mode'
+      expect(editorView).toHaveClass 'vim-mode'
+      expect(editorView).toHaveClass 'command-mode'


track artist follow from search artist component on homepage

--- apps/home/components/followed_artists/search_artists_view.coffee
+++ apps/home/components/followed_artists/search_artists_view.coffee
@@ -3,6 +3,7 @@
 { API_URL } = require('sharify').data
 Q = require 'bluebird-q'
 Backbone = require 'backbone'
+analyticsHooks = require '../../../../lib/analytics_hooks.coffee'
 Artist = require '../../../../models/artist.coffee'
 CurrentUser = require '../../../../models/current_user.coffee'
 Match = require '../../../../collections/match.coffee'
@@ -84,6 +85,11 @@
     id = $suggestion.data('artist-id')
     @following.follow(id) if @user
     $suggestion.find('.typeahead-suggestion-follow').attr 'data-state', 'following'
+    analyticsHooks.trigger 'followable:followed',  {
+      modelName: @match.kind
+      entity_slug: id
+      context_module: 'Homepage followed artists'
+    }
     $suggestion.addClass 'tt-suggestion-inner__selected'
     @$('input').select()
     @findReplacementAndWait(id).then ([ artists ]) =>


account: count has 2 signatures

account: count has 2 signatures

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -77,7 +77,10 @@
           (signature Object, Object, Object, Function)
         getAutoCompleteData:
           (signature String, String, Function)
-        count: (signature Object, Function)
+        count: [
+          (signature Function)
+          (signature Object, Function)
+        ]
         byRelevance: [
           (signature String, Function)
           (signature String, Object, Function)


Fix typo

Fix typo

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -197,7 +197,7 @@
   glanceMessages: secure (client, callback)->

   glanceActivities: secure (client, activityId, callback)->
-    (callback, activityId) = (activityId, callback) unless callback
+    [callback, activityId] = [activityId, callback] unless callback
     {delegate} = client.connection
     unless @equals delegate
       callback new KodingError 'Access denied.'


worker: add 'kodinglearn' to impersonate on nitin's request

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -726,7 +726,7 @@

   dummyAdmins = [ "sinan", "devrim", "gokmen", "fatihacet", "arslan",
                   "sent-hil", "cihangirsavas", "leeolayvar", "stefanbc",
-                  "szkl", "canthefason", "nitin", "usirin"]
+                  "szkl", "canthefason", "nitin", "usirin", "kodinglearn" ]

   userIsExempt: (callback)->
     # console.log @isExempt, this


fixed code style on servers.lib.server.facebook_callback.coffee

--- servers/lib/server/facebook_callback.coffee
+++ servers/lib/server/facebook_callback.coffee
@@ -1,56 +1,56 @@
 {
   redirectOauth
   saveOauthToSession
-}          = require './helpers'
-{facebook} = KONFIG
-http       = require "https"
-{decode}   = require "querystring"
-provider   = "facebook"
+}            = require './helpers'
+{ facebook } = KONFIG
+http         = require 'https'
+{ decode }   = require 'querystring'
+provider     = 'facebook'

 module.exports = (req, res) ->
   access_token = null
-  {clientId}   = req.cookies
-  {code}       = req.query
+  { clientId } = req.cookies
+  { code }     = req.query

   unless code
-    redirectOauth "No code", req, res, {provider}
+    redirectOauth 'No code', req, res, { provider }
     return

-  url  = "https://graph.facebook.com/oauth/access_token?"
+  url  = 'https://graph.facebook.com/oauth/access_token?'
   url += "client_id=#{facebook.clientId}&"
   url += "redirect_uri=#{facebook.redirectUri}&"
   url += "client_secret=#{facebook.clientSecret}&"
   url += "code=#{code}&"
-  url += "scope=email"
+  url += 'scope=email'

   # Get access token with code
-  http.get url, (httpResp)->
-    rawResp = ""
-    httpResp.on "data", (chunk) -> rawResp += chunk
-    httpResp.on "end", ->
+  http.get url, (httpResp) ->
+    rawResp = ''
+    httpResp.on 'data', (chunk) -> rawResp += chunk
+    httpResp.on 'end', ->
       access_token = decode(rawResp).access_token
       if access_token
         options =
-          host    : "graph.facebook.com"
+          host    : 'graph.facebook.com'
           path    : "/me?access_token=#{access_token}"
-          method  : "GET"
+          method  : 'GET'
         r = http.request options, fetchUserInfo
         r.end()
       else
-        console.log "facebook err, no access token", rawResp
-        redirectOauth "No access token", req, res, {provider}
+        console.log 'facebook err, no access token', rawResp
+        redirectOauth 'No access token', req, res, { provider }

   # Get user info with access token
   fetchUserInfo = (userInfoResp) ->
-    rawResp = ""
-    userInfoResp.on "data", (chunk) -> rawResp += chunk
-    userInfoResp.on "end", ->
+    rawResp = ''
+    userInfoResp.on 'data', (chunk) -> rawResp += chunk
+    userInfoResp.on 'end', ->
       userInfo = JSON.parse rawResp

       [firstName, restOfNames...] = userInfo.name.split ' '
       lastName = restOfNames.join ' '

-      {username, email} = userInfo
+      { username, email } = userInfo
       facebookResp = {
         username
         email
@@ -60,10 +60,10 @@
         foreignId : userInfo.id
       }

-      saveOauthToSession facebookResp, clientId, provider, (err)->
+      saveOauthToSession facebookResp, clientId, provider, (err) ->
         if err
-          console.log "facebook err, saving to session", err
-          redirectOauth err, req, res, {provider}
+          console.log 'facebook err, saving to session', err
+          redirectOauth err, req, res, { provider }
           return

-        redirectOauth null, req, res, {provider}
+        redirectOauth null, req, res, { provider }


Fix boolean values in array.

Fix boolean values in array.

--- app/assets/javascripts/try_api/application.js.coffee
+++ app/assets/javascripts/try_api/application.js.coffee
@@ -166,14 +166,17 @@
                   .success method.response_handler
                   .error method.response_handler

-    $scope.addParameterToForm = (form, parameter) ->
+    $scope.addParameterToForm = (form, parameter) -> # TODO implement multidimentional parameters
       if parameter.type == 'array'
-        console.log(parameter.values)
         $.each parameter.values, ->
           value = this
           $.each value, ->
             subparameter = this
-            form.append parameter.name + '[]' + subparameter.name, subparameter.value || ''
+            switch subparameter.type
+              when 'boolean'
+                form.append parameter.name + '[]' + subparameter.name, subparameter.value || false
+              else
+                form.append parameter.name + '[]' + subparameter.name, subparameter.value || ''
       else
         form.append parameter.name, parameter.value || ''
 ]


fixed dataset tests

fixed dataset tests


fix ssh integration test

fix ssh integration test

--- test/integration/ssh_dataset.coffee
+++ test/integration/ssh_dataset.coffee
@@ -21,6 +21,9 @@
       , 500

     context 'when I click on the importer tool\'s SSH in button', (done) ->
+      before (done) ->
+        setTimeout done, 500
+
       before clickSSHButton

       it 'a modal window appears', (done) =>


change SMC_DB_HOSTS default to localhost

--- src/c.coffee
+++ src/c.coffee
@@ -9,7 +9,7 @@

 async = require('async')

-db_hosts = process.env.SMC_DB_HOSTS?.split(',') ? ['db0']
+db_hosts = process.env.SMC_DB_HOSTS?.split(',') ? ['localhost']

 global.misc = require('smc-util/misc')
 global.done = misc.done


:bug: Fix keybindings for composed text editor

--- keymaps/table-edit.cson
+++ keymaps/table-edit.cson
@@ -43,7 +43,7 @@
   'ctrl-alt-backspace up': 'table-edit:delete-column'
   'ctrl-alt-backspace down': 'table-edit:delete-column'

-'atom-table-editor atom-text-editor':
+'atom-table-editor atom-text-editor:not([mini])':
   'enter': 'core:confirm'
   'ctrl-enter': 'editor:newline'
   'tab': 'table-edit:move-right'


updateCoverPhoto button added to cover image section

--- client/Social/Activity/views/feedcoverphotoview.coffee
+++ client/Social/Activity/views/feedcoverphotoview.coffee
@@ -3,35 +3,53 @@
     options.cssClass = "group-cover-view"
     super options, data

-    @addSubView @getCoverView()
+    group = KD.singletons.groupsController.getCurrentGroup()
+
+    if group.slug isnt "koding"
+      container = new KDView
+        cssClass : "container"
+        size     :
+          height : 315
+          width  : 914
+
+      container.addSubView @getCoverUpdateButton()
+      container.addSubView @getCoverView()
+    else
+      container = new KDCustomHTMLView
+
+    @addSubView container
+
+  getCoverUpdateButton: ->
+    admin = role for role in KD.config.roles when role is "admin"
+    if admin
+      new KDButtonView
+        style  : "solid green small account-header-button"
+        type   : "submit"
+        title  : "update cover photo"
+    else
+      new KDCustomHTMLView

   getCoverView: ->
     group = KD.singletons.groupsController.getCurrentGroup()
-    return new KDCustomHTMLView if group.slug is "koding"
     if group.customize.background.coverPhoto
       new KDCustomHTMLView
         tagName     : 'img'
-        size        :
-            width   : 942
-            height  : 315
         attributes  :
           src       : group.customize.background.coverPhoto
           title     : group.title or ''
     else
-      # if group doesnt has cover photo, put collage
-      collageList  = new KDListViewController
+      # if group doesnt has cover photo, put collage of group users
+      collageList           = new KDListViewController
         startWithLazyLoader : no
         view                : new KDListView
           type              : "collage"
           cssClass          : "cover-list"
           itemClass         : CollageItemList

-      options =
-        limit : 20
+      options = limit : 20

       group.fetchMembersFromGraph options, (err, accounts) ->
         collageList.instantiateListItems accounts
-
       collageList.getView()

 class CollageItemList extends KDListItemView


Sidebar: Ensure default workspace exists

--- client/app/lib/activity/sidebar/activitysidebar.coffee
+++ client/app/lib/activity/sidebar/activitysidebar.coffee
@@ -100,6 +100,7 @@
     @on 'ReloadMessagesRequested',     @bound 'handleReloadMessages'

     environmentDataProvider.revive()
+    environmentDataProvider.ensureDefaultWorkspace @bound 'updateMachines'

     mainController.ready =>
       whoami().on 'NewWorkspaceCreated', @bound 'updateMachines'


Destroy previous machine share popup

Destroy previous machine share popup

--- client/app/lib/navigation/navigationmachineitem.coffee
+++ client/app/lib/navigation/navigationmachineitem.coffee
@@ -173,10 +173,15 @@
     return data.workspaces.first?.channelId

+  popups = {}
+
   showSharePopup: (options = {}) ->

     options.position = @getPopupPosition 20
-    new SidebarMachineSharePopup options, @machine
+
+    popup = popups[@machine.uid]
+    kd.utils.defer -> popup?.destroy()
+    popups[@machine.uid] = new SidebarMachineSharePopup options, @machine

   subscribeMachineShareEvent: ->


Wait for appStorage to get ready before doing anything

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -24,6 +24,7 @@
     @manifests      = KodingAppsController.manifests
     @publishedApps  = {}
     @_fetchQueue    = []
+    @appStorage     = KD.getSingleton('appStorageController').storage 'Finder', '1.1'

     @getPublishedApps()
     @createExtensionToAppMap()
@@ -49,19 +50,17 @@

   fetchApps:(callback)->

-    if not @appStorage? # KD.isLoggedIn() and
-      @appStorage = new AppStorage 'KodingApps', '1.0.1'
-
-    if Object.keys(@constructor.manifests).length isnt 0
-      callback null, @constructor.manifests
-    else
-      @fetchAppsFromDb (err, apps)=>
-        if err
-          @fetchAppsFromFs (err, apps)=>
-            if err then callback? err
-            else callback null, apps
-        else
-          callback? err, apps
+    @appStorage.ready =>
+      if Object.keys(@constructor.manifests).length isnt 0
+        callback null, @constructor.manifests
+      else
+        @fetchAppsFromDb (err, apps)=>
+          if err
+            @fetchAppsFromFs (err, apps)=>
+              if err then callback? err
+              else callback null, apps
+          else
+            callback? err, apps

   fetchAppFromFs:(appName, cb)->


Passed sortable flag between KDTabView and MainTabView.

--- client/landing/app/MainApp/maintabs/maintabview.coffee
+++ client/landing/app/MainApp/maintabs/maintabview.coffee
@@ -5,6 +5,7 @@
   constructor:(options,data)->
     options.resizeTabHandles    = yes
     options.lastTabHandleMargin = 40
+    options.sortable            = yes
     @visibleHandles             = []
     @totalSize                  = 0
     @paneViewIndex              = {}


Don't embed anything explicitly through HTTP, as we may want to serve everything over HTTPS

--- app/assets/javascripts/rglossa/speech/components/centre/results/jplayer.jsx.coffee
+++ app/assets/javascripts/rglossa/speech/components/centre/results/jplayer.jsx.coffee
@@ -194,7 +194,7 @@

-
+
                  <object id="jp_flash_1" name="jp_flash_1" data="Jplayer.swf" type="application/x-shockwave-flash" width="1" height="1" tabIndex="-1" style={{width: 1, height: 1}}>
                      <param name="flashvars" value="jQuery=jQuery&id=jplayer&vol=0.8&muted=false" />
                      <param name="allowscriptaccess" value="always" />