OX-11 testing for kevin

OX-11 testing for kevin

--- app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
+++ app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
@@ -6,8 +6,8 @@
 class PullQueue

   # Constants
-  BATCH_LIMIT : 4
-  BATCH_SIZE : 1
+  BATCH_LIMIT : 6
+  BATCH_SIZE : 2
   ROUND_TRIP_TIME_SMOOTHER : .125
   BUCKET_TIME_SMOOTHER : .125


Use HTML5 History for removing the OAuth 2 access token from the page URL, when available.

--- src/auth_driver/browser.coffee
+++ src/auth_driver/browser.coffee
@@ -153,6 +153,20 @@
   @currentLocation: ->
     window.location.href

+  # Removes the OAuth 2 access token from the current page's URL.
+  #
+  # This hopefully also removes the access token from the browser history.
+  #
+  # @return undefined
+  @cleanupLocation: ->
+    if window.history
+      pageUrl = @currentLocation()
+      hashIndex = pageUrl.indexOf '#'
+      window.history.replaceState {}, document.title,
+                                  pageUrl.substring(0, hashIndex)
+    else
+      window.location.hash = ''
+    return

 # OAuth driver that uses a redirect and localStorage to complete the flow.
 class Dropbox.AuthDriver.Redirect extends Dropbox.AuthDriver.BrowserBase
@@ -190,7 +204,7 @@
   resumeAuthorize: (stateParam, client, callback) ->
     if @locationStateParam() is stateParam
       pageUrl = Dropbox.AuthDriver.BrowserBase.currentLocation()
-      window.location.hash = ''  # Remove the token from the browser history.
+      Dropbox.AuthDriver.BrowserBase.cleanupLocation()
       callback Dropbox.Util.Oauth.queryParamsFromUrl pageUrl
     else
       @forgetCredentials ->
@@ -318,7 +332,7 @@
   @oauthReceiver: ->
     window.addEventListener 'load', ->
       pageUrl = window.location.href
-      window.location.hash = ''  # Remove the token from the browser history.
+      Dropbox.AuthDriver.BrowserBase.cleanupLocation()
       opener = window.opener
       if window.parent isnt window.top
         opener or= window.parent


adding whostheman script

adding whostheman script

--- scripts/whos-the-man.coffee
+++ scripts/whos-the-man.coffee
@@ -15,5 +15,5 @@
 #   Mateusz Kopij <sagasu>

 module.exports = (robot) ->
-  robot.respond /who's the man/i, (msg) ->
+  robot.respond /whos the man/i, (msg) ->
     msg.send "Prem, Prem, Prem!"


This takes a callback

This takes a callback

--- src/Documentation.coffee
+++ src/Documentation.coffee
@@ -147,13 +147,12 @@

   return lines

-renderCapabilities = () ->
+renderCapabilities = (callback) ->
   tv4 = require '../schema/index.js'
   schema = tv4.getSchema '/shared/capabilities'

   lines = []
   p = (line) -> lines.push line
-
   p "
"
   for enumDescription in schema.items._enumDescriptions
     p "
#{enumDescription.name}
"
@@ -175,7 +174,7 @@

   p "
"

-  return lines.join('\n')
+  callback null, lines.join('\n')

 renderMessages = (callback) ->
   getSchemas (err, schemas) ->


fix fsCheckbox e2e test

fix fsCheckbox e2e test

--- test/e2e/fsCheckboxSpec.coffee
+++ test/e2e/fsCheckboxSpec.coffee
@@ -12,12 +12,12 @@

   it 'should check item on click', ->
     expect($('#value').getText()).toBe ''
-    $('.fs-checkbox:first-child > div:first-child > a').click()
-    $('.fs-checkbox:first-child > div:last-child > a').click()
+    $('.fs-checkbox:first-child > div:first-child > .fs-racheck-item').click()
+    $('.fs-checkbox:first-child > div:last-child > .fs-racheck-item').click()

     takeScreenshot()
     expect($('#value').getText()).toMatch(/one/)
     expect($('#value').getText()).toMatch(/three/)

-    $('.fs-checkbox:first-child > div:first-child > a').click()
+    $('.fs-checkbox:first-child > div:first-child > .fs-racheck-item').click()
     expect($('#value').getText()).not.toMatch(/one/)


Fixing matcher for istanbul middleware

Fixing matcher for istanbul middleware

--- test/tests/utils/server.coffee
+++ test/tests/utils/server.coffee
@@ -10,7 +10,7 @@

 matcher = (req)->
     parsed = url.parse req.url
-    return /__split__\/src\//.test parsed.pathname
+    return /__split__\/lib\//.test parsed.pathname

 exports.start = (cover)->


Add impersonate feature to UI for selected user

--- client/landing/app/MainApp/sidebar/modals/adminmodal.coffee
+++ client/landing/app/MainApp/sidebar/modals/adminmodal.coffee
@@ -34,6 +34,30 @@
                 label         : "Flags:"
                 name          : "flags"
                 placeholder   : "no flags assigned"
+              Impersonate     :
+                label         : "Switch to User"
+                itemClass     : KDButtonView
+                title         : "Impersonate"
+                callback      : =>
+                  modal = new KDModalView
+                    title          : "Switch to this user?"
+                    content        : "
This action will reload Koding and log you in with this user.
"
+                    height         : "auto"
+                    overlay        : yes
+                    buttons        :
+                      Impersonate  :
+                        style      : "modal-clean-green"
+                        loader     :
+                          color    : "#FFF"
+                          diameter : 16
+                        callback   : =>
+                          accounts = @userController.getSelectedItemData()
+                          unless accounts.length is 0
+                            KD.impersonate accounts[0].profile.nickname, =>
+                              modal.destroy()
+                          else
+                            modal.destroy()
+
           "Migrate Kodingen Users" :
             buttons           :
               Migrate         :


strip away this turd

strip away this turd

--- src/lib/client.iced
+++ src/lib/client.iced
@@ -122,7 +122,6 @@
   prepare_key : (mode, cb) ->
     inp = @package_input mode
     await inp.derive_key defer key if inp?
-    @doc().finish_key mode if inp?
     cb key, inp

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


Removed not longer needed bindings.

Removed not longer needed bindings.

--- app/assets/javascripts/app/lib/app_post/collection.js.coffee
+++ app/assets/javascripts/app/lib/app_post/collection.js.coffee
@@ -28,22 +28,6 @@
   @include App.LogInclude

   constructor: (@args) ->
-
-    # add trigger - bind new events
-    App.Event.bind 'loadAssets', (data) =>
-      if !data
-        @log 'error', 'loadAssets:trigger, got no data, cant load assets'
-        return
-
-      @loadAssets( data )
-
-    # add trigger - bind new events
-    App.Event.bind 'resetCollection', (data) =>
-      if !data
-        @log 'error', 'resetCollection:trigger, got no data, cant for collections'
-        return
-
-      @resetCollections( data )

     # find collections to load
     @_loadObjectsFromLocalStore()


Renamed the angularjs hash that contains alert timers in the root scope, to clear the way before implementing other similar hashes for different timers.

--- app/assets/javascripts/ng-services/ng-TimerFlagSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-TimerFlagSvc.js.coffee
@@ -10,10 +10,10 @@
   # PRIVATE FUNCTION: Stop a running timer.
   #--------------------------------------------
   stop_timer = (flag)->
-    timer = $rootScope.timers[flag]
+    timer = $rootScope.alert_timers[flag]
     if timer
       $timeout.cancel timer
-      delete $rootScope.timers[flag]
+      delete $rootScope.alert_timers[flag]

   service =

@@ -23,14 +23,14 @@
     #---------------------------------------------
     start: (flag)->
       # Store running timers in a hash in the root scope
-      $rootScope.timers ||= {}
+      $rootScope.alert_timers ||= {}
       stop_timer flag
       eval "$rootScope.#{flag} = true"
       timer = $timeout ->
         eval "$rootScope.#{flag} = false"
-        delete $rootScope.timers[flag]
+        delete $rootScope.alert_timers[flag]
       , 5000
-      $rootScope.timers[flag] = timer
+      $rootScope.alert_timers[flag] = timer

     #---------------------------------------------
     # Set a boolean flag in the root scope as false.


activity: reformat `CommentViewHeader` class definition

--- client/Social/Activity/views/comments/commentviewheader.coffee
+++ client/Social/Activity/views/comments/commentviewheader.coffee
@@ -1,13 +1,11 @@
 class CommentViewHeader extends JView

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

     options.cssClass       = "show-more-comments in"
     options.itemTypeString = options.itemTypeString or "comments"

     super options, data
-
-    data = @getData()

     @maxCommentToShow = options.maxCommentToShow or 3
     @oldCount         = data.repliesCount
@@ -18,11 +16,12 @@
       @onListCount = data.repliesCount
       @hide()

-    @hide() if data.repliesCount is 0
+    @hide()  if data.repliesCount is 0

     list = @getDelegate()

     list.on "AllCommentsWereAdded", =>
+
       @newCount = 0
       @onListCount = @getData().repliesCount
       @updateNewCount()
@@ -32,7 +31,7 @@
       tagName   : "a"
       cssClass  : "all-count"
       pistachio : "View all {{#(repliesCount)}} #{@getOptions().itemTypeString}..."
-      click     : (event)->
+      click     : (event) ->
         KD.utils.stopDOMEvent event
         list.emit "AllCommentsLinkWasClicked", this
     , data
@@ -45,11 +44,14 @@
         list.emit "AllCommentsLinkWasClicked", this

     @liveUpdate = KD.getSingleton('activityController').flags?.liveUpdates or off
+
     KD.getSingleton('activityController').on "LiveStatusUpdateStateChanged", (newstate)=>
+
       # log "Live update state changed to", newstate
       @liveUpdate = newstate

-  ownCommentArrived:->
+
+  ownCommentArrived: ->

     # Get correct number of items in list from controller
     # I'm not sure maybe its not a good idea
@@ -64,40 +66,13 @@

     @updateNewCount()

-  ownCommentDeleted:->
-    if @newCount > 0
-      @newCount++

-  render:->
+  ownCommentDeleted: ->

-    # Get correct number of items in list from controller
-    # I'm not sure maybe its not a good idea
-    if @parent?.commentController?.getItemCount?()
-      @onListCount = @parent.commentController.getItemCount()
-    _newCount = @getData().repliesCount
+    @newCount++  if @newCount > 0

-    # Show View all bla bla link if there are more comments
-    # than maxCommentToShow
-    @show() if _newCount > @maxCommentToShow and @onListCount < _newCount

-    # Check the oldCount before update anything
-    # if its less means someone deleted a comment
-    # otherwise it meanse we have a new comment
-    # if nothing changed it means user clicked like button
-    # so we don't need to touch anything
-    if _newCount > @oldCount
-      @newCount++
-    else if _newCount < @oldCount
-      if @newCount > 0 then @newCount--
-
-    # If the count is changed then we need to update UI
-    if _newCount isnt @oldCount
-      @oldCount = _newCount
-      @utils.defer => @updateNewCount()
-
-    super
-
-  updateNewCount:->
+  updateNewCount: ->

     # If there is no comments so we can not have new comments
     if @oldCount is 0 then @newCount = 0
@@ -127,16 +102,51 @@
     else
       @show()

-  hide:->
-    @unsetClass "in"
+
+  show: ->
+
+    @setClass "in"
+
     super

-  show:->
-    @setClass "in"
+
+  hide: ->
+
+    @unsetClass "in"
+
     super

-  pistachio:->
-    """
-      {{> @allItemsLink}}
-      {{> @newItemsLink}}
-    """+
+  render: ->
+
+    # Get correct number of items in list from controller
+    # I'm not sure maybe its not a good idea
+    if @parent?.commentController?.getItemCount?()
+      @onListCount = @parent.commentController.getItemCount()
+    _newCount = @getData().repliesCount
+
+    # Show View all bla bla link if there are more comments
+    # than maxCommentToShow
+    @show() if _newCount > @maxCommentToShow and @onListCount < _newCount
+
+    # Check the oldCount before update anything
+    # if its less means someone deleted a comment
+    # otherwise it meanse we have a new comment
+    # if nothing changed it means user clicked like button
+    # so we don't need to touch anything
+    if _newCount > @oldCount
+      @newCount++
+    else if _newCount < @oldCount
+      if @newCount > 0 then @newCount--
+
+    # If the count is changed then we need to update UI
+    if _newCount isnt @oldCount
+      @oldCount = _newCount
+      @utils.defer => @updateNewCount()
+
+    super
+
+
+  pistachio: ->
+
+    "{{> @allItemsLink}}{{> @newItemsLink}}"


Classroom: Added placeholder view.

Classroom: Added placeholder view.

--- client/app/Applications/Classroom.kdapplication/AppView.coffee
+++ client/app/Applications/Classroom.kdapplication/AppView.coffee
@@ -5,5 +5,13 @@
     super options, data

     @addSubView @container = new KDView
-      partial  : "HELLLO"
-      cssClass : "classroom"+      cssClass : "classroom"
+      partial  : """
+

+

+
<classroom />

+
Start learning, teaching and sharing together.
 It's your classroom and it's online and it's free!

+
+

+

+      """


clean up whitespace

clean up whitespace

--- services/web/public/coffee/ide/connection/ConnectionManager.coffee
+++ services/web/public/coffee/ide/connection/ConnectionManager.coffee
@@ -28,8 +28,8 @@
 			@connected = false
 			@userIsInactive = false
 			@gracefullyReconnecting = false
-
-			@$scope.connection =
+
+			@$scope.connection =
 				reconnecting: false
 				# If we need to force everyone to reload the editor
 				forced_disconnect: false
@@ -262,7 +262,7 @@
 				setTimeout () =>
 					@reconnectGracefully()
 				, @RECONNECT_GRACEFULLY_RETRY_INTERVAL
-
+
 		_reconnectGracefullyNow: () ->
 			@gracefullyReconnecting = true
 			@reconnectGracefullyStarted = null


remove the render timeout

remove the render timeout

pdfjs uses requestAnimationFrame to schedule the rendering, and this
does not fire if the tab is inactive. That causes an unwanted error if
switching tabs for more than the one minute timeout.

--- services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee
@@ -8,7 +8,6 @@
 		class PDFRenderer
 			JOB_QUEUE_INTERVAL: 25
 			PAGE_LOAD_TIMEOUT: 60*1000
-			PAGE_RENDER_TIMEOUT: 60*1000
 			INDICATOR_DELAY1: 100  # time to delay before showing the indicator
 			INDICATOR_DELAY2: 250  # time until the indicator starts animating

@@ -295,19 +294,9 @@
 					transform: [pixelRatio, 0, 0, pixelRatio, 0, 0]
 				}

-				timedOut = false
-
-				timer = $timeout () =>
-					# page render timed out
-					Raven?.captureMessage?('pdfng page render timed out after ' + @PAGE_RENDER_TIMEOUT + 'ms (1% sample)') if Math.random() < 0.01
-					timedOut = true
-					result.cancel()
-				, @PAGE_RENDER_TIMEOUT
-
 				result.then () ->
 					# page render success
 					element.canvas.replaceWith(canvas)
-					$timeout.cancel(timer)
 					canvas.removeClass('pdfng-rendering')
 					page.getTextContent().then (textContent) ->
 						textLayer.setTextContent textContent
@@ -319,10 +308,7 @@
 						self.errorCallback?(error)
 				.catch (error) ->
 					# page render failed
-					$timeout.cancel(timer)
-					if timedOut
-						self.errorCallback?('timeout')
-					else if error is 'cancelled'
+					if error is 'cancelled'
 						return # do nothing when cancelled
 					else
 						self.errorCallback?(error)


Fix $x and $y in hover tool by not shadowing variables (#5406)

--- bokehjs/src/coffee/models/tools/inspectors/hover_tool.coffee
+++ bokehjs/src/coffee/models/tools/inspectors/hover_tool.coffee
@@ -200,13 +200,11 @@
           # which patch we're over if there are discontinuous
           # patches.
           pt = renderer.glyph.get_anchor_point(@model.anchor, i, [sx, sy])
-          if pt?
-            {x, y} = pt
-          else
-            {x, y} = renderer.glyph.get_anchor_point("center", i, [sx, sy])
-
-          rx = canvas.sx_to_vx(x)
-          ry = canvas.sy_to_vy(y)
+          if not pt?
+            pt = renderer.glyph.get_anchor_point("center", i, [sx, sy])
+
+          rx = canvas.sx_to_vx(pt.x)
+          ry = canvas.sy_to_vy(pt.y)
         else
           [rx, ry] = [vx, vy]


quick bug fix

quick bug fix

--- server/publish/products.coffee
+++ server/publish/products.coffee
@@ -11,6 +11,7 @@
   category: 1
   minimumOrder: 1
   daysNotice: 1
+  extraMarkup: 1

 Meteor.publish "active-products", (query, limit, sort)->


protect over cyclic references

protect over cyclic references

--- library/exposure/session.coffee
+++ library/exposure/session.coffee
@@ -75,6 +75,7 @@
         assert _.isObject(@redis), "no Redis client yet"
         message = "Redis session engine error at destroy"
         process = "Destroying a Redis stored session %s"
+        {GraniteKernel} = try require "../nucleus/kernel"
         assert kernel = @kernel or GraniteKernel.instance
         logger.debug process.grey, try sid.underline or 0
         assert _.all try hosting = [this, kernel] or null
@@ -104,6 +105,7 @@
         process = "Writing in a Redis stored session %s"
         assert _.isObject(@redis), "no Redis client yet"
         assert not _.isEmpty(encoded or null), encFailed
+        {GraniteKernel} = try require "../nucleus/kernel"
         assert kernel = @kernel or GraniteKernel.instance
         logger.debug process.grey, try sid.underline or 0
         assert _.all try hosting = [this, kernel] or null
@@ -129,6 +131,7 @@
         df = "failed to decode JSON payload on the get"
         process = "Reading out a Redis stored session %s"
         assert _.isObject(@redis), "no Redis client yet"
+        {GraniteKernel} = try require "../nucleus/kernel"
         assert kernel = @kernel or GraniteKernel.instance
         logger.debug process.grey, try sid.underline or 0
         assert _.all try hosting = [this, kernel] or null


default values

default values

--- vendor/assets/javascripts/fustrate/components/modal.coffee
+++ vendor/assets/javascripts/fustrate/components/modal.coffee
@@ -60,14 +60,14 @@
     else
       $('.modal-title span', @modal).html title

-  setContent: (content, { reload = true }) =>
+  setContent: (content, { reload } = { reload: true }) =>
     $('.modal-content', @modal).html content

     @settings._cachedHeight = undefined

     @_reloadUIElements() if reload

-  setButtons: (buttons, { reload = true }) =>
+  setButtons: (buttons, { reload } = { reload: true }) =>
     if buttons?.length < 1
       $('.modal-buttons', @modal).empty()


:lipstick: rename @screenLines to @lineFragments

--- src/atom/line-map.coffee
+++ src/atom/line-map.coffee
@@ -5,49 +5,49 @@
 module.exports =
 class LineMap
   constructor: ->
-    @screenLines = []
+    @lineFragments = []

-  insertAtBufferRow: (bufferRow, screenLines) ->
+  insertAtBufferRow: (bufferRow, lineFragments) ->
     delta = new Point
     insertIndex = 0

-    for screenLine in @screenLines
-      nextDelta = delta.add(screenLine.bufferDelta)
+    for lineFragment in @lineFragments
+      nextDelta = delta.add(lineFragment.bufferDelta)
       break if nextDelta.row > bufferRow
       delta = nextDelta
       insertIndex++

-    @screenLines[insertIndex...insertIndex] = screenLines
+    @lineFragments[insertIndex...insertIndex] = lineFragments

-  spliceAtBufferRow: (startRow, rowCount, screenLines) ->
-    @spliceByDelta('bufferDelta', startRow, rowCount, screenLines)
+  spliceAtBufferRow: (startRow, rowCount, lineFragments) ->
+    @spliceByDelta('bufferDelta', startRow, rowCount, lineFragments)

-  spliceAtScreenRow: (startRow, rowCount, screenLines) ->
-    @spliceByDelta('screenDelta', startRow, rowCount, screenLines)
+  spliceAtScreenRow: (startRow, rowCount, lineFragments) ->
+    @spliceByDelta('screenDelta', startRow, rowCount, lineFragments)

-  spliceByDelta: (deltaType, startRow, rowCount, screenLines) ->
+  spliceByDelta: (deltaType, startRow, rowCount, lineFragments) ->
     stopRow = startRow + rowCount
     startIndex = undefined
     stopIndex = 0
     delta = new Point

-    for screenLine, i in @screenLines
+    for lineFragment, i in @lineFragments
       startIndex ?= i if delta.row == startRow
-      nextDelta = delta.add(screenLine[deltaType])
+      nextDelta = delta.add(lineFragment[deltaType])
       break if nextDelta.row > stopRow
       delta = nextDelta
       stopIndex++

-    @screenLines[startIndex...stopIndex] = screenLines
+    @lineFragments[startIndex...stopIndex] = lineFragments

-  replaceBufferRows: (start, end, screenLines) ->
-    @spliceAtBufferRow(start, end - start + 1, screenLines)
+  replaceBufferRows: (start, end, lineFragments) ->
+    @spliceAtBufferRow(start, end - start + 1, lineFragments)

-  replaceScreenRow: (row, screenLines) ->
-    @replaceScreenRows(row, row, screenLines)
+  replaceScreenRow: (row, lineFragments) ->
+    @replaceScreenRows(row, row, lineFragments)

-  replaceScreenRows: (start, end, screenLines) ->
-    @spliceAtScreenRow(start, end - start + 1, screenLines)
+  replaceScreenRows: (start, end, lineFragments) ->
+    @spliceAtScreenRow(start, end - start + 1, lineFragments)

   lineForScreenRow: (row) ->
     @linesForScreenRows(row, row)[0]
@@ -56,44 +56,44 @@
     lines = []
     delta = new Point

-    for fragment in @screenLines
+    for lineFragment in @lineFragments
       break if delta.row > endRow
       if delta.row >= startRow
         if pendingFragment
-          pendingFragment = pendingFragment.concat(fragment)
+          pendingFragment = pendingFragment.concat(lineFragment)
         else
-          pendingFragment = _.clone(fragment)
+          pendingFragment = _.clone(lineFragment)
         if pendingFragment.screenDelta.row > 0
           pendingFragment.bufferDelta = new Point(1, 0)
           lines.push pendingFragment
           pendingFragment = null
-      delta = delta.add(fragment.screenDelta)
+      delta = delta.add(lineFragment.screenDelta)

     lines

   lineForBufferRow: (row) ->
     line = null
     delta = new Point
-    for fragment in @screenLines
+    for lineFragment in @lineFragments
       break if delta.row > row
       if delta.row == row
         if line
-          line = line.concat(fragment)
+          line = line.concat(lineFragment)
         else
-          line = fragment
-      delta = delta.add(fragment.bufferDelta)
+          line = lineFragment
+      delta = delta.add(lineFragment.bufferDelta)
     line

   bufferLineCount: ->
     delta = new Point
-    for screenLine in @screenLines
-      delta = delta.add(screenLine.bufferDelta)
+    for lineFragment in @lineFragments
+      delta = delta.add(lineFragment.bufferDelta)
     delta.row

   screenLineCount: ->
     delta = new Point
-    for screenLine in @screenLines
-      delta = delta.add(screenLine.screenDelta)
+    for lineFragment in @lineFragments
+      delta = delta.add(lineFragment.screenDelta)
     delta.row

   lastScreenRow: ->
@@ -104,15 +104,15 @@
     bufferDelta = new Point
     screenDelta = new Point

-    for screenLine in @screenLines
-      nextDelta = bufferDelta.add(screenLine.bufferDelta)
+    for lineFragment in @lineFragments
+      nextDelta = bufferDelta.add(lineFragment.bufferDelta)
       break if nextDelta.isGreaterThan(bufferPosition)
       break if nextDelta.isEqual(bufferPosition) and not eagerWrap
       bufferDelta = nextDelta
-      screenDelta = screenDelta.add(screenLine.screenDelta)
+      screenDelta = screenDelta.add(lineFragment.screenDelta)

     remainingBufferColumn = bufferPosition.column - bufferDelta.column
-    additionalScreenColumn = Math.max(0, Math.min(remainingBufferColumn, screenLine.lengthForClipping()))
+    additionalScreenColumn = Math.max(0, Math.min(remainingBufferColumn, lineFragment.lengthForClipping()))

     new Point(screenDelta.row, screenDelta.column + additionalScreenColumn)

@@ -121,11 +121,11 @@
     bufferDelta = new Point
     screenDelta = new Point

-    for screenLine in @screenLines
-      nextDelta = screenDelta.add(screenLine.screenDelta)
+    for lineFragment in @lineFragments
+      nextDelta = screenDelta.add(lineFragment.screenDelta)
       break if nextDelta.isGreaterThan(screenPosition)
       screenDelta = nextDelta
-      bufferDelta = bufferDelta.add(screenLine.bufferDelta)
+      bufferDelta = bufferDelta.add(lineFragment.bufferDelta)

     column = bufferDelta.column + (screenPosition.column - screenDelta.column)
     new Point(bufferDelta.row, column)
@@ -157,7 +157,7 @@
       screenPosition.column = Infinity

     screenDelta = new Point
-    for lineFragment in @screenLines
+    for lineFragment in @lineFragments
       nextDelta = screenDelta.add(lineFragment.screenDelta)
       break if nextDelta.isGreaterThan(screenPosition)
       screenDelta = nextDelta


Socialapi: add new handler's caller function

--- workers/social/lib/social/models/socialapi/requests.coffee
+++ workers/social/lib/social/models/socialapi/requests.coffee
@@ -428,6 +428,10 @@
 fetchBotChannel = (data, callback) ->
   url = "#{webhookProxyUrl}/botchannel"
   get url, data, callback
+
+publishMailEvent = (data, callback) ->
+  url = "#{socialProxyUrl}/private/mail/publish"
+  post url, data, callback

 post = (url, data, callback) ->
   reqOptions =
@@ -566,4 +570,5 @@
   post
   get
   deleteReq
+  publishMailEvent
 }


add portfolio status to student export

add portfolio status to student export

--- src/app/units/partials/directives/unit-contexts.coffee
+++ src/app/units/partials/directives/unit-contexts.coffee
@@ -38,7 +38,7 @@
       )

     $scope.getCSVHeader = () ->
-      result = ['student_code', 'name', 'email', 'lab']
+      result = ['student_code', 'name', 'email', 'portfolio', 'lab']
       angular.forEach(projectService.progressKeys, (key) ->
         result.push(key)
       )
@@ -55,6 +55,7 @@
         row['student_code'] = student.student_id
         row['name'] = student.name
         row['email'] = student.student_email
+        row['portfolio'] = student.portfolio_status
         if student.tutorial
           row['lab'] = student.tutorial.abbreviation
         else


Try to remove SQL logins

Try to remove SQL logins

--- src/block/connect.coffee
+++ src/block/connect.coffee
@@ -101,4 +101,30 @@
     try
       mssql.h.ActiveConnection.sp_dropuser u.name
     catch
-  echo "Удалялись: #{x.join ', '}"
+  echo "Удалялись: #{x.join ', '}" if x.length
+
+
+echo 'Удаление логинов SQL'
+do ->
+  mssql.command "Create Table #Do(main NVarChar(255))"
+  .Execute()
+  cmd = mssql.command "Insert Into #Do(main) Values(?+'\\')"
+  assign cmd, 0, ad.dc
+  cmd.Execute()
+  cmd = mssql.command """
+    Select name
+    From master..syslogins
+    Where name in
+      (Select (Select main From #Do)+UserLogin Collate Cyrillic_General_CI_AS
+       From MBUser
+       Where UserStatus='О' And NeedEncode='W')
+  """
+  x = []
+  mssql.execute cmd, (u)->
+    x.push u.name
+    try
+      mssql.h.ActiveConnection.sp_revokelogin u.name
+    catch
+  echo "Удалялись: #{x.join ', '}" if x.length
+
+echo "That's all folks!"


Client: Activity: Using threads instead of  sidebarModalChannels.followed and using state.isSearching instead of searching flag

--- client/activity/lib/components/sidebarmodallist/index.coffee
+++ client/activity/lib/components/sidebarmodallist/index.coffee
@@ -9,8 +9,6 @@
 module.exports = class SidebarModalList extends React.Component

   @include [ScrollerMixin]
-
-  isSearching = no

   @defaultProps =
     title             : ''
@@ -24,14 +22,15 @@
     super

     @state =
-      value: ''
-      noResultText: no
-      threads: @props.sidebarModalChannels.followed
+      value             : ''
+      noResultText      : no
+      isSearching       : no
+      threads           : @props.threads

   onThresholdReached: ->

-    return  if isSearching
+    return  if @state.isSearching

     { channel } = ActivityFlux.actions
     loadFollowedChannels = channel[@props.onThresholdAction]
@@ -42,22 +41,22 @@

   setThreads: kd.utils.debounce 1000, ->

-    @setState threads: @props.sidebarModalChannels.followed
+    @setState threads: @props.threads

   resetSearch: ->

-    threads = @props.sidebarModalChannels.followed
+    threads = @props.threads
     @setState threads: threads
     @setState noResultText: no  if threads.size

   filter: kd.utils.debounce 800, ->

-    { sidebarModalChannels } = @props
+    { threads } = @props
     { value } = @state

-    threads = sidebarModalChannels.followed.filter (thread) =>
+    threads = threads.filter (thread) =>
       typeConstant = thread.getIn ['channel', 'typeConstant']
       if @props.searchProp is 'purpose' and typeConstant is 'bot'
         thread = thread.setIn ['channel', 'purpose'], 'Bot Koding'
@@ -66,8 +65,8 @@

     @setState { threads : threads, noResultText : threads.size is 0}

-    kd.utils.wait 1000, ->
-      isSearching = no
+    kd.utils.wait 1000, =>
+      @setState isSearching: no

   search: (event) ->
@@ -79,7 +78,7 @@

     return @resetSearch()  if value is ''

-    isSearching = yes
+    @setState isSearching: yes

     value = value.slice(1)  if value[0] is '#'
     loadFollowedChannels = channel[@props.onThresholdAction]
@@ -113,7 +112,7 @@

   renderChildren: ->

-    { itemComponent: Component, threads } = @props
+    { itemComponent: Component } = @props

     @state.threads.toList().map (thread, i) ->
       itemProps =


webterm: pull focus on paste only if the paste happened while web term was focussed.

--- client/app/Applications/WebTerm.kdapplication/AppView.coffee
+++ client/app/Applications/WebTerm.kdapplication/AppView.coffee
@@ -105,8 +105,9 @@
       @terminal.setFocused @focused

     $(document).on "paste", (event) =>
-      @terminal.server.input event.originalEvent.clipboardData.getData("text/plain") if @focused
-      @setKeyView()
+      if @focused
+        @terminal.server.input event.originalEvent.clipboardData.getData("text/plain")
+        @setKeyView()

     @bindEvent 'contextmenu'


header: duplicate testpath fix for login link

--- client/landing/site.landing/coffee/core/topnavigation.coffee
+++ client/landing/site.landing/coffee/core/topnavigation.coffee
@@ -4,9 +4,10 @@

   menu = [
     { title : 'Koding University', href : 'http://learn.koding.com', name : 'about'}
-    { title : 'Features',          href : '/Features',               name : 'features'}
-    { title : 'SIGN IN',           href : '/Login',                  name : 'buttonized white login', attributes: testpath: 'login-link'}
-    { title : 'SIGN UP',           href : '/Register',               name : 'buttonized green signup', attributes: testpath: 'login-link'}
+    # { title : 'Teams',    href : '/Teams',    name : 'teams'}
+    { title : 'Features', href : '/Features', name : 'features'}
+    { title : 'SIGN IN',  href : '/Login',    name : 'buttonized white login', attributes: testpath: 'login-link'}
+    { title : 'SIGN UP',  href : '/Register', name : 'buttonized green signup', attributes: testpath: 'signup-link'}
   ]

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


Return null from OpenURL.execute() to reduce output size

--- bokehjs/src/coffee/action/open_url.coffee
+++ bokehjs/src/coffee/action/open_url.coffee
@@ -19,6 +19,7 @@
     for i in indices
       url = Util.replace_placeholders(@get("url"), data_source, i)
       window.open(url)
+    null

   defaults: ->
     return _.extend {}, super(), {
@@ -26,4 +27,4 @@
     }

 module.exports =
-  Model: OpenURL+  Model: OpenURL


Draw a grid on the floor

Draw a grid on the floor

--- source/game_renderer.coffee
+++ source/game_renderer.coffee
@@ -68,9 +68,24 @@
     @floor.position.z = Game.state.levelHeight * 5
     @room.add @floor

-    @grid = new THREE.GridHelper(1000, 10)
-    @grid.position.y += 0.01
-    #@room.add @grid
+
+    # Draw a grid on the floor
+    geometry = new THREE.Geometry()
+    material = new THREE.LineBasicMaterial({vertexColors: THREE.VertexColors})
+    color    = new THREE.Color(0x666666)
+    for i in [0..(Game.state.levelHeight*10)] by 10
+      geometry.vertices.push(
+        new THREE.Vector3(0, 0, i), new THREE.Vector3(Game.state.levelWidth*10, 0, i)
+      )
+      geometry.colors.push(color, color)
+    for i in [0..(Game.state.levelWidth*10)] by 10
+      geometry.vertices.push(
+        new THREE.Vector3(i, 0, 0), new THREE.Vector3(i, 0, Game.state.levelHeight*10)
+      )
+      geometry.colors.push(color, color)
+    @grid = new THREE.Line(geometry, material, THREE.LinePieces)
+    @grid.position.y += 0.1
+    @room.add @grid

     # Add/update all the movable stuff
     this.update()


KodingLogger.test: tests for getIdentifier and scope helpers {log, info, warn, debug}

--- workers/social/lib/social/models/kodinglogger.test.coffee
+++ workers/social/lib/social/models/kodinglogger.test.coffee
@@ -3,6 +3,26 @@

 runTests = -> describe 'KodingLogger', ->
+
+  it 'should provide helper method for each scope', (done) ->
+
+    for scope in KodingLogger.SCOPES
+      expect(KodingLogger[scope]).to.exist
+
+    done()
+
+
+  describe '# getIdentifier', ->
+
+    it 'should generate an identifier for provided group and scope', (done) ->
+
+      group = 'testgroup'
+      scope = 'warn'
+
+      identifier = KodingLogger.getIdentifier scope, group
+      expect(identifier).to.be.equal "[#{scope}:#{group}]"
+
+      done()

   describe '# generateRestrictedQuery', ->


payment: fix naming

payment: fix naming

--- workers/social/lib/social/models/socialapi/payment.coffee
+++ workers/social/lib/social/models/socialapi/payment.coffee
@@ -68,7 +68,7 @@

     get url, data, callback

-  @creditCard = secure (client, data, callback)->
+  @creditcard = secure (client, data, callback)->
     data.accountId = getAccountId client
     url = "/payments/creditcard/#{data.accountId}"


Fix reposting a post

Fix reposting a post

--- lib/assets/javascripts/views/post/post_action_repost.js.coffee
+++ lib/assets/javascripts/views/post/post_action_repost.js.coffee
@@ -6,11 +6,11 @@
     data = {
       permissions:
         public: true
-      type: 'https://tent.io/types/post/repost/v0.1.0'
+      type: "https://tent.io/types/repost/v0##{encodeURIComponent (new TentClient.PostType post.get('type')).toStringWithoutFragment()}"
       mentions: [{ entity: post.get('entity'), post: post.get('id') }]
       content:
         entity: post.get('entity')
-        id: post.get('id')
+        post: post.get('id')
     }
     TentStatus.Models.Post.create(data,
       error: (res, xhr) =>


many advice

many advice

--- app/views/weather.coffee
+++ app/views/weather.coffee
@@ -1,7 +1,4 @@
 class exports.WeatherView extends Backbone.Marionette.ItemView
-  onBeforeRender:
-    app.models.weather.fetch()
-  model: app.models.weather
   template: require './templates/weather'
   onRender:
     console.log @model


Make sure you have the valid location before showing marker on map

--- app/assets/javascripts/angular/controllers/postJobCtrl.coffee
+++ app/assets/javascripts/angular/controllers/postJobCtrl.coffee
@@ -10,8 +10,12 @@
 		$scope.lat = null;
 		$scope.lng = null;
 		$scope.editjob = {};
-		$scope.map = {};
-		$scope.emap = {};
+		$scope.map = {
+			zoom : 8
+		};
+		$scope.emap = {
+			zoom : 8
+		};
 		$scope.newjob = {};
 		$scope.editjob = null;
 		$scope.marker = {
@@ -98,37 +102,47 @@
 		showPosition = (position) ->
 			$scope.lat = position.coords.latitude;
 			$scope.lng = position.coords.longitude;
-			$scope.map = {
-				center: { latitude: $scope.lat, longitude: $scope.lng};
-			};
+			$scope.map.center = { latitude: $scope.lat, longitude: $scope.lng};
 			$scope.marker.coord =  {latitude: $scope.lat, longitude: $scope.lng};
 			$scope.$apply();
 			return true
-
-		$scope.$watchCollection 'marker.coord', ->
-			$scope.lat = $scope.marker.coord.latitude
-			$scope.lng = $scope.marker.coord.longitude
-			$scope.map.center = {latitude: $scope.lat, longitude: $scope.lng};
-			$http({url: '/getaddress', params: {lat: $scope.lat, lng: $scope.lng}}).then (response) ->
-				$scope.newjob.address = response.data.result
-
-		$scope.$watchCollection 'emarker.coord', ->
-			if $scope.editjob
-				$scope.editjob.lat = $scope.emarker.coord.latitude
-				$scope.editjob.lng = $scope.emarker.coord.longitude
-				$scope.emap.center = {latitude: $scope.editjob.lat, longitude: $scope.editjob.lng};
-				$http({url: '/getaddress', params: {lat: $scope.editjob.lat, lng: $scope.editjob.lng}}).then (response) ->
-					$scope.editjob.address = response.data.result
-
-		$scope.toggleLatLng = () ->
-			if(navigator.geolocation)
-				navigator.geolocation.getCurrentPosition(showPosition);
-			else
-				alert("Browser not supported");
-			return true
-
-		$scope.toggleLatLng();

+		showPositionOnEdit = (position) ->
+                        $scope.lat = position.coords.latitude;
+                        $scope.lng = position.coords.longitude;
+                        $scope.emap.center = { latitude: $scope.lat, longitude: $scope.lng};
+                        $scope.emarker.coord =  {latitude: $scope.lat, longitude: $scope.lng};
+                        $scope.$apply();
+                        return true
+
+		$scope.toggleLatLng = () ->
+                        if(navigator.geolocation)
+                                navigator.geolocation.getCurrentPosition(showPosition);
+                        else
+                                alert("Browser not supported");
+                        return true
+
+                $scope.toggleLatLng();
+
+		$scope.$watchCollection 'marker.coord',(newValue, oldValue) ->
+			if(oldValue != newValue)
+				$scope.lat = $scope.marker.coord.latitude
+				$scope.lng = $scope.marker.coord.longitude
+				$scope.map.center = {latitude: $scope.lat, longitude: $scope.lng};
+				$http({url: '/getaddress', params: {lat: $scope.lat, lng: $scope.lng}}).then (response) ->
+					if response.data.result?
+						$scope.newjob.address = response.data.result
+
+		$scope.$watchCollection 'emarker.coord', (newValue, oldValue) ->
+			if oldValue != newValue
+				if $scope.editjob
+					$scope.editjob.lat = $scope.emarker.coord.latitude
+					$scope.editjob.lng = $scope.emarker.coord.longitude
+					$scope.emap.center = {latitude: $scope.editjob.lat, longitude: $scope.editjob.lng};
+					$http({url: '/getaddress', params: {lat: $scope.editjob.lat, lng: $scope.editjob.lng}}).then (response) ->
+						if response.data.result?
+							$scope.editjob.address = response.data.result
+
 		$scope.edit = (job) ->
 			$timeout ()->
 				$scope.modalshow = true
@@ -141,10 +155,11 @@

 		$scope.etoggleLatLng = () ->
 			if(navigator.geolocation)
-				navigator.geolocation.getCurrentPosition(showPosition);
+				navigator.geolocation.getCurrentPosition(showPositionOnEdit);
 			else
 				alert("Browser not supported");
 			return true
+
 		$scope.updateJob = (editjob) ->
 			editjob.lat = $scope.editjob.lat;
 			editjob.lng = $scope.editjob.lng;
@@ -171,16 +186,19 @@

 		$scope.getLatLngfAdd = (address) ->
 			$http({url: '/getlatlng', method: 'GET', params: {address: address}}).then (response) ->
-				$scope.lat = response.data.result[0];
-				$scope.lng = response.data.result[1];
-				$scope.map.center = {latitude: $scope.lat, longitude: $scope.lng};
-				$scope.marker.coord = {latitude: $scope.lat, longitude: $scope.lng};
+				if response.data.result[0]? && response.data.result[1]?
+					$scope.lat = response.data.result[0];
+					$scope.lng = response.data.result[1];
+					$scope.map.center = {latitude: $scope.lat, longitude: $scope.lng};
+					$scope.marker.coord = {latitude: $scope.lat, longitude: $scope.lng};
+
 		$scope.egetLatLngfAdd = (address) ->
 			$http({url: '/getlatlng', method: 'GET', params: {address: address}}).then (response) ->
-				$scope.editjob.lat = response.data.result[0];
-				$scope.editjob.lng = response.data.result[1];
-				$scope.emap.center = {latitude: $scope.editjob.lat, longitude: $scope.editjob.lng};
-				$scope.emarker.coord = {latitude: $scope.editjob.lat, longitude: $scope.editjob.lng};
+				if response.data.result[0]? && response.data.result[1]?
+					$scope.editjob.lat = response.data.result[0];
+					$scope.editjob.lng = response.data.result[1];
+					$scope.emap.center = {latitude: $scope.editjob.lat, longitude: $scope.editjob.lng};
+					$scope.emarker.coord = {latitude: $scope.editjob.lat, longitude: $scope.editjob.lng};
 		return
 	]
-	return+	return


FinderController: fix mountMachine, base method to mount a Machine instance to filetree

--- client/Finder/filetree/controllers/findercontroller.coffee
+++ client/Finder/filetree/controllers/findercontroller.coffee
@@ -132,40 +132,41 @@
     return warn err if err or not info
     switch info.state
       when "MAINTENANCE" then @unmountVm vm
-
-  mountVm:(vm, fetchContent = yes)->
-
-    { region, hostnameAlias: vmName, path } = vm
-
-    vmRoots = (@appStorage.getValue 'vmRoots') or {}
-    pipedVm = @_pipedVmName vmName
-    path    ?= "/home/#{KD.nick()}"
-
-    if vmItem = @getVmNode vmName
-      return warn "VM #{vmName} is already mounted!"
-
-    @updateMountState vmName, yes
-
-    @vms.push FSHelper.createFile
-      name   : "#{path}"
-      path   : "[#{vmName}]#{path}"
-      type   : "vm"
-      vmName : vmName
-      vm     : vm
-      treeController: @treeController
-
-    @noVMFoundWidget.hide()
-    @treeController.addNode @vms.last
-
-    vmItem = @getVmNode vmName
-    if fetchContent and vmItem
+  mountMachine:(machine, fetchContent = yes)->
+
+    unless machine.status.state is Machine.State.Running
+      return warn "Machine '#{machine.getName()}' was not ready, I skipped it."
+
+    { uid } = machine
+    mRoots  = (@appStorage.getValue 'machineRoots') or {}
+    path    = mRoots[uid] or "/"
+
+    if @getMachineNode uid
+      return warn "Machine #{machine.getName()} is already mounted!"
+
+    @updateMountState uid, yes
+
+    @machines.push FSHelper.createFileInstance
+      name           : path
+      path           : "[#{uid}]#{path}"
+      type           : "machine"
+      machine        : machine
+      treeController : @treeController
+
+    @noMachineFoundWidget.hide()
+
+    machineItem = @treeController.addNode @machines.last
+
+    if fetchContent and machineItem
+
       @utils.defer =>
-        @treeController.expandFolder vmItem, (err)=>
-          if err?.name is 'VMNotFoundError'
-            return @unmountVm vmName
-          @treeController.selectNode vmItem
+
+        @treeController.expandFolder machineItem, (err)=>
+
+          @treeController.selectNode machineItem
+
           @utils.defer =>
-            if @getOptions().useStorage then @reloadPreviousState vmName
+            if @getOptions().useStorage then @reloadPreviousState uid
         , yes

   unmountVm:(vmName)->


bugfix: took the wrong line while i was merging

--- client/landing/app/MainApp/VirtualizationController.coffee
+++ client/landing/app/MainApp/VirtualizationController.coffee
@@ -98,7 +98,7 @@
           callback message: "No such VM!"

   info:(vm, callback)->
-    [callback, vm] = [vm, callback]  unless callback
+    [callback, vm] = [vm, callback]  if 'function' is typeof callback
     @_runWrapper 'vm.info', vm, (err, info)=>
       warn "[VM-#{vm}]", err  if err
       if err?.name is "UnderMaintenanceError"


Home: change the welcome message per site

--- client/landing/site.boilerplate/coffee/home/AppView.coffee
+++ client/landing/site.boilerplate/coffee/home/AppView.coffee
@@ -58,6 +58,10 @@

   partial: ->

+    slogan = if KD.siteName
+    then "Welcome to #{KD.siteName}"
+    else "Full Stack Browser Based Development"
+
     """
     <section class="introduction">
       <div>


Closing tag should be more specific

Closing tag should be more specific

--- lib/written/app/assets/javascripts/written/parsers/block/code.coffee
+++ lib/written/app/assets/javascripts/written/parsers/block/code.coffee
@@ -16,7 +16,7 @@
     @opened

   append: (text) ->
-    match = /(~{3})$/i.exec(text)
+    match = /^(~{3})$/i.exec(text)
     if match?
       @matches.push(match)
       @opened = false


fixes

fixes

--- src/Harentius/FolkprogBundle/Resources/public/js/common-folkprog.coffee
+++ src/Harentius/FolkprogBundle/Resources/public/js/common-folkprog.coffee
@@ -10,7 +10,7 @@
     return

   if $adsenceArticlesLoader.length > 0
-    (global.adsbygoogle = global.adsbygoogle || []).push({});
+    (adsbygoogle = global.adsbygoogle || []).push({});
     $adsenceArticlesLoader.html(
       '<ins class="adsbygoogle"
           style="display:inline-block;width:336px;height:280px"
@@ -20,7 +20,7 @@
     )

   if isShowCategoryAd && $adsenceCategoryLoader.length > 0
-    (global.adsbygoogle = global.adsbygoogle || []).push({});
+    (adsbygoogle = global.adsbygoogle || []).push({});
     $adsenceCategoryLoader.html(
       '<ins class="adsbygoogle"
          style="display:block"


Remove extra test line

Remove extra test line

--- mobile/apps/artwork/components/meta_data/test/template.coffee
+++ mobile/apps/artwork/components/meta_data/test/template.coffee
@@ -165,7 +165,6 @@

         $ = cheerio.load(html)
         $('.artwork-meta-data__partner .artwork-header').text().should.equal ""
-        $('.artwork-meta-data-black__contact-button').length.should.equal 0

       it 'should display collecting institution as link text', ->
         html = render('partner')(


Portuguese translation tweaks

Portuguese translation tweaks

--- @client/customizations.coffee
+++ @client/customizations.coffee
@@ -213,8 +213,8 @@
   pros: 'A Favor'
   con: 'Contra'
   cons: 'Contra'
-  your_header: "Entrega tu --valences--"
-  other_header: "Otros' --valences--"
+  your_header: "Teus pontos --valences--"
+  other_header: "Outros --valences--"
   top_header: "Top --valences--"

@@ -887,7 +887,7 @@
     lang: 'ptbr'
     point_labels : port_pros_cons
     slider_pole_labels : port_agree_disagree
-    homie_histo_title: "Opiniones"
+    homie_histo_title: "Opiniõnes"
     show_slider_feedback: false
     proposal_filters: false


fix save opinion text when logged in

fix save opinion text when logged in

--- @client/opinion_slider.coffee
+++ @client/opinion_slider.coffee
@@ -86,6 +86,7 @@
   saveYourOpinionNotice : ->
     your_opinion = fetch @props.your_opinion
     slider = fetch @props.key
+    current_user = fetch '/current_user'

     return SPAN null if (!TWO_COL() && customization('discussion', @proposal))  || \
                         ( your_opinion.published || \
@@ -106,7 +107,13 @@
       cursor: 'pointer'
       color: focus_blue

-    s = sizeWhenRendered t('login_to_save_opinion'), style
+    notice = if current_user.logged_in
+               t('save_your_opinion')
+             else
+               t('login_to_save_opinion')
+
+    s = sizeWhenRendered notice, style
+

     DIV
       style:
@@ -120,7 +127,9 @@

         onClick: => saveOpinion(@proposal)

-        t('login_to_save_opinion')
+        notice
+
+

   drawPoleLabels: ->


Made alerts fade away after a while

Made alerts fade away after a while

--- app/assets/javascripts/bootstrap.js.coffee
+++ app/assets/javascripts/bootstrap.js.coffee
@@ -7,3 +7,6 @@
     format: 'mm/dd/yyyy',
 		todayHighlight: true
   )
+  setTimeout ->
+    $(".alert").fadeOut(500)
+  , 2000


Add control option

Add control option

--- app/assets/javascripts/client/index.coffee
+++ app/assets/javascripts/client/index.coffee
@@ -81,7 +81,7 @@
     @variants.push(name: name, callback: callback, control: true)
     this

-  start: (name) =>
+  start: (name, options = {}) =>
     if previous = @getVariantCookie()
       # Use the same variant as before, don't record anything
       variant = (v for v in @variants when v.name is previous)[0]
@@ -89,16 +89,18 @@
     else if name?
       # Custom variant provided
       variant = (v for v in @variants when v.name is name)[0]
-      variant or= name: name
+      variant or=
+        name:    name
+        control: options.control
       @recordStart(variant)

     else
       # Or choose a random one
-      random ?= Math.floor(Math.random() * @variants.length)
+      random  = Math.floor(Math.random() * @variants.length)
       variant = @variants[random]
+
+      throw new Error('No variants added') unless variant
       @recordStart(variant)
-
-    throw new Error('No valid variant') unless variant

     variant?.callback?()
     @chosen = variant


update format

update format

--- app/assets/javascripts/dashboard.js.coffee
+++ app/assets/javascripts/dashboard.js.coffee
@@ -25,7 +25,7 @@
         plotShadow: false
         type: 'pie'
       title: text: '入境國家比例'
-      tooltip: pointFormat: '{series.name}: {point.percentage:.1f}%'
+      tooltip: pointFormat: '{series.name}: {point.percentage:.1f}% ({point.y})'
       plotOptions: pie:
         allowPointSelect: true
         cursor: 'pointer'
@@ -43,7 +43,7 @@
         plotShadow: false
         type: 'pie'
       title: text: '出境國家比例'
-      tooltip: pointFormat: '{series.name}: {point.percentage:.1f}%'
+      tooltip: pointFormat: '{series.name}: {point.percentage:.1f}% ({point.y})'
       plotOptions: pie:
         allowPointSelect: true
         cursor: 'pointer'


without collaborator URLs interactives fail

[#132185691]

--- app/assets/javascripts/iframe-saver.coffee
+++ app/assets/javascripts/iframe-saver.coffee
@@ -141,7 +141,7 @@
         hasLinkedInteractive: response?.has_linked_interactive or false
         linkedState: if response?.linked_state then JSON.parse(response.linked_state) else null
         interactiveStateUrl: @interactive_run_state_url
-        collaboratorUrls: @collaborator_urls.split(';')
+        collaboratorUrls: if @collaborator_urls? then @collaborator_urls.split(';') else null

     $.ajax
       url: @interactive_run_state_url


OX-729 started panZoom for svg

OX-729 started panZoom for svg

--- app/assets/javascripts/main/routing.coffee
+++ app/assets/javascripts/main/routing.coffee
@@ -3,6 +3,7 @@
 underscore : _
 libs/toast : Toast
 libs/keyboard : KeyboardJS
+libs/input : Input
 main/routing_utils : RoutingUtils
 ###

@@ -106,7 +107,43 @@
             $svg[0].setAttribute("height", "100%")
             $svg.css("max-width", "100%")

-            $svg.svgPan("graph1")
+            #$svg.svgPan("graph1")
+            panZoom = (delta, position) ->
+
+              svgElement = $svg[0]
+
+              if position
+
+                mouse =
+                  x: position[0] - $svg.offset().left
+                  y: position[1] - $svg.offset().top
+
+              else
+
+                mouse =
+                  x: $svg.width() / 2
+                  y: $svg.height() / 2
+
+              scale = 1 #zoomLevel / @oldZoomLevel
+
+              p = svgElement.createSVGPoint()
+              p.x = mouse.x
+              p.y = mouse.y
+
+              p.matrixTransform(@transformationGroup.getCTM().inverse())
+
+              transformationMatrix = svgElement.createSVGMatrix()
+                .translate(p.x, p.y)
+                .scale(scale)
+                .translate(-p.x, -p.y)
+
+              matrix = svgElement.getCTM().multiply(transformationMatrix)
+              matrixString = "#{matrix.a} #{matrix.b} #{matrix.c} #{matrix.d} #{matrix.e} #{matrix.f}"
+              $svg.attr("transform", "matrix(#{matrixString})")
+              #@oldZoomLevel = zoomLevel
+
+            mouse = new Input.Mouse($svg)
+            mouse.on("leftDownMove", panZoom)

           (error) ->
             $(".graph").html(" #{error.replace(/\n/g,"
")}")


Fixed filter buttons functionality

Fixed filter buttons functionality

--- app/assets/javascripts/restrooms.js.coffee
+++ app/assets/javascripts/restrooms.js.coffee
@@ -3,22 +3,15 @@
 # You can use CoffeeScript in this file: http://coffeescript.org/

 $ ->
-  $('#ada_filter .filter_off').click ->
-    $('#ada_filter .filter_off').hide()
-    $('#ada_filter .filter_on').show()
-    $('.listItem.not_accessible').hide()
+  $('#ada_filter').click ->
+    if $(this).hasClass("active")
+      $('.listItem.not_accessible').show()
+    else
+      $('.listItem.not_accessible').hide()

-  $('#ada_filter .filter_on').click ->
-    $('#ada_filter .filter_on').hide()
-    $('#ada_filter .filter_off').show()
-    $('.listItem.not_accessible').show()
-
-  $('#unisex_filter .filter_off').click ->
-    $('#unisex_filter .filter_off').hide()
-    $('#unisex_filter .filter_on').show()
-    $('.listItem.not_unisex').hide()
-
-  $('#unisex_filter .filter_on').click ->
-    $('#unisex_filter .filter_on').hide()
-    $('#unisex_filter .filter_off').show()
-    $('.listItem.not_unisex').show()
+  $('#unisex_filter').click ->
+    if $(this).hasClass("active")
+      $('.listItem.not_unisex').show()
+    else
+      $('.listItem.not_unisex').hide()
+


Register handler on document, that way we don’t have to wait for DOM load

--- app/assets/javascripts/views/header.coffee
+++ app/assets/javascripts/views/header.coffee
@@ -1,9 +1,4 @@
-ready = ->
-  $('body').on 'click', (e)->
-    unless $(e.target).parents('.dropdown').length > 0 || $(e.target).hasClass('dropdown')
-      $('.menu-selector').prop('checked', false)
-
-
-
-$(document).on 'ready', ready
-$(document).on 'page:load', ready
+$(document).on('click', '.dropdown', (e)->
+  unless $(e.target).parents('.dropdown').length > 0 || $(e.target).hasClass('dropdown')
+    $('.menu-selector').prop('checked', false)
+)


apply invalid filters

apply invalid filters

--- app/coffee/modules/controllerMixins.coffee
+++ app/coffee/modules/controllerMixins.coffee
@@ -137,7 +137,18 @@
         selectedFilters = _.filter list, (it) ->
             selectedIds.indexOf(_.toString(it.id)) != -1

-        return _.map selectedFilters, (it) ->
+        invalidTags = _.filter selectedIds, (it) ->
+            return !_.find selectedFilters, (sit) -> _.toString(sit.id) == it
+
+        invalidAppliedTags =  _.map invalidTags, (it) ->
+            return {
+                id: it
+                key: type + ":" + it
+                dataType: type,
+                name: it
+            }
+
+        validAppliedTags = _.map selectedFilters, (it) ->
             return {
                 id: it.id
                 key: type + ":" + it.id
@@ -145,6 +156,8 @@
                 name: it.name
                 color: it.color
             }
+
+        return invalidAppliedTags.concat(validAppliedTags)

 taiga.FiltersMixin = FiltersMixin


Testing if CO_BOXSERVER_DIR is null

Testing if CO_BOXSERVER_DIR is null

--- util/migrateBoxToServer.coffee
+++ util/migrateBoxToServer.coffee
@@ -25,6 +25,8 @@

 BOX_NAME = argv.box
 NEW_BOX_SERVER = argv.host
+
+process.env.CO_STORAGE_DIR?= ""

 if typeof argv.box isnt "string"
   console.log "Box name not specified"


metrics of different types can clash, share promMetrics

--- libraries/metrics/metrics.coffee
+++ libraries/metrics/metrics.coffee
@@ -11,9 +11,7 @@
 buildKey = (key)-> "#{name}.#{hostname}.#{key}"
 buildGlobalKey = (key)-> "#{name}.global.#{key}"

-counters = {}
-gauges = {}
-summaries = {}
+promMetrics = {}

 destructors = []

@@ -45,13 +43,13 @@
 	inc : (key, sampleRate = 1)->
 		statsd.increment buildKey(key), sampleRate
 		key = this.sanitizeKey(key)
-		if !counters[key]?
-			counters[key] = new prom.Counter({
-		  		name: "#{name}_#{key}",
+		if !promMetrics[key]?
+			promMetrics[key] = new prom.Counter({
+				name: "#{name}_#{key}",
 				help: key,
 				labelNames: ['name','host']
 			})
-		counters[key].inc({name: name, host: hostname})
+		promMetrics[key].inc({name: name, host: hostname})

 	count : (key, count, sampleRate = 1)->
 		statsd.count buildKey(key), count, sampleRate
@@ -68,37 +66,37 @@
 		done:->
 			timeSpan = new Date - this.start
 			statsd.timing(buildKey(this.key), timeSpan, this.sampleRate)
-			if !summaries[this.key]?
-				summary = new prom.Summary({
-					name: "#{name}_#{this.key}",
+			if !promMetrics[this.key]
+				promMetrics[this.key] = new prom.Summary({
+					name: "#{name}_timer_#{this.key}".replace(/\./g,"_"),
 					help: this.key,
 					maxAgeSeconds: 600,
 					ageBuckets: 10
 				})
-			summaries[this.key].observe(timeSpan)
+			promMetrics[this.key].observe(timeSpan)
 			return timeSpan

 	gauge : (key, value, sampleRate = 1)->
 		statsd.gauge buildKey(key), value, sampleRate
 		key = this.sanitizeKey(key)
-		if !gauges[key]?
-			gauges[key] = new prom.Gauge({
+		if !promMetrics[key]
+			promMetrics[key] = new prom.Gauge({
 				name: "#{name}_#{key}",
 				help: key,
 				labelNames: ['name','host']
 			})
-		gauges[key].set({name: name, host: hostname}, this.sanitizeValue(value))
+		promMetrics[key].set({name: name, host: hostname}, this.sanitizeValue(value))

 	globalGauge: (key, value, sampleRate = 1)->
 		statsd.gauge buildGlobalKey(key), value, sampleRate
 		key = this.sanitizeKey(key)
-		if !gauges[key]
-			gauges[key] = new prom.Gauge({
-		  		name: "#{name}_#{key}",
+		if !promMetrics[key]
+			promMetrics[key] = new prom.Gauge({
+				name: "#{name}_#{key}",
 				help: key,
 				labelNames: ['name','host']
 			})
-		gauges[key].set({name: name},this.sanitizeValue(value))
+		promMetrics[key].set({name: name},this.sanitizeValue(value))

 	mongodb: require "./mongodb"
 	http: require "./http"


Update fix conflicts

Update fix conflicts

--- src/coffee/flight.coffee
+++ src/coffee/flight.coffee
@@ -7,7 +7,7 @@
   os: ''
   iScroll: null
   moved: false
-  theTarget: {}
+  theTarget: null

   isTransitioning: false
   menuOpen: false
@@ -33,13 +33,24 @@
     @mainMenu = document.querySelector @mainMenu if typeof @mainMenu is "string"

     @detectUserAgent()
-
+
     @transitionAnimation = false if @os.android and @os.version <= '2.1'

     @hideUrlBar() if options.hideUrlbar

-    document.body.addEventListener('touchstart', @handleEvents, false)
-    document.body.addEventListener('mousedown', @handleEvents, false)
+    if @isTouch()
+      document.body.addEventListener 'touchstart', @handleEvents, false
+    else
+      document.body.addEventListener 'mousedown', @handleEvents, false
+
+  # Private: Is the device touch enabled.
+  #
+  # Returns True if the device is touch enabled, else False.
+  isTouch: =>
+    if @os.android
+      !!('ontouchstart' of window)
+    else
+      window.Touch?

   # Public: Go to a specific page.
   #
@@ -47,7 +58,7 @@
   #
   # Returns nothing.
   goto: (targetPage) =>
-    if @menuOpen is true
+    if @menuOpen is true
       @closeMenu()

     if typeof targetPage is "string"
@@ -55,8 +66,9 @@
     else if targetPage
       @targetPage = targetPage

-    @iScroll = new iScroll(@targetPage.querySelector('.scrollview-inner')) if @os.android and @os.version < '4'
-
+    if @os.android and @os.version < '4'
+      @iScroll = new iScroll @targetPage.querySelector('.scrollview-inner')
+
     unless @currentPage
       @targetPage.style.display = "-webkit-box"
       @pageHistory = [window.location.hash]
@@ -73,14 +85,14 @@

     if @pageHistory.length > 1 and window.location.hash is @pageHistory[@pageHistory.length - 2]
       @back = true
-
+
     if @back and @pageHistory.length != 1
       transitionType = @currentPage.getAttribute("data-transition") or 'slide'
       @pageHistory.pop()
     else
       transitionType = @targetPage.getAttribute("data-transition") or 'slide'
-      @pageHistory.push(window.location.hash)
-
+      @pageHistory.push window.location.hash
+
     targetPage = @targetPage
     currentPage = @currentPage
     @currentPage = @targetPage
@@ -89,11 +101,12 @@
     window.setTimeout =>
       if @transitionAnimation
         switch transitionType
-          when "slide" then @slide(targetPage, currentPage)
-          when "slideUp" then @slideUp(targetPage, currentPage)
+          when "slide"
+            @slide targetPage, currentPage
+          when "slideUp"
+            @slideUp targetPage, currentPage
       else
-        @displayPage(targetPage, currentPage)
-
+        @displayPage targetPage, currentPage
     , 10

   # Private: Perform a slide transition.
@@ -106,18 +119,20 @@
     screenWidth = window.innerWidth + 'px'

     if @back
-      @translate(targetPage, "X", "-" + screenWidth, "0ms")
-      window.setTimeout =>
-        @translate(currentPage, "X", "100%")
+      @translate targetPage, "X", "-" + screenWidth, "0ms"
+
+      window.setTimeout =>
+        @translate currentPage, "X", "100%"
       , 0
     else
-      @translate(targetPage,"X", screenWidth, "0ms")
-      window.setTimeout =>
-        @translate(currentPage, "X", "-100%")
+      @translate targetPage,"X", screenWidth, "0ms"
+
+      window.setTimeout =>
+        @translate currentPage, "X", "-100%"
       , 0

     window.setTimeout =>
-      @translate(targetPage, "X", "0%")
+      @translate targetPage, "X", "0%"
       @back = false
     , 0

@@ -160,17 +175,21 @@
   slideOutMenu: ->
     if @menuOpen
       window.setTimeout =>
-        @translate(@mainMenu, "X", "-110%", "0.3s")
-      ,10
-      window.setTimeout =>
-        @mainMenu.style.display = "none"
-      ,300
+        @translate @mainMenu, "X", "-110%", "0.3s"
+      , 10
+
+      window.setTimeout =>
+        @mainMenu.style.display = "none"
+      , 300
+
       @menuOpen = false
     else
-      @translate(@mainMenu, "X", "-110%", "0ms")
-      window.setTimeout =>
-        @translate(@mainMenu, "X", "0%", "0.3s")
-      ,10
+      @translate @mainMenu, "X", "-110%", "0ms"
+
+      window.setTimeout =>
+        @translate @mainMenu, "X", "0%", "0.3s"
+      , 10
+
       @mainMenu.style.display = "block"
       @menuOpen = true

@@ -178,7 +197,7 @@
   #
   # Returns nothing.
   closeMenu: ->
-    @mainMenu.style.display = "none"
+    @mainMenu.style.display = "none"
     @menuOpen = false

   # Private: Translate page on a specified axis.
@@ -227,7 +246,7 @@
     @os = os

   # Private: Hide the URL bar in mobile browsers.
-  #
+  #
   # Returns nothing.
   hideUrlBar: ->
     setTimeout ->
@@ -235,29 +254,38 @@
     , 50

   handleEvents: (e) =>
-    switch e.type
-      when 'touchstart' then @onTouchStart(e)
-      when 'mousedown' then @onTouchStart(e)
-      when 'touchmove' then @onTouchMove(e)
-      when 'touchend' then @onTouchEnd(e)
+    if @isTouch()
+      switch e.type
+        when 'touchstart'
+          @onTouchStart e
+        when 'touchmove'
+          @onTouchMove e
+        when 'touchend'
+          @onTouchEnd e
+    else
+      switch e.type
+        when 'mousedown'
+          @onTouchStart e

   onTouchStart: (e) =>
     @moved = false
-
-    if window.Touch
+
+    if @isTouch()
       @theTarget = document.elementFromPoint(e.targetTouches[0].clientX, e.targetTouches[0].clientY)
     else
       @theTarget = document.elementFromPoint e.clientX, e.clientY

     if @theTarget.nodeName and @theTarget.nodeName.toLowerCase() isnt 'a' and (@theTarget.nodeType is 3 or @theTarget.nodeType is 1)
-      @theTarget = @theTarget.parentNode
-
+      @oldTarget = @theTarget
+      @parents = $(@theTarget).parentsUntil('ul li')
+      @theTarget = @parents[@parents.length-1] or @oldTarget
+
     @theTarget.className+= ' pressed'
-    @theTarget.addEventListener('touchmove', @onTouchMove, false)
-    @theTarget.addEventListener('mouseout', @onTouchEnd, false)
-    @theTarget.addEventListener('touchend', @onTouchEnd, false)
-    @theTarget.addEventListener('mouseup', @onTouchEnd, false)
-    @theTarget.addEventListener('touchcancel', @onTouchcancel, false)
+    @theTarget.addEventListener 'touchmove', @onTouchMove, false
+    @theTarget.addEventListener 'mouseout', @onTouchEnd, false
+    @theTarget.addEventListener 'touchend', @onTouchEnd, false
+    @theTarget.addEventListener 'mouseup', @onTouchEnd, false
+    @theTarget.addEventListener 'touchcancel', @onTouchcancel, false

   onTouchMove: (e) =>
     @moved = true


fix the scoping when calling renderer

fix the scoping when calling renderer

--- library/exposure/pinpoint.coffee
+++ library/exposure/pinpoint.coffee
@@ -102,7 +102,7 @@
             try logger.info pe, moved.length, this.service
             $(nod).data owners: go(nod) for nod in moved
             go(nod).push this for nod in moved or Array()
-            _.each moved, (nod) -> renderer nod, go(nod)
+            _.each moved, (n) => renderer.call @, n, go n

     # Pinpoint when the specified selector vanishes (is removed or
     # detach) and then invoke the supplied rendering function, which
@@ -124,7 +124,7 @@
             try logger.info pe, moved.length, this.service
             $(nod).data owners: go(nod) for nod in moved
             go(nod).push this for nod in moved or Array()
-            _.each moved, (nod) -> renderer nod, go(nod)
+            _.each moved, (n) => renderer.call @, n, go n

     # Pinpoint when the specified selector appears (or if it already
     # exists) and then invoke the supplied rendering function, which
@@ -146,4 +146,4 @@
             try logger.info pe, added.length, this.service
             $(nod).data owners: go(nod) for nod in added
             go(nod).push this for nod in added or Array()
-            _.each added, (nod) -> renderer nod, go(nod)
+            _.each added, (n) => renderer.call @, n, go n


Fixed document handler functions now returns result

--- src/document/input/prop.coffee
+++ src/document/input/prop.coffee
@@ -59,8 +59,8 @@
                 return
             r = input.bindingConfig.func.apply input, input.file.inputArgs
             if typeof r is 'function'
-                r.apply @, arguments
-            return
+                r = r.apply @, arguments
+            r

     clone: (original, file) ->
         node = original.node.getCopiedElement @node, file.node


Update es translation for about.cson

Update es translation for about.cson

--- def/es/about.cson
+++ def/es/about.cson
@@ -1,9 +1,9 @@
 About:
   tabTitle: "Acerca de"
   releaseNotes: "Notas de la Version"
-  newUpdate: "New Update"
-  automaticDownloadUpdates: "Automatic download updates"
-  restartInstall: "Restart and install"
+  newUpdate: "Nueva Actualización"
+  automaticDownloadUpdates: "Descargar actualizaciones automáticamente"
+  restartInstall: "Reiniciar e instalar"
   license: "Licencia"
   termsOfUse: "Términos de Uso"
   with: " con "


Put callback in state

Put callback in state

--- core/app/backbone/views/popovers/react_signin_popover.coffee
+++ core/app/backbone/views/popovers/react_signin_popover.coffee
@@ -1,6 +1,7 @@
 window.ReactSigninPopover = React.createClass
   getInitialState: ->
     opened: false
+    callback: null

   componentDidMount: ->
     window.currentUser.on 'change:username', @_onSignedInChange, @
@@ -13,12 +14,12 @@
   _onOtherPopoverOpened: (popover) ->
     return if popover == this

-    @setState opened: false
+    @setState opened: false, callback: null

   _onSignedInChange: ->
     if FactlinkApp.signedIn() && @state.opened
-      @_callback()
-      @setState opened: false
+      @state.callback()
+      @setState opened: false, callback: null

     @forceUpdate()

@@ -26,11 +27,10 @@
     if FactlinkApp.signedIn()
       callback()
     else
-      opened = !@state.opened
-      @setState opened: opened
-
-      if opened
-        @_callback = callback
+      if @state.opened
+        @setState opened: false, callback: null
+      else
+        @setState opened: true, callback: callback
         FactlinkApp.vent.trigger 'ReactSigninPopover:opened', this

   render: ->


Fix typo

Fix typo

--- views/env.coffee
+++ views/env.coffee
@@ -420,7 +420,7 @@
           postBody: postBody
       window.dispatchEvent event
     catch err
-      console.error e
+      console.error err
   locked = false
 proxy.addListener 'game.on.response', (method, path, body, postBody) ->
   # Invalid response


Video.js: hide volume controls

Video.js: hide volume controls

--- app/scripts/common/directives/video.coffee
+++ app/scripts/common/directives/video.coffee
@@ -69,7 +69,7 @@
           mp4src = scope.videoData.mp4.toString()
           if Modernizr.video.h264 and scope.videoData.mp4_low
             mp4src = scope.videoData.mp4_low
-        player = videojs iElement.attr("id"), {}, ->
+        player = videojs iElement.attr("id"), {controlBar: {volumeMenuButton: false}}, ->
           player.poster scope.videoData.poster
           player.src(
             {type: "video/mp4", src: mp4src}


Update names after preprocessing.

Update names after preprocessing.

--- core/htdocs_source/src/pods/oxisection-form/component.coffee
+++ core/htdocs_source/src/pods/oxisection-form/component.coffee
@@ -24,15 +24,15 @@
             if clonable.name not in names
                 names.push clonable.name

+        for name in names
+            clones = (f for f in fields when f.name is name)
+            Em.set clone, "isLast", false for clone in clones
+            Em.set clones[clones.length-1], "isLast", true
+
         for field in fields
             if field.value and typeof field.value is "object"
                 field.name = field.value.key
                 field.value = field.value.value
-
-        for name in names
-            clones = (f for f in fields when f.name is name)
-            Em.set clone, "isLast", false for clone in clones
-            Em.set clones[clones.length-1], "isLast", true

         fields


Remove title which is not commonly used, add inputName to use it in forms

--- client/Framework/components/inputs/KDMultipleChoice.coffee
+++ client/Framework/components/inputs/KDMultipleChoice.coffee
@@ -36,8 +36,8 @@
     @setValue options.defaultValue, no

   setDomElement:(cssClass)->
-    {title, labels, name} = @getOptions()
-    title = if title then "#{title}" else ''
+    {labels, name} = @getOptions()
+    @inputName = name

     labelItems = ""
     for label in labels
@@ -45,7 +45,6 @@

     @domElement = $ """

-        #{title}
         #{labelItems}

 """


default opentsdb aggregation, allow strings instead of objects

--- src/opentsdb.coffee
+++ src/opentsdb.coffee
@@ -8,8 +8,11 @@
     fn 'tsd', 'Fetches time series data from OpenTSDB', (args...) ->
       @value opentsdb.tsd args...

-    to_metric_string: ({aggregation, metric_name, downsample, tags}) ->
-      parts = [aggregation]
+    to_metric_string: (time_series) ->
+      if _.isString time_series
+        time_series = metric_name: time_series
+      {aggregation, metric_name, downsample, tags} = time_series
+      parts = [aggregation ? 'sum']
       parts.push "#{downsample.period}-#{downsample.aggregation}" if downsample
       parts.push metric_name + '{' + _.map(tags, (tagv, tagk) -> "#{tagk}=#{tagv}").join(',') + '}'
       parts.join ':'


Troubleshoot: instead of listening events VMChecker fetches the vm states from kiteList

--- client/Main/troubleshoot/vmchecker.coffee
+++ client/Main/troubleshoot/vmchecker.coffee
@@ -1,64 +1,19 @@
 class VMChecker extends KDObject
-  [NOTWORKING, READY, RUNNING] = [0..2]

   constructor: (options, data) ->
     super options, data

-    @vmList = {}
-    @status = NOTWORKING
+  healthCheck: (callback) ->
+    status = "success"
+    {kites} = KD.singletons.vmController
+    for own alias, kite of kites
+      switch kite.recentState.state
+        when 'RUNNING'
+          continue
+        when 'FAILED'
+          status = "fail"
+          return callback {status}
+        when "STOPPED"
+          status = "pending"

-    @resetTimeout()
-
-    {vmController} = KD.singletons
-    vmController.on 'vm.state.info', ({alias, state})  =>
-      switch state.state
-        when "RUNNING"
-          @vmList[alias] = "on"
-        when "STOPPED"
-          @vmList[alias] = "off"
-      @updateStatus()
-
-    vmController.on 'vm.progress.start', ({alias, update}) =>
-      {message} = update
-      switch message
-        when "FINISHED"
-          @vmList[alias] = "on"
-        when "STARTED"
-          @vmList[alias] = "starting"
-      @updateStatus()
-
-    vmController.on 'vm.progress.stop', ({alias, update}) =>
-      {message} = update
-      switch message
-        when "FINISHED"
-          @vmList[alias] = "off"
-        when "STARTED"
-          @vmList[alias] = "stopping"
-      @updateStatus()
-
-
-  resetTimeout: ->
-    clearTimeout @timer
-    @timer = KD.utils.wait KD.config.osKitePollingMs, =>
-      @status = NOTWORKING
-
-
-  updateStatus: ->
-    @status = RUNNING
-    for own name, status of @vmList
-      switch status
-        when "on"
-          continue
-        else
-          @status = READY
-
-
-  healthCheck: (callback) ->
-    status = switch @status
-      when NOTWORKING
-        "fail"
-      when READY
-        "pending"
-      when RUNNING
-        "success"
     callback {status}


Minor Change

Minor Change

--- client/Social/Topics/AppController.coffee
+++ client/Social/Topics/AppController.coffee
@@ -22,7 +22,7 @@
     @controllers = {}

   createFeed:(view, loadFeed = no)->
-    JTag = new KD.remote.api.JTag()
+    {JTag} = KD.remote.api.JTag
     KD.getSingleton("appManager").tell 'Feeder', 'createContentFeedController', {
       feedId                : 'topics.main'
       itemClass             : @listItemClass


Remove unused sprites child

Remove unused sprites child

--- src/config/layout.coffee
+++ src/config/layout.coffee
@@ -11,6 +11,5 @@
   _CHILDREN: [
     controls
     grid
-#    sprites
   ]
 }


Add support for reordering selected columns

This is a tad hackish feeling because the sort* events are not
triggered on the items themselves, only the container. In order to
make the view for the item available, the event is triggered on the
item to it is caught by the view to be handled.

--- coffee/cilantro/ui/concept/columns.coffee
+++ coffee/cilantro/ui/concept/columns.coffee
@@ -110,11 +110,21 @@

         events:
             'click button': 'triggerRemove'
+            'sortupdate': 'updateIndex'

         triggerRemove: (event) ->
             event.preventDefault()
             @model.trigger 'columns:remove', @model

+        # Updates the index of the model within the collection relative
+        # DOM index
+        updateIndex: (event, index) ->
+            event.stopPropagation()
+            collection = @model.collection
+            # Silently move the model to a new index
+            collection.remove(@model, silent: true)
+            collection.add(@model, at: index, silent: true)
+

     class SelectedColumns extends c.Marionette.CollectionView
         tagName: 'ul'
@@ -122,12 +132,21 @@
         className: 'selected-columns'

         itemView: SelectedItem
+
+        events:
+            'sortupdate': 'triggerItemSort'

         initialize: ->
             @$el.sortable
                 cursor: 'move'
                 forcePlaceholderSize: true
                 placeholder: 'placeholder'
+
+        # 'Sortable' events are not triggered on the item being sorted
+        # so this handles proxying the event to the item itself.
+        # See the SelectedItem handler for the event above.
+        triggerItemSort: (event, ui) ->
+            ui.item.trigger(event, ui.item.index())

     class ConceptColumns extends c.Marionette.Layout


Use JSON.stringify in soft-wrap assertions

This increases the output clarity for invisible
characters such as `\n`.

--- spec/display-layer-spec.coffee
+++ spec/display-layer-spec.coffee
@@ -237,48 +237,48 @@

       expect(displayLayer.getText()).toBe 'abc\ndef\nghi\nj'

-  describe "soft wraps", ->
+  fdescribe "soft wraps", ->
     it "soft wraps the line at the rightmost word boundary at or preceding the softWrapColumn", ->
       buffer = new TextBuffer(text: 'abc def ghi jkl mno')
       displayLayer = buffer.addDisplayLayer(softWrapColumn: 7)
-      expect(displayLayer.getText()).toBe 'abc def \nghi jkl \nmno'
+      expect(JSON.stringify(displayLayer.getText())).toBe JSON.stringify('abc def\nghi jkl\nmno')

       buffer = new TextBuffer(text: 'abc defg hijkl mno')
       displayLayer = buffer.addDisplayLayer(softWrapColumn: 7)
-      expect(displayLayer.getText()).toBe 'abc \ndefg \nhijkl \nmno'
+      expect(JSON.stringify(displayLayer.getText())).toBe JSON.stringify('abc\ndefg\nhijkl\nmno')

     it "soft wraps the line at the softWrapColumn if no word boundary precedes it", ->
       buffer = new TextBuffer(text: 'abcdefghijklmno')
       displayLayer = buffer.addDisplayLayer(softWrapColumn: 7)
-      expect(displayLayer.getText()).toBe 'abcdefg\nhijklmn\no'
+      expect(JSON.stringify(displayLayer.getText())).toBe JSON.stringify('abcdefg\nhijklmn\no')

     it "preserves the indent on wrapped segments of the line", ->
       buffer = new TextBuffer(text: '   abc de fghi jkl')
       displayLayer = buffer.addDisplayLayer(softWrapColumn: 7)
-      expect(displayLayer.getText()).toBe '   abc \n   de \n   fghi \n   jkl'
+      expect(JSON.stringify(displayLayer.getText())).toBe JSON.stringify('   abc\n   de\n   fghi\n   jkl')

     it "ignores indents that are greater than or equal to the softWrapColumn", ->
       buffer = new TextBuffer(text: '       abcde fghijk')
       displayLayer = buffer.addDisplayLayer(softWrapColumn: 7)
-      expect(displayLayer.getText()).toBe '       \nabcde \nfghijk'
+      expect(JSON.stringify(displayLayer.getText())).toBe JSON.stringify('       \nabcde\nfghijk')

     it "honors the softWrapHangingIndent setting", ->
       buffer = new TextBuffer(text: 'abcdef ghi')
       displayLayer = buffer.addDisplayLayer(softWrapColumn: 7, softWrapHangingIndent: 2)
-      expect(displayLayer.getText()).toBe 'abcdef \n  ghi'
+      expect(JSON.stringify(displayLayer.getText())).toBe JSON.stringify('abcdef\n  ghi')

       buffer = new TextBuffer(text: '   abc de fghi jk')
       displayLayer = buffer.addDisplayLayer(softWrapColumn: 7, softWrapHangingIndent: 2)
-      expect(displayLayer.getText()).toBe '   abc \n     de \n     fg\n     hi \n     jk'
+      expect(JSON.stringify(displayLayer.getText())).toBe JSON.stringify('   abc\n     de\n     fg\n     hi\n     jk')

       buffer = new TextBuffer(text: '       abcde fghijk')
       displayLayer = buffer.addDisplayLayer(softWrapColumn: 7, softWrapHangingIndent: 2)
-      expect(displayLayer.getText()).toBe '       \n  abcde \n  fghij\n  k'
-
-    ffit "translates points correctly on soft-wrapped lines", ->
+      expect(JSON.stringify(displayLayer.getText())).toBe JSON.stringify('       \n  abcde\n  fghij\n  k')
+
+    it "translates points correctly on soft-wrapped lines", ->
       buffer = new TextBuffer(text: '   abc de fghi')
       displayLayer = buffer.addDisplayLayer(softWrapColumn: 7, softWrapHangingIndent: 2)
-      expect(displayLayer.getText()).toBe '   abc\n     de\n     fg\n     hi'
+      expect(JSON.stringify(displayLayer.getText())).toBe JSON.stringify('   abc\n     de\n     fg\n     hi')

       expectPositionTranslations(displayLayer, [
         [Point(0, 0), Point(0, 0)],


Move specs back to original position

Move specs back to original position

--- spec/notifications-spec.coffee
+++ spec/notifications-spec.coffee
@@ -166,16 +166,6 @@
           notificationContainer = workspaceElement.querySelector('atom-notifications')
           fatalError = notificationContainer.querySelector('atom-notification.fatal')

-        it "contains core and notifications config values", ->
-          atom.config.set('notifications.something', 10)
-          waitsForPromise ->
-            fatalError.getRenderPromise().then -> issueBody = fatalError.issue.issueBody
-
-          runs ->
-            expect(issueBody).toContain '"core":'
-            expect(issueBody).toContain '"notifications":'
-            expect(issueBody).not.toContain '"editor":'
-
         it "displays a fatal error with the package name in the error", ->
           waitsForPromise ->
             fatalError.getRenderPromise().then -> issueBody = fatalError.issue.issueBody
@@ -200,6 +190,16 @@

             # FIXME: this doesnt work on the test server. `apm ls` is not working for some reason.
             # expect(issueBody).toContain 'notifications, v'
+
+        it "contains core and notifications config values", ->
+          atom.config.set('notifications.something', 10)
+          waitsForPromise ->
+            fatalError.getRenderPromise().then -> issueBody = fatalError.issue.issueBody
+
+          runs ->
+            expect(issueBody).toContain '"core":'
+            expect(issueBody).toContain '"notifications":'
+            expect(issueBody).not.toContain '"editor":'

       describe "when an exception is thrown from core", ->
         beforeEach ->


Test inferrence mechanism of the ancestor node

--- spec/chattifier.spec.coffee
+++ spec/chattifier.spec.coffee
@@ -1,21 +1,69 @@
 jsdom = require("jsdom").jsdom
 Chattifier = require "../src/chattifier.coffee"

-describe "A suite of Chattifier tests", ->
+describe "A suite of Chattifier tests:", ->
   chattifier = {}
   document = {}
   node = {}

   beforeEach ->
     # create mock documentument
-    document = jsdom ''
+    document = jsdom ''
     # create mock node
     node = document.createElement 'div'
+    document.body.appendChild node
     # create the Chattifier and add the node
     chattifier = new Chattifier document
     chattifier.ancestorNode = node

-  describe "any existing hyperlink within the ancestor node", ->
+  describe "tests whether the immediate parent node of", ->
+    beforeEach ->
+      # create mock documentument
+      document = jsdom ''
+      # create mock node
+      node = document.createElement 'div'
+      node.className = 'the-parent'
+      document.body.appendChild node
+      # create the Chattifier and add the node
+      chattifier = new Chattifier document
+
+    it "topmost blockquote element is selected, or", ->
+      html = "

"
+      node.innerHTML = html
+
+      # run chattifier
+      chattifier.inferAncestorNode()
+
+      # test
+      expect(chattifier.ancestorNode.tagName.toLowerCase()).
+        toEqual "div"
+
+      expect(chattifier.ancestorNode.className.toLowerCase()).
+        toEqual "the-parent"
+
+    it "the parent of the element containing the text blockquotes of type >...>, or", ->
+      html = "
>> something\n>> something\n>> something
"
+      node.innerHTML = html
+
+      # run chattifier
+      chattifier.inferAncestorNode()
+
+      # test
+      expect(chattifier.ancestorNode.tagName.toLowerCase()).
+        toEqual "div"
+
+      expect(chattifier.ancestorNode.className.toLowerCase()).
+        toEqual "the-parent"
+
+    it "exits gracefully otherwise", ->
+      # run chattifier
+      chattifier.inferAncestorNode()
+
+      # test
+      expect(chattifier.ancestorNode).
+        toEqual null
+
+  describe "tests that any existing hyperlink within the ancestor node", ->
     it "should be escaped before parsing content", ->
       links = [
         'www.google.com',
@@ -73,7 +121,7 @@
     expect(node.innerHTML).
       toEqual html

-  describe "the HTML output should be grouped into divs regardless if", ->
+  describe "tests the HTML output should be grouped into divs regardless if", ->
     it "has a header element as the topmost element, or if", ->
       # create mock document of the following structure
       h = "h" + Chattifier.headerStartLevel


More onboarding tweaks

More onboarding tweaks

--- app.coffee
+++ app.coffee
@@ -95,10 +95,9 @@
         found: (conversation) -> processMessage(conversation)
         notFound: () ->
             createNewUser sender, (conversation) ->
-                sendSMS(sender, "how u doin bae? ;)")
                 sendSMS(sender, "[OPERATIONS MANUAL: Using complex nudity-detection algorithms, Sext Machine is programmed to feel arousal proportional to the likelihood a photo is X-rated.")
                 sendSMS(sender, "Without sending actual explicit photos, arouse the unit by taking and sending pictures with your phone camera that it believes to contain nudity.]")
-                sendSMS(sender, "ooh, baby. send me some hot pics. i want to be so aroused that i produce lubricant at an alarming rate. <3")
+                sendSMS(sender, "how u doin bae? ;)")

 app.listen process.env.PORT || 3000
 console.log "Listening on #{process.env.PORT || 3000}"


Use coffee script ranges instead of substring

--- app.coffee
+++ app.coffee
@@ -268,7 +268,7 @@
 		# loop over each file in the package folder
 		async.eachSeries files, (file, fileCallback) ->
 			# ignore dotfiles…
-			if (file.substring 0, 1) is '.'
+			if file[0] is '.'
 				debug "Skipping dotfile #{config.packageFolder}#{file}"
 				fileCallback null
 				return


Update xdomain url

Update xdomain url

--- app.coffee
+++ app.coffee
@@ -70,7 +70,7 @@
 app.get "/proxy.html", (req, res) ->
   res.send """
     <!DOCTYPE HTML>
-    <script src="//cdn.rawgit.com/jpillora/xdomain/gh-pages/dist/0.6/xdomain.min.js" master="#{wwwUrl}"></script>
+    <script src="https://cdn.rawgit.com/jpillora/xdomain/0.7.3/dist/xdomain.min.js" master="#{wwwUrl}"></script>
   """


Don't require ignore file

Don't require ignore file

--- app.coffee
+++ app.coffee
@@ -55,8 +55,11 @@
     _.indexOf(Ignored.files, file) >= 0

   loadIgnored: ->
-    files = fs.readFileSync '../commit_analyzer_ignore.txt', 'utf-8'
-    files = files.split('\n')
-    _.each files, (file) -> Ignored.files.push file
+    try
+      files = fs.readFileSync '../commit_analyzer_ignore.txt', 'utf-8'
+      files = files.split('\n')
+      _.each files, (file) -> Ignored.files.push file
+    catch err
+      console.log "\n\nunable to find commit_analyzer_ignore.txt file; not ignoring any files...\n\n"

 App.analyze()


lineForBufferRown -> lineTextForBufferRow

--- spec/toggle-quotes-spec.coffee
+++ spec/toggle-quotes-spec.coffee
@@ -33,56 +33,56 @@
       it "switches the quotes to double", ->
         editor.setCursorBufferPosition([4, 13])
         toggleQuotes(editor)
-        expect(editor.lineForBufferRow(4)).toBe 'console.log("");'
+        expect(editor.lineTextForBufferRow(4)).toBe 'console.log("");'
         expect(editor.getCursorBufferPosition()).toEqual [4, 13]

     describe "when the cursor is inside a double quoted string", ->
       it "switches the quotes to single", ->
         editor.setCursorBufferPosition([0, 16])
         toggleQuotes(editor)
-        expect(editor.lineForBufferRow(0)).toBe "console.log('Hello World');"
+        expect(editor.lineTextForBufferRow(0)).toBe "console.log('Hello World');"
         expect(editor.getCursorBufferPosition()).toEqual [0, 16]

     describe "when the cursor is inside a single quoted string", ->
       it "switches the quotes to double", ->
         editor.setCursorBufferPosition([1, 16])
         toggleQuotes(editor)
-        expect(editor.lineForBufferRow(1)).toBe 'console.log("Hello World");'
+        expect(editor.lineTextForBufferRow(1)).toBe 'console.log("Hello World");'
         expect(editor.getCursorBufferPosition()).toEqual [1, 16]

     describe "when the cursor is inside a single-quoted string that is nested within a double quoted string", ->
       it "switches the outer quotes to single and escapes the inner quotes", ->
         editor.setCursorBufferPosition([2, 22])
         toggleQuotes(editor)
-        expect(editor.lineForBufferRow(2)).toBe "console.log('Hello \\'World\\'');"
+        expect(editor.lineTextForBufferRow(2)).toBe "console.log('Hello \\'World\\'');"
         expect(editor.getCursorBufferPosition()).toEqual [2, 22]

         toggleQuotes(editor)
-        expect(editor.lineForBufferRow(2)).toBe 'console.log("Hello \'World\'");'
+        expect(editor.lineTextForBufferRow(2)).toBe 'console.log("Hello \'World\'");'

     describe "when the cursor is inside a double-quoted string that is nested within a single quoted string", ->
       it "switches the outer quotes to double and escapes the inner quotes", ->
         editor.setCursorBufferPosition([3, 22])
         toggleQuotes(editor)
-        expect(editor.lineForBufferRow(3)).toBe 'console.log("Hello \\"World\\"");'
+        expect(editor.lineTextForBufferRow(3)).toBe 'console.log("Hello \\"World\\"");'
         expect(editor.getCursorBufferPosition()).toEqual [3, 22]

         toggleQuotes(editor)
-        expect(editor.lineForBufferRow(3)).toBe "console.log('Hello \"World\"');"
+        expect(editor.lineTextForBufferRow(3)).toBe "console.log('Hello \"World\"');"

     describe "when the cursor is inside multiple quoted strings", ->
       it "switches the quotes of both quoted strings separately and leaves the cursors where they were, and does so atomically", ->
         editor.setCursorBufferPosition([0, 16])
         editor.addCursorAtBufferPosition([1, 16])
         toggleQuotes(editor)
-        expect(editor.lineForBufferRow(0)).toBe "console.log('Hello World');"
-        expect(editor.lineForBufferRow(1)).toBe 'console.log("Hello World");'
+        expect(editor.lineTextForBufferRow(0)).toBe "console.log('Hello World');"
+        expect(editor.lineTextForBufferRow(1)).toBe 'console.log("Hello World");'
         expect(editor.getCursors()[0].getBufferPosition()).toEqual [0, 16]
         expect(editor.getCursors()[1].getBufferPosition()).toEqual [1, 16]

         editor.undo()
-        expect(editor.lineForBufferRow(0)).toBe 'console.log("Hello World");'
-        expect(editor.lineForBufferRow(1)).toBe "console.log('Hello World');"
+        expect(editor.lineTextForBufferRow(0)).toBe 'console.log("Hello World");'
+        expect(editor.lineTextForBufferRow(1)).toBe "console.log('Hello World');"
         expect(editor.getCursors()[0].getBufferPosition()).toEqual [0, 16]
         expect(editor.getCursors()[1].getBufferPosition()).toEqual [1, 16]

@@ -125,12 +125,12 @@
       it "switches quotes to single excluding unicode character", ->
         editor.setCursorBufferPosition([0, 16])
         toggleQuotes(editor)
-        expect(editor.lineForBufferRow(0)).toBe "print(u'Hello World')"
+        expect(editor.lineTextForBufferRow(0)).toBe "print(u'Hello World')"
         expect(editor.getCursorBufferPosition()).toEqual [0, 16]

     describe "when cursor is inside an empty single quoted raw string", ->
       it "switches quotes to double", ->
         editor.setCursorBufferPosition([1, 8])
         toggleQuotes(editor)
-        expect(editor.lineForBufferRow(1)).toBe 'print(r"")'
+        expect(editor.lineTextForBufferRow(1)).toBe 'print(r"")'
         expect(editor.getCursorBufferPosition()).toEqual [1, 8]


Use `package` instead of `extension`

Use `package` instead of `extension`

--- spec/whitespace-spec.coffee
+++ spec/whitespace-spec.coffee
@@ -22,12 +22,12 @@
   it "strips trailing whitespace before an editor saves a buffer", ->
     atom.config.set("whitespace.ensureSingleTrailingNewline", false)

-    # works for buffers that are already open when extension is initialized
+    # works for buffers that are already open when package is initialized
     editor.insertText("foo   \nbar\t   \n\nbaz")
     editor.save()
     expect(editor.getText()).toBe "foo\nbar\n\nbaz"

-    # works for buffers that are opened after extension is initialized
+    # works for buffers that are opened after package is initialized
     editor = atom.project.openSync('sample.txt')
     editor.moveCursorToEndOfLine()
     editor.insertText("           ")


:lipstick: Call save on edit session instead of buffer

--- spec/whitespace-spec.coffee
+++ spec/whitespace-spec.coffee
@@ -25,7 +25,7 @@

     # works for buffers that are already open when extension is initialized
     editor.insertText("foo   \nbar\t   \n\nbaz")
-    editor.getBuffer().save()
+    editor.save()
     expect(editor.getText()).toBe "foo\nbar\n\nbaz"

     # works for buffers that are opened after extension is initialized
@@ -33,7 +33,7 @@
     editor.moveCursorToEndOfLine()
     editor.insertText("           ")

-    editor.getBuffer().save()
+    editor.save()
     expect(editor.getText()).toBe 'Some text.\n'

   describe "when the edit session is destroyed", ->
@@ -41,7 +41,6 @@
       spyOn(fsUtils, 'write')
       config.set("whitespace.ensureSingleTrailingNewline", false)

-      buffer = editor.getBuffer()
       buffer.retain()
       editor.destroy()

@@ -64,40 +63,40 @@

     it "adds a trailing newline when there is no trailing newline", ->
       editor.insertText "foo"
-      editor.getBuffer().save()
+      editor.save()
       expect(editor.getText()).toBe "foo\n"

     it "removes extra trailing newlines and only keeps one", ->
       editor.insertText "foo\n\n\n\n"
-      editor.getBuffer().save()
+      editor.save()
       expect(editor.getText()).toBe "foo\n"

     it "leaves a buffer with a single trailing newline untouched", ->
       editor.insertText "foo\nbar\n"
-      editor.getBuffer().save()
+      editor.save()
       expect(editor.getText()).toBe "foo\nbar\n"

     it "leaves an empty buffer untouched", ->
       editor.insertText ""
-      editor.getBuffer().save()
+      editor.save()
       expect(editor.getText()).toBe ""

     it "leaves a buffer that is a single newline untouched", ->
       editor.insertText "\n"
-      editor.getBuffer().save()
+      editor.save()
       expect(editor.getText()).toBe "\n"

     it "does not add trailing newline if ensureSingleTrailingNewline is false", ->
       config.set("whitespace.ensureSingleTrailingNewline", false)

       editor.insertText "no trailing newline"
-      editor.getBuffer().save()
+      editor.save()
       expect(editor.getText()).toBe "no trailing newline"

     it "does not move the cursor when the new line is added", ->
       editor.insertText "foo"
       expect(editor.getCursorBufferPosition()).toEqual([0,3])
-      editor.getBuffer().save()
+      editor.save()
       expect(editor.getText()).toBe "foo\n"
       expect(editor.getCursorBufferPosition()).toEqual([0,3])

@@ -113,17 +112,17 @@
     it "trims GFM text with a single space", ->
       editor.setGrammar(grammar)
       editor.insertText "foo \nline break!"
-      editor.getBuffer().save()
+      editor.save()
       expect(editor.getText()).toBe "foo\nline break!\n"

     it "leaves GFM text with double spaces alone", ->
       editor.setGrammar(grammar)
       editor.insertText "foo  \nline break!"
-      editor.getBuffer().save()
+      editor.save()
       expect(editor.getText()).toBe "foo  \nline break!\n"

     it "trims GFM text with a more than two spaces", ->
       editor.setGrammar(grammar)
       editor.insertText "foo   \nline break!"
-      editor.getBuffer().save()
+      editor.save()
       expect(editor.getText()).toBe "foo\nline break!\n"


url for hortonworks memory script

url for hortonworks memory script

--- hadoop/lib/memory.coffee
+++ hadoop/lib/memory.coffee
@@ -1,5 +1,7 @@

 {merge} = require 'mecano/lib/misc'
+
+# https://github.com/hortonworks/hdp-configuration-utils/blob/master/2.1/hdp-configuration-utils.py

 memory = (ctx) ->
   {yarn, hadoop_conf_dir} = ctx.config.hdp


add some missing props

add some missing props

--- tutor/specs/components/course-calendar/add-assignment-sidebar.spec.cjsx
+++ tutor/specs/components/course-calendar/add-assignment-sidebar.spec.cjsx
@@ -11,6 +11,8 @@
     @props =
       courseId: '1'
       onSidebarToggle: sinon.spy()
+      isOpen: false
+      shouldIntro: false
       hasPeriods: true

   it 'renders with style for periods', ->


Bongo: fixed TypeError: Cannot read property 'toHexString' of undefined

in some rare cases getid returns undefined and places in application calls for @equals causes worker to fail

--- node_modules_koding/bongo/lib/model/index.coffee
+++ node_modules_koding/bongo/lib/model/index.coffee
@@ -794,7 +794,7 @@

   equals:(model, deep=no)->
     unless deep
-      @getId().equals model?.getId?()
+      @getId()?.equals model?.getId?()
     # TODO: I'm not currently interested in "deep", but it might be needed later CT

   prune:do ->


use setHeader for benefit of connect's compress middleware

--- server/coffee/src/context.coffee
+++ server/coffee/src/context.coffee
@@ -17,9 +17,13 @@
     headers ||= {}
     if content.constructor != String
       content = JSON.stringify(content)
-    headers["Content-Length"] = Buffer.byteLength(content)
+    # Set the content-type and content-length headers explicitly
+    # for the benefit of connect's compress middleware
+    # headers["Content-Length"] = Buffer.byteLength(content)
+    @response.setHeader "Content-Length", Buffer.byteLength(content)
     if @match.accept && content.length > 0
-      headers["Content-Type"] ||= @match.accept
+      # headers["Content-Type"] ||= @match.accept
+      @response.setHeader("Content-Type", @match.accept)
     @response.writeHead(status, headers)
     @response.end(content)


Client: Activity: Added renderMoreLink. It renders if thread size greated than ten

--- client/app/lib/components/sidebarchannelssection/index.coffee
+++ client/app/lib/components/sidebarchannelssection/index.coffee
@@ -12,6 +12,12 @@
     threads    : immutable.Map()
     selectedId : null

+  renderMoreLink: ->
+
+    { threads } = @props
+
+    if threads.size > PREVIEW_COUNT
+      More ...

   render: ->


update published

update published

--- client/apps/edit/components/content/section_list/index.coffee
+++ client/apps/edit/components/content/section_list/index.coffee
@@ -31,6 +31,7 @@

   onDragEnd: (sections) ->
     @props.sections.reset sections
+    @forceUpdate() if @props.article.get 'published'

   isDraggable: ->
     if @state.editingIndex or @state.editingIndex is 0 then false else true


client/landing: show correct view for team/create

--- client/landing/site.landing/coffee/teams/AppController.coffee
+++ client/landing/site.landing/coffee/teams/AppController.coffee
@@ -14,9 +14,9 @@

     { currentPath } = kd.singletons.router

-    if currentPath is '/Teams/Create'
-    then options.view = new TeamSelectorView { cssClass: 'content-page' }
-    else options.view = new TeamsView { cssClass: 'content-page' }
+    if currentPath.indexOf('/Teams/Create') > -1
+    then options.view = new TeamsView { cssClass: 'content-page' }
+    else options.view = new TeamSelectorView { cssClass: 'content-page' }

     super options, data


Fix error templates rendering

Fix error templates rendering

--- server/middlewares/errors.coffee
+++ server/middlewares/errors.coffee
@@ -17,9 +17,7 @@
         res.set header, value for header, value of err.headers

     if err.template? and req?.accepts('html') is 'html'
-        templateName = "#{err.template.name}.jade"
-        res.render templateName, err.template.params, (err, html) ->
+        res.render err.template.name, err.template.params, (err, html) ->
             res.send statusCode, html
     else
         res.send statusCode, error: message
-