Add 'Teachers Create Account Loaded' event to CreateAccountModal

--- app/views/core/CreateAccountModal/CreateAccountModal.coffee
+++ app/views/core/CreateAccountModal/CreateAccountModal.coffee
@@ -86,6 +86,7 @@
     @listenTo @insertSubView(new ChooseAccountTypeView()),
       'choose-path': (path) ->
         if path is 'teacher'
+          window.tracker?.trackEvent 'Teachers Create Account Loaded', category: 'Teachers', ['Mixpanel']
           @signupState.set { path, screen: 'basic-info' }
         else
           @signupState.set { path, screen: 'segment-check' }


[Backport] use javascript dates to allow render flot points on the right place

--- app/coffee/modules/taskboard/charts.coffee
+++ app/coffee/modules/taskboard/charts.coffee
@@ -42,7 +42,7 @@
         width = element.width()
         element.height(240)

-        days = _.map(dataToDraw, (x) -> moment(x.day))
+        days = _.map(dataToDraw, (x) -> moment(new Date(x.day).getTime()))

         data = []
         data.unshift({


issue #88: quit h9 on ctrl-c

issue #88: quit h9 on ctrl-c

--- src/cli.coffee
+++ src/cli.coffee
@@ -4,6 +4,15 @@

 require "./index"
 {run} = require "panda-9000"
+
+# exit on ctrl-c, related signals
+# https://github.com/pandastrike/haiku9/issues/88
+process.on 'SIGHUP',  ()-> process.exit()
+process.on 'SIGINT',  ()-> process.exit()
+process.on 'SIGQUIT', ()-> process.exit()
+process.on 'SIGABRT', ()-> process.exit()
+process.on 'SIGTERM', ()-> process.exit()
+

 call ->


Renamed variables for clarity

Renamed variables for clarity

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

 if argv.env then config.env argv.env

-cli =
+commands =
   help: (argv) ->
     if argv.v || argv.version then return console.log pkg.version

@@ -110,6 +110,6 @@
     console.log 'Enter your Versal credentials to sign in:'
     prompt.get promptParams, callback

-cmd = argv._.shift() || 'help'
-if typeof cli[cmd] == 'function'
-  cli[cmd](argv)
+command = argv._.shift() || 'help'
+if typeof commands[command] == 'function'
+  commands[command](argv)


Make it possible to require files in src directly

The compiled PEG parser is the only thing that needs to be explicitly
loaded from the lib folder.

--- src/scope-selector.coffee
+++ src/scope-selector.coffee
@@ -1,4 +1,4 @@
-ScopeSelectorParser = require './scope-selector-parser'
+ScopeSelectorParser = require '../lib/scope-selector-parser'

 module.exports =
 class ScopeSelector


Gerrit script - less hackish hack to get the room list

Look for any HUBOT_<whatever>_ROOMS environment variable to
determine the room list instead of hardcoding just a few
variable names. Still a hack, but it should work for all the
currently listed adapters that take a list of room and probably
will work for any new adapters added later.

--- src/scripts/gerrit.coffee
+++ src/scripts/gerrit.coffee
@@ -91,10 +91,10 @@

 # So this is kind of terrible - not sure of a better way to do this for now
 robotRooms = (robot) ->
-  if process.env.HUBOT_IRC_ROOMS
-    process.env.HUBOT_IRC_ROOMS.split(",")
-  else if process.env.HUBOT_CAMPFIRE_ROOMS
-    process.env.HUBOT_CAMPFIRE_ROOMS.split(",")
+  roomlistish = /^HUBOT_.+_ROOMS/i
+  roomlists = (v for k,v of process.env when roomlistish.exec(k) isnt null)
+  if roomlists.length != 0
+    roomlists[0].split(",")
   else
     robot.logger.error "Gerrit stream-events: Unable to determine the list of rooms"
     [ "dummy" ]


fix(macModal): Fixed not able to close modal when clicking on overlay

--- src/services/modal.coffee
+++ src/services/modal.coffee
@@ -122,7 +122,7 @@
             wrapper.html template

             if options.overlayClose
-              element.bind "click", ($event) ->
+              element.bind "click", ($event) =>
                 if angular.element($event.target).hasClass("modal-overlay")
                   viewScope.$apply => @hide()


[Dev] Remove unnecessary migration

[Dev] Remove unnecessary migration

--- src/renderer/scripts/app/Migrater.coffee
+++ src/renderer/scripts/app/Migrater.coffee
@@ -31,7 +31,6 @@
         @moved "nco.lastSelectChannel", "nco.nsen.lastSelectChannel"
         @moved "nco.comment.postAsAnonymous", "nco.nsen.postAsAnonymous", "nco.comment"
         @moved "nco.autoMove", "nco.nsen.autoMoveToNextLive"
-        @moved "nco.comment", "nco.services.comment"
         @moved "nco.speech", "nco.services.speech.enabled"
         @moved "nco.player", "nco.services.player"


Apply patch from 3769 issue

Apply patch from 3769 issue

--- bokehjs/src/coffee/models/plots/plot.coffee
+++ bokehjs/src/coffee/models/plots/plot.coffee
@@ -286,12 +286,17 @@

   update_selection: (selection) ->
     for renderer in @mget("renderers")
-      if renderer instanceof GlyphRenderer.Model
-        selected = selection[renderer.id] or []
-        renderer.get('data_source').set("selected", selected)
+      if not renderer instanceof GlyphRenderer.Model
+        continue
+      ds = renderer.get('data_source')
+      if selection?
+        if renderer.id in selection
+          ds.set("selected", selection[renderer.id])
+      else
+        ds.get('selection_manager').clear()

   reset_selection: () ->
-    @update_selection({})
+    @update_selection(null)

   _update_single_range: (rng, range_info, is_panning) ->
     # Is this a reversed range?


Bug fix: Anti-Air CI from fleet 2 doesn't show ship name

--- views/battle-info-area.cjsx
+++ views/battle-info-area.cjsx
@@ -62,10 +62,10 @@
     shipName = null
     if 0 <= shipId <= 5
       shipName = $ships[sortieFleet[shipId]]?.api_name
-    else if 6 <= api.api_idx <= 11
-      shipName = $ships[combinedFleet[shipId]]?.api_name
+    else if 6 <= shipId <= 11
+      shipName = $ships[combinedFleet[shipId - 6]]?.api_name
     if not shipName?
-      shipName = [api.api_idx, '?'].join ' '
+      shipName = "? (#{shipId})"

     tooltip = []
     tooltip.push
{__ 'Anti-air Kind'}: {api.api_kind}


Fix level_about a bit

Fix level_about a bit

--- server/materials/data/level_about.coffee
+++ server/materials/data/level_about.coffee
@@ -366,11 +366,11 @@
         k = int(input())
         print(k // n)

-        Обратите внимание на следующие особенности (в вашем учебнике может быть написано не так — надо делать так, как написано тут, а не в учебнике. Возможно, чего-то из описанного здесь вы не поймете — тогда просто пропускайте этот пункт). Часть из этих особенностей относятся только к паскалю, часть — к любому языку программирования.
+        Обратите внимание на следующие особенности (в вашем учебнике может быть написано не так — надо делать так, как написано тут, а не в учебнике. Возможно, чего-то из описанного здесь вы не поймете — тогда просто пропускайте этот пункт).

Мы не выводим никаких "приглашений" типа "введите N". В условии задачи четко написано, что именно наша программа должна выводить на экран, и там ничего не сказано про то, что надо выводить приглашения. Поэтому любой лишний вывод на экран будет рассмотрен как нарушение формата вывода.

Нет никаких "проверок на дурака", например, нет проверки "а вдруг N=0?", или "а вдруг ввели не число?". Аналогично предыдущему, это в наших задачах не требуется. В более серьезных задачах в условии будут стоять ограничения на допустимые значения N, и никто не будет тестировать вашу программу с N, не удовлетворяющим этим ограничениям. В этой задаче считается, что N>0 всегда.

-
Vы не ставим в конце программы пустой input() (или getch(); в c++) или другую задержку работы. Это тоже не требуется; информация, выведенная на экран, все равно сохраняется и ее все равно можно посмотреть.

+
Мы не ставим в конце программы пустой input() (или getch(); в c++) или другую задержку работы. Это тоже не требуется; информация, выведенная на экран, все равно сохраняется и ее все равно можно посмотреть.

Мы посчитали и сразу вывели ответ, не сохраняя его в промежуточную переменную. Можно было сохранить:

ans = k / n
         print(ans)


Add Wakka Maul to simulation rotation

Add Wakka Maul to simulation rotation

--- server/queues/scoring/getTwoGames.coffee
+++ server/queues/scoring/getTwoGames.coffee
@@ -66,7 +66,7 @@

 # Sampling by level: we pick a level, then find a human and ogre session for that level, one at random, one biased towards recent submissions.
 #ladderLevelIDs = ['greed', 'criss-cross', 'brawlwood', 'dungeon-arena', 'gold-rush', 'sky-span']  # Let's not give any extra simulations to old ladders.
-ladderLevelIDs = ['dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove', 'harrowland', 'zero-sum', 'ace-of-coders']
+ladderLevelIDs = ['dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove', 'harrowland', 'zero-sum', 'ace-of-coders', 'wakka-maul']
 sampleByLevel = (callback) ->
   levelID = _.sample ladderLevelIDs
   favorRecentHumans = Math.random() < 0.5  # We pick one session favoring recent submissions, then find another one uniformly to play against


Check if module path is already loaded before doing fs operations

resolve now checks the existence of a module already loaded at the
given path before checking if the file exists on disk.

This removes the need for many redundant fs.exists and fs.isFile
calls for already loaded modules.

This speeds up package loading since most packages have a common
set of requires that were doing needless fs operations for each
searched path when the module was already in the cache at that
path.

--- src/stdlib/require.coffee
+++ src/stdlib/require.coffee
@@ -73,16 +73,25 @@
     file = file.replace '../', "#{prefix}/"

   if file[0] isnt '/'
-    paths.some (path) ->
-      fileExists = /\.(.+)$/.test(file) and __exists "#{path}/#{file}"
-      jsFileExists = not /\.(.+)$/.test(file) and __exists "#{path}/#{file}.js"
+    moduleAlreadyLoaded = paths.some (path) ->
+      if __moduleExists "#{path}/#{file}"
+        file = "#{path}/#{file}"
+      else if __moduleExists "#{path}/#{file}.js"
+        file = "#{path}/#{file}.js"
+      else if expanded = __moduleExpand "#{path}/#{file}"
+        file = expanded

-      if jsFileExists
-        file = "#{path}/#{file}.js"
-      else if fileExists
-        file = "#{path}/#{file}"
-      else if expanded = __expand "#{path}/#{file}"
-        file = expanded
+    if not moduleAlreadyLoaded
+      paths.some (path) ->
+        fileExists = /\.(.+)$/.test(file) and __exists "#{path}/#{file}"
+        jsFileExists = not /\.(.+)$/.test(file) and __exists "#{path}/#{file}.js"
+
+        if jsFileExists
+          file = "#{path}/#{file}.js"
+        else if fileExists
+          file = "#{path}/#{file}"
+        else if expanded = __expand "#{path}/#{file}"
+          file = expanded
   else
     file = __expand(file) or file

@@ -92,16 +101,27 @@
     console.warn("Failed to resolve '#{name}'") if verifyExistence
     null

+__moduleExists = (path) ->
+  __modules[path] isnt undefined
+
+__moduleExpand = (path) ->
+  return path if __moduleExists path
+  for ext, handler of exts
+    return "#{path}.#{ext}" if __moduleExists "#{path}.#{ext}"
+    return "#{path}/index.#{ext}" if __moduleExists "#{path}/index.#{ext}"
+  null
+
 __expand = (path) ->
+  modulePath = __moduleExpand path
+  return modulePath if modulePath
+
   return path if __isFile path
   for ext, handler of exts
-    if __exists "#{path}.#{ext}"
-      return "#{path}.#{ext}"
-    else if __exists "#{path}/index.#{ext}"
-      return "#{path}/index.#{ext}"
+    return "#{path}.#{ext}" if __exists "#{path}.#{ext}"
+    return "#{path}/index.#{ext}" if __exists "#{path}/index.#{ext}"

   return path if __exists path
-  return null
+  null

 __exists = (path) ->
   $native.exists path


(feature) Pass error when finished.

(feature) Pass error when finished.

--- src/test_processor.coffee
+++ src/test_processor.coffee
@@ -42,8 +42,8 @@
         customApi.remote.failed = (e) ->
           _.each failedCallbacks, (c) -> c(e)
           done()
-        customApi.remote.finished = () ->
-          _.each finishedCallbacks, (c) -> c()
+        customApi.remote.finished = (err) ->
+          _.each finishedCallbacks, (c) -> c(err)
           done()

         deactivateConsoleAPI = """
@@ -52,7 +52,7 @@

         fullCode = deactivateConsoleAPI + "\n" + flavoredCode

-        runner.run fullCode, customApi
+        config.testProcessor.init(runner.run fullCode, customApi)

       return {
         dom: config.templates.tests id: id


:art: Refactor buildSoftWrapIndentationTokens

...as suggested by @nathansobo :sparkles:

--- src/tokenized-line.coffee
+++ src/tokenized-line.coffee
@@ -111,26 +111,16 @@

       return maxColumn

-  # For a given `indentLevel`, calculates how many trailing spaces cannot fit in a single tab.
-  #
-  # indentLevel - {Number}
-  # Returns a {Number} representing the odd indentation spaces in `indentLevel`.
-  oddIndentationSpacesForIndentLevel: (indentLevel) ->
-    oddIndentLevel = indentLevel - Math.floor(indentLevel)
-    Math.round(@tabLength * oddIndentLevel)
-
   buildSoftWrapIndentationTokens: (token, hangingIndent) ->
-    hangingIndentLevel = hangingIndent / @tabLength
-    indentLevel = @indentLevel + hangingIndentLevel
-    indentTokens = [0...Math.floor(indentLevel)].map =>
-      token.buildSoftWrapIndentationToken(@tabLength)
-
-    if oddIndentationSpaces = @oddIndentationSpacesForIndentLevel(indentLevel)
-      indentTokens.concat(
-        token.buildSoftWrapIndentationToken(oddIndentationSpaces)
-      )
-    else
-      indentTokens
+    totalIndentSpaces = (@indentLevel * @tabLength) + hangingIndent
+    indentTokens = []
+    while totalIndentSpaces > 0
+      tokenLength = Math.min(@tabLength, totalIndentSpaces)
+      indentToken = token.buildSoftWrapIndentationToken(tokenLength)
+      indentTokens.push(indentToken)
+      totalIndentSpaces -= tokenLength
+
+    indentTokens

   softWrapAt: (column, hangingIndent) ->
     return [new TokenizedLine([], '', [0, 0], [0, 0]), this] if column == 0


Minimize substring calls and concatenation in transformContent

--- src/tokenized-line.coffee
+++ src/tokenized-line.coffee
@@ -41,6 +41,9 @@
     firstNonWhitespaceColumn = null
     lastNonWhitespaceColumn = null

+    substringStart = 0
+    substringEnd = 0
+
     while bufferColumn < @text.length
       # advance to next token if we've iterated over its length
       if tokenOffset is @tags[tokenIndex]
@@ -66,7 +69,7 @@
         firstNonWhitespaceColumn ?= screenColumn
         lastNonWhitespaceColumn = screenColumn + 1

-        text += @text.substr(bufferColumn, 2)
+        substringEnd += 2
         screenColumn += 2
         bufferColumn += 2

@@ -78,14 +81,21 @@
       # split out leading soft tabs
       else if character is ' '
         if firstNonWhitespaceColumn?
-          text += ' '
+          substringEnd += 1
         else
           if (screenColumn + 1) % @tabLength is 0
             @specialTokens[tokenIndex] = SoftTab
             suffix = @tags[tokenIndex] - @tabLength
             @tags.splice(tokenIndex, 1, @tabLength)
             @tags.splice(tokenIndex + 1, 0, suffix) if suffix > 0
-          text += @invisibles?.space ? ' '
+
+          if @invisibles?.space
+            text += @text.substring(substringStart, substringEnd) if substringEnd > substringStart
+            substringStart = substringEnd
+            text += @invisibles.space
+            substringStart += 1
+
+          substringEnd += 1

         screenColumn++
         bufferColumn++
@@ -93,12 +103,18 @@

       # expand hard tabs to the next tab stop
       else if character is '\t'
+        text += @text.substring(substringStart, substringEnd) if substringEnd > substringStart
+        substringStart = substringEnd
+
         tabLength = @tabLength - (screenColumn % @tabLength)
         if @invisibles?.tab
           text += @invisibles.tab
         else
           text += ' '
         text += ' ' for i in [1...tabLength] by 1
+
+        substringStart += 1
+        substringEnd += 1

         prefix = tokenOffset
         suffix = @tags[tokenIndex] - tokenOffset - 1
@@ -122,12 +138,17 @@
         firstNonWhitespaceColumn ?= screenColumn
         lastNonWhitespaceColumn = screenColumn

-        text += character
+        substringEnd += 1
         screenColumn++
         bufferColumn++
         tokenOffset++

-    @text = text
+    if substringEnd > substringStart
+      unless substringStart is 0 and substringEnd is @text.length
+        text += @text.substring(substringStart, substringEnd)
+        @text = text
+    else
+      @text = text

     @firstNonWhitespaceIndex = firstNonWhitespaceColumn
     if lastNonWhitespaceColumn?


Always use an element to find selection points

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -2,7 +2,8 @@
 #= require trix/observers/selection_change_observer

 {defer, elementContainsNode, nodeIsCursorTarget, innerElementIsActive, makeElement,
- handleEvent, handleEventOnce, normalizeRange, rangeIsCollapsed, rangesAreEqual} = Trix
+ handleEvent, handleEventOnce, normalizeRange, rangeIsCollapsed, rangesAreEqual,
+ findNodeFromContainerAndOffset, tagName} = Trix

 class Trix.SelectionManager extends Trix.BasicObject
   constructor: (@element) ->
@@ -24,7 +25,10 @@
       @updateCurrentLocationRange(locationRange)

   getSelectedPointRange: ->
-    getExpandedPointRange() ? getCollapsedPointRange()
+    return unless getDOMSelection()
+    points = [getSelectionPoint("end")]
+    points.unshift(getSelectionPoint("start")) if @selectionIsExpanded()
+    normalizeRange(points)

   setLocationRangeFromPointRange: (pointRange) ->
     pointRange = normalizeRange(pointRange)
@@ -142,54 +146,26 @@

     @createLocationRangeFromDOMRange(domRange)

-  point = do ->
-    pad = 0.01
-    placeholder = makeElement
+  getSelectionPoint = (side) ->
+    domRange = getDOMRange().cloneRange()
+    element = findNodeFromContainerAndOffset(domRange["#{side}Container"], domRange["#{side}Offset"])
+
+    if tagName(element) is "br"
+      {left, bottom} = element.getBoundingClientRect()
+      x: left, y: bottom
+    else
+      element = makeMeasurementElement()
+      domRange.collapse(false) if side is "end"
+      domRange.insertNode(element)
+      {left, top} = element.getBoundingClientRect()
+      left -= 1 if side is "end"
+      x: left, y: top + 1
+
+  makeMeasurementElement = ->
+    makeElement
       tagName: "span"
-      style: marginLeft: "-#{pad}em"
       data: trixMutable: true
       trixSerialize: false
-    {pad, placeholder}
-
-  getCollapsedPointRange = ->
-    return unless domRange = getDOMRange()
-
-    node = point.placeholder.cloneNode(true)
-    try
-      domRange.insertNode(node)
-      rect = node.getBoundingClientRect()
-    finally
-      node.parentNode.removeChild(node)
-      true
-
-    start =
-      x: rect.left - point.pad
-      y: rect.top + point.pad
-
-    normalizeRange(start)
-
-  getExpandedPointRange = ->
-    return unless domRange = getDOMRange()
-    rects = domRange.getClientRects()
-    return unless rects.length
-
-    startRect = rects[0]
-    endRect = rects[rects.length - 1]
-
-    start =
-      x: startRect.left - point.pad
-      y: startRect.top + 1
-
-    if startRect isnt endRect and Trix.tagName(domRange.cloneContents().lastChild) is "br"
-      end =
-        x: startRect.left - point.pad
-        y: startRect.bottom + (startRect.height / 2)
-    else
-      end =
-        x: endRect.right + point.pad
-        y: endRect.top + point.pad
-
-    normalizeRange([start, end])

   getDOMSelection = ->
     selection = window.getSelection()
@@ -203,7 +179,3 @@
     selection.removeAllRanges()
     selection.addRange(domRange)
     Trix.selectionChangeObserver.update()
-
-  getClientRects = ->
-    rects = getDOMRange()?.getClientRects()
-    rects if rects?.length


Fix npm test failing.

Fix npm test failing.

--- src/ui/models/conv.coffee
+++ src/ui/models/conv.coffee
@@ -10,7 +10,7 @@

 preprocessMessage = (msg) ->
     cont = msg?.chat_message?.message_content
-    embed = cont?.attachment?[0].embed_item
+    embed = cont?.attachment?[0]?.embed_item

     # Workaround for attachments sent through google chat not appearing correctly
     if cont? and embed?.type_?[0] is 456


Display '-' when missings, zeros, pinfs, ninfs equal 0.

--- h2o-web/src/main/steam/scripts/frame-view.coffee
+++ h2o-web/src/main/steam/scripts/frame-view.coffee
@@ -88,11 +88,15 @@
   createPlainRow = (attribute, columns) ->
     map columns, (column) -> column[attribute]

+  createMissingsRow = (columns) ->
+    map columns, (column) ->
+      if column.missing is 0 then '-' else column.missing
+
   createInfRow = (attribute, columns) ->
     map columns, (column) ->
       switch column.type
         when 'real', 'int'
-          column[attribute]
+          if column[attribute] is 0 then '-' else column[attribute]
         else
           '-'

@@ -142,7 +146,7 @@
     meanRow: createMeanRow columns
     sigmaRow: createSigmaRow columns
     cardinalityRow: createCardinalityRow columns
-    missingsRow: if hasMissings then createPlainRow 'missing', columns else null
+    missingsRow: if hasMissings then createMissingsRow columns else null
     zerosRow: if hasZeros then createInfRow 'zeros', columns else null
     pinfsRow: if hasPinfs then createInfRow 'pinfs', columns else null
     ninfsRow: if hasNinfs then createInfRow 'ninfs', columns else null


Client: Activity: DesktopNotifications: Deleted notification settings menu item from private channels

--- client/activity/lib/components/channelthreadheader/container.coffee
+++ client/activity/lib/components/channelthreadheader/container.coffee
@@ -60,11 +60,16 @@
       title   : 'Update purpose'
       key     : 'updatepurpose'
       onClick : @bound 'onUpdatePurpose'
-    ,
-      title   : 'Notification settings'
-      key     : 'notificationsettings'
-      onClick : @props.onShowNotificationSettings
     ]
+
+    # notification settings should be visible only public channels
+    if channel.typeConstant isnt 'privatemessage'
+      notificationSettings =
+        title   : 'Notification settings'
+        key     : 'notificationsettings'
+        onClick : @props.onShowNotificationSettings
+
+      result.push notificationSettings

     # if channel isn't a team channel,
     # add "Leave channel" at the 2nd position of the list


activity feed: remove hack since last commit fixes this race condition

--- client/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/app/Applications/Activity.kdapplication/AppController.coffee
@@ -316,11 +316,8 @@
     if @continueLoadingTeasersLastTimeStamp is lastTimeStamp
       return

-    # HACK: this gets called multiple times if there's no wait
-    KD.utils.wait 1000, =>
-      return  unless @lastFrom?
-      lastTimeStamp = (new Date @lastFrom).getTime()
-      @populateActivity {slug : "before/#{lastTimeStamp}", to: lastTimeStamp}
+    lastTimeStamp = (new Date @lastFrom).getTime()
+    @populateActivity {slug : "before/#{lastTimeStamp}", to: lastTimeStamp}

   teasersLoaded:->
     # the page structure has changed


groups: show tabs when super admin

groups: show tabs when super admin

--- client/app/Applications/Groups.kdapplication/Views/groupview.coffee
+++ client/app/Applications/Groups.kdapplication/Views/groupview.coffee
@@ -142,7 +142,7 @@

   decorateUponRoles:(roles)->

-    if "admin" in roles
+    if "admin" in roles or KD.checkFlag 'super-admin'
       @tabView.showHandleContainer()

   privateGroupOpenHandler: GroupsAppController.privateGroupOpenHandler


enhance error management

enhance error management

--- app/core/voicious.coffee
+++ app/core/voicious.coffee
@@ -90,11 +90,13 @@
         @app.use Express.static Config.Paths.Webroot
         @app.use (require 'connect-assets') src : Config.Paths.Webroot
         do @setAllRoutes
-#        @app.use (err, req, res, next) =>
-#            if err instanceof Errors.NotFound
-#                Errors.RenderNotFound req, res
-#            else
-#                Errors.RenderError req, res
+
+        @app.use (req, res, next) =>
+            Errors.RenderNotFound req, res
+        @app.use (err, req, res, next) =>
+            console.error err
+            Errors.RenderError req, res
+
         @configured = yes

     # Main function


fix hideAxes, add hideXAxis, hideYAxis

fix hideAxes, add hideXAxis, hideYAxis

--- app/graph_drawing.coffee
+++ app/graph_drawing.coffee
@@ -74,6 +74,8 @@
   valueFormat: d3.format(',.4g')
   hideAxes: false
   hideLegend: false
+  hideXAxis: false
+  hideYAxis: false
   #lineWidth: 1

 fgColorParams = ['axisLineColor', 'axisTextColor', 'crosshairLineColor', 'crosshairTextColor']
@@ -125,7 +127,7 @@
   xAxisG = g.append('g')
     .attr('class', 'x axis')

-  yXaxisG = g.append('g')
+  yAxisG = g.append('g')
     .attr('class', 'y axis')

   verticalCrosshair = g.append('line')
@@ -357,17 +359,25 @@
     g
       .attr("transform", "translate(#{margin.left},#{margin.top})")

-    if !params.hideAxes
-      xAxisG
-        .attr('class', 'x axis')
-        .attr('transform', "translate(0, #{height})")
-        .call(xAxis)
-
-      yXaxisG.call(yAxis)
-
-      axes = g.selectAll('.axis')
-      axes.selectAll('path, line').attr('stroke', params.axisLineColor).attr({'fill': 'none', 'shape-rendering': 'crispEdges'})
-      axes.selectAll('text').attr('fill', params.axisTextColor).style('font-size', params.axisTextSize)
+
+    xAxisG
+      .attr('transform', "translate(0, #{height})")
+      .call(xAxis)
+    if params.hideXAxis or params.hideAxes
+      xAxisG.attr('visibility', 'hidden')
+    else
+      xAxisG.attr('visibility', 'visible')
+
+    yAxisG.call(yAxis)
+    if params.hideYAxis or params.hideAxes
+      yAxisG.attr('visibility', 'hidden')
+    else
+      yAxisG.attr('visibility', 'visible')
+
+    axes = g.selectAll('.axis')
+
+    axes.selectAll('path, line').attr('stroke', params.axisLineColor).attr({'fill': 'none', 'shape-rendering': 'crispEdges'})
+    axes.selectAll('text').attr('fill', params.axisTextColor).style('font-size', params.axisTextSize)

     verticalCrosshair
       .attr('y2', height)


variable changing in spectra model

variable changing in spectra model

--- app/models/Source.coffee
+++ app/models/Source.coffee
@@ -83,7 +83,7 @@
   @spectrumID = /(?:all|sdss|boss)\.\d{3,4}\.5\d{4}\.\d{1,3}\.(?:103|26|104|v5_4_45)?/

   @fetch: (sdssid) =>
-    match = sdssid.match(SDSS3SpectralData.spectrumID)
+    match = sdssid.match(@spectrumID)
     unless match?
       alert 'SDSS Spectral ID is malformed.'
       return null
@@ -92,7 +92,7 @@
     specLineUrl = "http://api.sdss3.org/spectrumLines?id=#{sdssid}"

     return $.when($.ajax(spectrumUrl), $.ajax(specLineUrl)).done (spectrum, lines) =>
-      SDSS3SpectralData.fromJSON(spectrum[0], lines)
+      @fromJSON(spectrum[0], lines)

   @fromJSON: (spectrum, lines) =>
     @lastFetch = new Array


teams: activity: type without focusing to chat input

--- client/activity/lib/components/chatinputwidget/index.coffee
+++ client/activity/lib/components/chatinputwidget/index.coffee
@@ -19,7 +19,7 @@

 module.exports = class ChatInputWidget extends React.Component

-  { TAB, ESC, ENTER, UP_ARROW, RIGHT_ARROW, DOWN_ARROW, LEFT_ARROW } = KeyboardKeys
+  { TAB, ESC, ENTER, SPACE, UP_ARROW, RIGHT_ARROW, DOWN_ARROW, LEFT_ARROW } = KeyboardKeys

   @defaultProps =
     enableSearch : no
@@ -29,6 +29,26 @@
     super props

     @state               = { value : '' }
+    { windowController } = kd.singletons
+
+    windowController.on 'keydown', (event) =>
+
+      textInput         = React.findDOMNode this.refs.textInput
+      keyboardElements  = "input,textarea,select,datalist,keygen,[contenteditable='true'],button"
+      { activeElement } = document
+
+      return  unless textInput
+      return  if textInput is activeElement
+      # do not break accessibility
+      return  if event.which in [ ENTER, TAB, SPACE ]
+
+      nothingFocused  = not $(activeElement).is keyboardElements
+      inputInViewport = textInput.offsetParent
+      # temp: until modals are made stateful
+      noActiveModals  = !($('body > .kdmodal').length + $('body > div > .Modal').length)
+
+      textInput.focus()  if nothingFocused and inputInViewport and noActiveModals
+

   getDataBindings: ->


TeamMembersCommonView: Implement base fetch and listing flow.

--- client/admin/lib/views/members/teammemberscommonview.coffee
+++ client/admin/lib/views/members/teammemberscommonview.coffee
@@ -1,5 +1,6 @@
 kd                   = require 'kd'
 KDView               = kd.View
+MemberItemView       = require './memberitemview'
 KDListItemView       = kd.ListItemView
 KDCustomHTMLView     = kd.CustomHTMLView
 KDListViewController = kd.ListViewController
@@ -9,21 +10,28 @@

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

-    options.noItemFoundWidget or= new KDCustomHTMLView
-    options.listViewItemClass or= KDListItemView
+    options.noItemFoundWidget   or= new KDCustomHTMLView
+    options.listViewItemClass   or= MemberItemView
+    options.listViewItemOptions or= {}
+    options.itemLimit            ?= 10

     super options, data

+    @skip = 0
+
     @createListController()
-    @listMembers()
+    @fetchMembers()

   createListController: ->

-    { listViewItemClass, noItemFoundWidget } = @getOptions
+    { listViewItemClass, noItemFoundWidget, listViewItemOptions } = @getOptions()

     @listController       = new KDListViewController
-      itemClass           : listViewItemClass
+      viewOptions         :
+        wrapper           : yes
+        itemClass         : listViewItemClass
+        itemOptions       : listViewItemOptions
       noItemFoundWidget   : noItemFoundWidget
       startWithLazyLoader : yes
       lazyLoadThreshold   : .90
@@ -34,9 +42,26 @@
     @addSubView @listController.getView()

-  ###*
-    This method needs to be implemented in subclasses.
+  fetchMembers: ->

-    @abstract
-  ###
-  listMembers: -> throw new Error 'Method needs to be implemented in subclasses'
+    selector = @query or ''
+    options  =
+      limit  : @getOptions().itemLimit
+      sort   : { timestamp: -1 }
+      skip   : @skip
+
+    @getData().searchMembers selector, options, (err, members) =>
+      return kd.warn err  if err
+      @listMembers members
+
+
+  listMembers: (members) ->
+
+    return  unless members.length
+
+    @skip += members.length
+
+    for member in members
+      @listController.addItem member
+
+    @listController.lazyLoader.hide()


minor: error handling

minor: error handling

--- client/app/MainApp/maincontroller/mainviewcontroller.coffee
+++ client/app/MainApp/maincontroller/mainviewcontroller.coffee
@@ -43,7 +43,7 @@

     isEntryPointSet = null

-    (options)->
+    (options = {})->

       {behavior, name} = options
       isEntryPointSet  = yes if name isnt "Home"


adjusted logic on images

adjusted logic on images

--- lib/assets/javascripts/directives.js.coffee
+++ lib/assets/javascripts/directives.js.coffee
@@ -446,8 +446,7 @@
             _resetAxis()

     scope.$watch 'settings.images', ->
-      # console.log 'settings.images'
-      if scope.settings? && scope.settings.images and scope.settings.images.length != scope.image_set.length
+      if scope.settings? && scope.settings.images
         _draw_images()

     scope.$on 'images_changed', ->


changed request headers per @dhh

changed request headers per @dhh

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -1,7 +1,8 @@
 pageCache    = []
 currentState = null
 initialized  = false
-referer = document.location.href
+referer      = document.location.href
+

 visit = (url) ->
   if browserSupportsPushState


Fix Turbolinks.visit()

Fix Turbolinks.visit()

If the browser doesn't support Turbolinks, when `Turbolinks.visit()` was
being called with no parameters `document.location.href = undefined` was
executed, resulting in a request to a non-existent route.

This is not happening if the browser does support Turbolinks.

This commit fixes this issue.

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -639,7 +639,7 @@
   visit = fetch
   initializeTurbolinks()
 else
-  visit = (url) -> document.location.href = url
+  visit = (url = document.location.href) -> document.location.href = url

 # Public API
 #   Turbolinks.visit(url)


Minor: rename variable for clarity

Minor: rename variable for clarity

--- src/resources/forms/orbeon/newbuilder/resources/grid/events.coffee
+++ src/resources/forms/orbeon/newbuilder/resources/grid/events.coffee
@@ -94,8 +94,8 @@
         Events.clickEvent.subscribe ({target}) ->
             isLabel = YD.hasClass target, 'xforms-label'
             isHint = YD.hasClass target, 'xforms-hint'
-            isInGridTd = (YD.getAncestorByClassName target, "fr-grid-td")?
-            if (isLabel or isHint) and isInGridTd
+            isInGridThTd = $(target).closest('th, td').is('.fr-grid-th, .fr-grid-td')
+            if (isLabel or isHint) and isInGridThTd
               # Wait for Ajax response before showing editor to make sure the editor is bound to the current control
               Events.runOnNext Events.ajaxResponseProcessedEvent, ->
                   labelHint = target


Use binary encoding for child_process.exec(): it allows to retrieve binary files content from history.

--- src/git.coffee
+++ src/git.coffee
@@ -13,7 +13,7 @@
     args    ?= []
     args     = args.join " " if args instanceof Array
     bash     = "#{Git.bin} #{command} #{options} #{args}"
-    exec bash, {cwd: git_dir}, callback
+    exec bash, {cwd: git_dir, encoding:'binary'}, callback
     return bash


Only open submodule after checking parent path

Previously an infinite loop of open calls were possible since
the path check to the parent repository was done after opening
which would never occur if the same repository path was used each
time.

Now opening the repository is separate from opening its submodules.

--- src/git.coffee
+++ src/git.coffee
@@ -156,7 +156,7 @@
   else
     repositoryPath is path.sep

-exports.open = (repositoryPath) ->
+openRepository = (repositoryPath) ->
   symlink = realpath(repositoryPath) isnt repositoryPath

   repositoryPath = repositoryPath.replace(/\\/g, '/') if process.platform is 'win32'
@@ -170,16 +170,22 @@
           repository.openedWorkingDirectory = repositoryPath
           break
         repositoryPath = path.resolve(repositoryPath, '..')
-
-    repository.submodules = {}
-    for relativePath in repository.getSubmodulePaths()
-      submodulePath = path.join(repository.getWorkingDirectory(), relativePath)
-      if submoduleRepo = exports.open(submodulePath)
-        if submoduleRepo.getPath() is repository.getPath()
-          submoduleRepo.release()
-        else
-          repository.submodules[relativePath] = submoduleRepo
-
     repository
   else
     null
+
+openSubmodules = (repository) ->
+  repository.submodules = {}
+  for relativePath in repository.getSubmodulePaths() when relativePath
+    submodulePath = path.join(repository.getWorkingDirectory(), relativePath)
+    if submoduleRepo = openRepository(submodulePath)
+      if submoduleRepo.getPath() is repository.getPath()
+        submoduleRepo.release()
+      else
+        openSubmodules(submoduleRepo)
+        repository.submodules[relativePath] = submoduleRepo
+
+exports.open = (repositoryPath) ->
+  repository = openRepository(repositoryPath)
+  openSubmodules(repository) if repository?
+  repository


game state

game state

--- test/engine_camera.coffee
+++ test/engine_camera.coffee
@@ -1,32 +0,0 @@
-module "Engine"
-
-test "Camera", ->
-  App.width = 800
-  App.height = 640
-
-  engine = Engine
-    includedModules: ["Camera"]
-
-  gameObject = GameObject
-    x: 50
-    y: 100
-
-  equal engine.I.cameraTransform.a, 1
-  equal engine.I.cameraTransform.b, 0
-  equal engine.I.cameraTransform.c, 0
-  equal engine.I.cameraTransform.d, 1
-  equal engine.I.cameraTransform.tx, 0
-  equal engine.I.cameraTransform.ty, 0
-
-  engine.follow(gameObject, "centered")
-
-  engine.update()
-
-  equal engine.I.cameraTransform.a, 1
-  equal engine.I.cameraTransform.b, 0
-  equal engine.I.cameraTransform.c, 0
-  equal engine.I.cameraTransform.d, 1
-  equal engine.I.cameraTransform.tx, (App.width / 2) - gameObject.I.x
-  equal engine.I.cameraTransform.ty, (App.height / 2) - gameObject.I.y
-
-module()


:bug: Fix range of color from variable expression

--- lib/tasks/scan-buffer-colors-handler.coffee
+++ lib/tasks/scan-buffer-colors-handler.coffee
@@ -21,6 +21,7 @@
       registry.createExpression 'variables', paletteRegexpString, 1, (match, expression, context) ->
         [d,d,name] = match
         baseColor = context.readColor(name)
+        @colorExpression = name
         @rgba = baseColor.rgba

   scan: ->


Fix comment linking issue

Fix comment linking issue

- Fixes issue where if users click a comment link url it would go to
  page 1 instead of the comment's page

--- app/talk/discussion.cjsx
+++ app/talk/discussion.cjsx
@@ -42,7 +42,7 @@
       @setDiscussion(nextProps.params.discussion)
         .then => @setComments(nextProps.query.page ? 1)
     else if nextProps.query.page isnt @props.query.page
-      @setComments(nextProps.query.page ? 1)
+      @setComments(nextProps.query.page)

   componentDidMount: ->
     @shouldScrollToBottom = true if @props.query?.scrollToLastComment


small fix parse url params

small fix parse url params

--- lib/client/javascripts/nali/view.js.coffee
+++ lib/client/javascripts/nali/view.js.coffee
@@ -100,7 +100,7 @@

   parseUrlSegments: ( segments ) ->
     params = []
-    for segment in segments
+    for segment in segments when segment isnt ''
       [ name, value ] = segment.split ':'
       if value
         last = params[ params.length - 1 ]


open modal at same scroll position

open modal at same scroll position

--- @client/modal.coffee
+++ @client/modal.coffee
@@ -47,6 +47,9 @@

     return

+  componentWillMount: ->
+    @scroll_Y = window.scrollY
+
   componentDidMount: ->
     @focused_element_before_opening = document.activeElement

@@ -75,8 +78,8 @@
     # all while making it seem like the whole page is scrollable.
     _.extend modal.style,
       position: 'fixed'
-      top: 0
-      left: 0
+      top: "0px"
+      left: "0px"
       width: '100vw'
       height: '100vh'
       overflow: 'auto'
@@ -88,6 +91,10 @@
       marginRight: scroll_bar_width
       overflow: 'hidden'
       position: 'fixed'
+      top: "-#{@scroll_Y}px"
+
+
+

   componentWillUnmount: ->
     # return the focus to the element that had focus when the modal was launched
@@ -99,5 +106,9 @@
       marginRight: null
       overflow: null
       position: null
+      top: 0
+
+    # restore scroll position
+    window.scroll(0, @scroll_Y)


Evaluate immediately if the result from a cell is a function

--- h2o-web/src/main/steam/scripts/flow.coffee
+++ h2o-web/src/main/steam/scripts/flow.coffee
@@ -1998,8 +1998,8 @@
       #     _.delay process, 1000
       # process()
       #
-
-      outputBuffer.subscribe (ft) ->
+
+      evaluate = (ft) ->
         if ft?.isFuture
           ft (error, result) ->
             if error
@@ -2025,6 +2025,7 @@
             text: ft
             template: 'flow-raw'

+      outputBuffer.subscribe evaluate

       tasks = [
         safetyWrapCoffeescript guid
@@ -2047,9 +2048,7 @@
             if isRoutine cellResult
               show assist _, sandbox.routines, cellResult
             else
-              output.close
-                text: cellResult
-                template: 'flow-raw'
+              evaluate cellResult
           else
             output.close
               text: cellResult
@@ -2596,7 +2595,8 @@
     insertNewCellBelow()

     link$ _.selectCell, selectCell
-    link$ _.insertAndExecuteCell, insertCellBelowAndRun
+    link$ _.insertAndExecuteCell, (type, input) ->
+      defer insertCellBelowAndRun, type, input

   link$ _.ready, initialize


pls work

pls work

--- modules/player/search.coffee
+++ modules/player/search.coffee
@@ -6,17 +6,16 @@
     @pending = {}

   doSearch: (msg, l, query)->
-    @pending[msg.id] = search = { msg, query, l }
+    @pending[msg.id] = search = { msg, query, l, react: [] }
     search.results = await @util.getInfo("ytsearch10:#{query}")
     if search.results.partial
       await @updateResults(msg.id)
       search.results.on('video', => @updateResults(msg.id))
-      r = await search.rmsg.awaitReactions((r, u) =>
+      r = await search.rmsg.awaitReactions (r, u) =>
         r.emoji.name in options and u.id is msg.author.id
-      , time: 60000)
       try await search.rmsg.delete()
       delete @pending[msg.id]
-      video = search.results.items[options.indexOf(r.emoji.name)]
+      video = search.results.items[options.indexOf(r.first().emoji.name)]
       # Make sure the video exists
       throw new Error(l.generic.error) unless video
       return video
@@ -26,18 +25,19 @@

   updateResults: (id)->
     return unless @pending[id]
-    { l, results, rmsg } = @pending[id]
+    { l, results, rmsg, react } = @pending[id]
     embed =
       author:
         name: if results.partial then l.player.hud.searching else l.player.hud.results
-        icon: if results.partial then 'https://d.thebitlink.com/wheel.gif'
+        icon_url: if results.partial then 'https://d.thebitlink.com/wheel.gif'
       description: ''
     for item, i in results.items
       embed.description += "#{options[i]} [#{item.title.replace(/\]/, '\\]')}]"
       embed.description += "(#{item.webpage_url.replace(/\]/, '\\]')})"
       embed.description += " (#{@util.displayTime item.duration})\n"
-      if rmsg and not rmsg.reactions.find((r)=> r.emoji.id is options[i])
+      if rmsg and not options[i] in react
         await @pending[id].rmsg.react(options[i])
+        react.push(options[i])
     if rmsg then try
       await rmsg.edit('', { embed })
       return rmsg


added spec

added spec

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -194,6 +194,16 @@
     beforeEach ->
       editor.getBuffer().setText("012 345\nabc\n")
       editor.setCursorScreenPosition([0, 4])
+
+    describe "when selected lines in visual linewise mode", ->
+      beforeEach ->
+        keydown('V')
+        keydown('k')
+        keydown('y')
+
+      it "saves the lines to the default register in linewise mode", ->
+        expect(vimState.getRegister('"').text).toBe "012 345\nabc\n"
+        expect(vimState.getRegister('"').type).toBe 'linewise'

     describe "when followed by a second y ", ->
       beforeEach ->


Use insertText instead of setText where possible in tests

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -821,12 +821,12 @@

     it "allows undoing an entire batch of typing", ->
       keydown 'i'
-      editor.setText("abc")
+      editor.insertText("abc")
       keydown 'escape'
       keydown 'i'
-      editor.setText("abcdef")
+      editor.insertText("def")
       keydown 'escape'
-      expect(editor.getText()).toBe "abcdef"
+      expect(editor.getText()).toBe "abdefc"
       keydown 'u'
       expect(editor.getText()).toBe "abc"
       keydown 'u'
@@ -834,21 +834,22 @@

     it "allows repeating typing", ->
       keydown 'i'
-      editor.setText("abc")
+      editor.insertText("abc")
       keydown 'escape'
       keydown '.'
-      editor.setText("ababcc")
+      editor.insertText("ababcc")

     # This one doesn't work because we can't simulate typing correctly,
     # and VimState#resetInputTransactions actually inspects buffer patches to
     # build patches for repeating
     xit "resets transactions for repeats after movement", ->
-      editor.setText("abc\n123")
+      editor.setCursorBufferPosition([0, 0])
+      editor.insertText("abc\n123")
       keydown 'i'
-      editor.setText("defabc\n123")
+      editor.insertText("def")
       editorView.trigger 'core:move-down'
       expect(editor.getCursorBufferPosition()).toEqual [1, 3]
-      editor.setText("defabc\n456123")
+      editor.insertText("456")
       keydown 'escape'
       editor.setCursorBufferPosition([0, 0])
       keydown '.'
@@ -861,7 +862,7 @@

     it "can be undone in one go", ->
       keydown 'a'
-      editor.setText("abc")
+      editor.insertText("abc")
       keydown 'escape'
       expect(editor.getText()).toBe "abc"
       keydown 'u'
@@ -869,7 +870,7 @@

     it "repeats correctly", ->
       keydown 'a'
-      editor.setText("abc")
+      editor.insertText("abc")
       keydown 'escape'
       expect(editor.getText()).toBe "abc"
       keydown '.'


Log error before exiting

Log error before exiting

--- spec/spec-bootstrap.coffee
+++ spec/spec-bootstrap.coffee
@@ -9,6 +9,7 @@
   runSpecSuite './spec-suite'
 catch error
   if atom?.getLoadSettings().exitWhenDone
+    console.error(error.stack ? error)
     atom.exit(1)
   else
     throw error


Add validation to diagram creator

Add validation to diagram creator

--- view/coffee/creator/create_diagram.coffee
+++ view/coffee/creator/create_diagram.coffee
@@ -70,26 +70,40 @@
       deleteStop: (idx) ->
         @stops.splice(idx, 1)
       submit: ->
-        API.postJSON
-          url: '/api/diagram'
-          data: @createData()
-          success: ->
-            location.reload(false)
+        data = @createData()
+        if data
+          API.postJSON
+            url: '/api/diagram'
+            data: data
+            success: ->
+              location.reload(false)
       updateDiagram: ->
-        if @update
+        data = @createData()
+        if @update and data
           API.putJSON
             url: "/api/diagram/#{@update}"
-            data: @createData()
+            data: data
             success: ->
               location.href = location.pathname
       createData: ->
+        isAlert = false
         stops = _.flatMap @stops, (s) =>
+          if !s.name then return []
           id = @getLineStationId(s.name)
           if id
             [{lineStationId: id, arrival: parseInt(s.arrival), departure: parseInt(s.departure)}]
-          else []
-        starts = for s in @starts.split(",")
-          s.trim()
+          else
+            if !isAlert then window.alert("該当の路線or駅が見つかりません: #{s.name}")
+            isAlert = true
+            []
+        if isAlert then return null
+        starts = _.flatMap @starts.split(","), (raw) ->
+          start = raw.trim()
+          if start then [start] else []
+        console.log(starts)
+        if starts.length < 1
+          window.alert('列車が存在しません')
+          return null
         {name: @name, trainType: parseInt(@trainType), subType: @subType, starts: starts, stops: stops}
       clear: ->
         location.href = location.pathname


Fix diagram copy

Fix diagram copy

--- view/coffee/creator/create_diagram.coffee
+++ view/coffee/creator/create_diagram.coffee
@@ -112,7 +112,7 @@
             @starts = starts.join(', ')
     ready: ->
       @getTypes()
-      @getStations ->
+      @getStations =>
         @setUpdate()
     watch:
       stops: ->


Use git-utils from status handler

Use git-utils from status handler

--- src/app/repository-status-handler.coffee
+++ src/app/repository-status-handler.coffee
@@ -1,4 +1,4 @@
-Git = require 'git'
+Git = nodeRequire 'git-utils'
 fs = require 'fs'

 module.exports =
@@ -7,10 +7,9 @@
     if repo?
       workingDirectoryPath = repo.getWorkingDirectory()
       statuses = {}
-      for path, status of repo.getRepo().getStatuses()
+      for path, status of repo.getStatuses()
         statuses[fs.join(workingDirectoryPath, path)] = status
-      upstream = repo.getAheadBehindCounts()
-      repo.destroy()
+      upstream = repo.getAheadBehindCount()
     else
       upstream = {}
       statuses = {}


Make tests windows-friendly using #{path.sep} instead of "/"

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -2593,7 +2593,7 @@
           jasmine.attachToDOM(workspaceElement)
           piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0]
           expect(piDir).not.toBeNull()
-          omicronPath = piDir.getPath().replace "/pi", ""
+          omicronPath = piDir.getPath().replace "#{path.sep}pi", ""
           sigmaFilePath = path.join(omicronPath, "sigma.txt")
           fs.writeFileSync(sigmaFilePath, "doesn't matter")
           treeView.updateRoots()
@@ -2611,7 +2611,7 @@
           jasmine.attachToDOM(workspaceElement)
           piDir = $(treeView.roots[0].entries).find(".directory:contains(omicron#{path.sep}pi):first")[0]
           expect(piDir).not.toBeNull()
-          omicronPath = piDir.getPath().replace "/pi", ""
+          omicronPath = piDir.getPath().replace "#{path.sep}pi", ""
           rhoDirPath = path.join(omicronPath, "rho")
           fs.makeTreeSync(rhoDirPath)
           treeView.updateRoots()


This never ended up being used

This never ended up being used

--- spec/vim-state-spec.coffee
+++ spec/vim-state-spec.coffee
@@ -17,11 +17,6 @@

   keydown = (key, options={}) ->
     options.element ?= editorView[0]
-    helpers.keydown(key, options)
-
-  type = (key, options={}) ->
-    options.element = editorView[0]
-    options.raw = true
     helpers.keydown(key, options)

   commandModeInputKeydown = (key, opts = {}) ->


silence socket.io update logs

silence socket.io update logs

--- vue/src/shared/helpers/message_bus.coffee
+++ vue/src/shared/helpers/message_bus.coffee
@@ -20,7 +20,7 @@

   recordsSocket.on 'update', (data) =>
     roomScores[data.room] = data.score
-    console.log("socket.io update", {roomScores: roomScores}, data)
+    # console.log("socket.io update", {roomScores: roomScores}, data)
     Records.import(data.records)

   recordsSocket.on 'reconnect', (data) =>
@@ -36,6 +36,6 @@
   recordsSocket.on 'connect', (data) =>
     # Flash.warning("server connected")
     console.log("socket.io connect")
-
+
 export closeLiveUpdate = ->
   recordsSocket.close()


add nockback mode env var

add nockback mode env var

--- tests/helpers/nock.coffee
+++ tests/helpers/nock.coffee
@@ -1,4 +1,4 @@
 global.nock = require('nock')
 global.nockback = nock.back
-global.nockback.setMode 'record'
+global.nockback.setMode process.env.NOCK_BACK_MODE || 'record'
 global.nockback.fixtures = './tests/fixtures'


transaction runner test use 'clone' instead of JSON.parse(JSON.stringify(obj))

--- test/unit/transaction-runner-test.coffee
+++ test/unit/transaction-runner-test.coffee
@@ -893,7 +893,7 @@
       describe 'in after hook when transaction fails ', () ->
         modifiedTransaction = {}
         beforeEach () ->
-          modifiedTransaction = JSON.parse(JSON.stringify(transaction))
+          modifiedTransaction = clone(transaction)
           modifiedTransaction['expected']['statusCode'] = "303"

           hooksStub.beforeHooks = {}


Adds formGroup boolean to FieldWrapper propTypes so that formGroup
becomes an optional className on the component.  This allows developers
to avoid writing class overrides because they may not want form-group
rendered.  This will be a default prop to true to avoid breaking
changes for anyone using the library.

Also updates propTypes to include the available options.

--- modules/components/forms/field_wrapper.cjsx
+++ modules/components/forms/field_wrapper.cjsx
@@ -6,13 +6,19 @@
   @displayName = 'FieldWrapper'

   @propTypes =
-    errors: React.PropTypes.array
+    className: React.PropTypes.string
+    errors:    React.PropTypes.array
+    formGroup: React.PropTypes.bool
+
+  @defaultProps =
+    formGroup: true
+

   render: ->
-    className = classNames @props.className, 'form-group',
-      'has-error': @props.errors?
+    className = classNames @props.className,
+      'form-group': @props.formGroup
+      'has-error':  @props.errors?

     <div className={className}>
       {@props.children}
-    </div>
-
+    </div>


Add back in pause & unpause

Add back in pause & unpause

--- bokehjs/src/coffee/common/gmap_plot.coffee
+++ bokehjs/src/coffee/common/gmap_plot.coffee
@@ -33,6 +33,7 @@
     @y_range.set({start: proj_ystart, end: proj_yend, silent:true})

   update_range: (range_info) ->
+    @pause()
     # PAN ----------------------------
     if range_info.sdx? or range_info.sdy?
       @map.panBy(range_info.sdx, range_info.sdy)
@@ -70,6 +71,7 @@
       # Finally re-center
       @setRanges()
     # END ZOOM ---------------------
+    @unpause()

   bind_bokeh_events: () ->
     super()


Fix talk links

Fix talk links

--- app/models/cyclone_subject.coffee
+++ app/models/cyclone_subject.coffee
@@ -9,7 +9,7 @@
   keys[Math.floor Math.random() * keys.length]

 class CycloneSubject extends Subject
-  @configure 'CycloneSubject', 'zooniverse_id', 'workflowId', 'groupId', 'location', 'coords', 'metadata'
+  @configure 'CycloneSubject', 'zooniverseId', 'workflowId', 'groupId', 'location', 'coords', 'metadata'

   @current: null


Update reconnecting_websocket.coffee

Update reconnecting_websocket.coffee

--- src/client/reconnecting_websocket.coffee
+++ src/client/reconnecting_websocket.coffee
@@ -42,10 +42,11 @@

 It is API compatible with the standard WebSocket API.

+Inspired from: https://github.com/joewalnes/reconnecting-websocket/
 Contributors:
 - Joe Walnes
 - Didier Colens
-
+- Wout Mertens
 ###
 class ReconnectingWebSocket
   constructor: (url, protocols, Socket) ->
@@ -60,72 +61,72 @@
     @reconnectInterval = 1000
     @timeoutInterval = 2000

-    forcedClose = false
-    timedOut = false
+    @forcedClose = false

     @url = url
     @protocols = protocols
     @readyState = Socket.CONNECTING
     @URL = url # Public API

-    @onopen = (event) ->
-    @onclose = (event) ->
-    @onconnecting = (event) ->
-    @onmessage = (event) ->
-    @onerror = (event) ->
-
+    timedOut = false
     connect = (reconnectAttempt) =>
-      @ws = new Socket(url)
-      console.debug "ReconnectingWebSocket", "attempt-connect", url  if @debug
-      localWs = @ws
-      timeout = setTimeout(->
-        console.debug "ReconnectingWebSocket", "connection-timeout", url  if @debug
+      @ws = new Socket(@url)
+      console.debug "ReconnectingWebSocket", "attempt-connect", @url  if @debug
+
+      timeout = setTimeout(=>
+        console.debug "ReconnectingWebSocket", "connection-timeout", @url  if @debug
         timedOut = true
-        localWs.close()
+        @ws.close()
         timedOut = false
       , @timeoutInterval)

-      @ws.onopen = (event) ->
+      @ws.onopen = (event) =>
         clearTimeout timeout
-        console.debug "ReconnectingWebSocket", "onopen", url  if @debug
+        console.debug "ReconnectingWebSocket", "onopen", @url  if @debug
         @readyState = Socket.OPEN
         reconnectAttempt = false
         @onopen event

-      @ws.onclose = (event) ->
+      @ws.onclose = (event) =>
         clearTimeout timeout
         @ws = null
-        if forcedClose
+        if @forcedClose
           @readyState = Socket.CLOSED
           @onclose event
         else
           @readyState = Socket.CONNECTING
           @onconnecting event
           if not reconnectAttempt and not timedOut
-            console.debug "ReconnectingWebSocket", "onclose", url  if @debug
+            console.debug "ReconnectingWebSocket", "onclose", @url  if @debug
             @onclose event
           setTimeout (-> connect true ), @reconnectInterval

-      @ws.onmessage = (event) ->
-        console.debug "ReconnectingWebSocket", "onmessage", url, event.data  if @debug
+      @ws.onmessage = (event) =>
+        console.debug "ReconnectingWebSocket", "onmessage", @url, event.data  if @debug
         @onmessage event

-      @ws.onerror = (event) ->
-        console.debug "ReconnectingWebSocket", "onerror", url, event  if @debug
+      @ws.onerror = (event) =>
+        console.debug "ReconnectingWebSocket", "onerror", @url, event  if @debug
         @onerror event

-    connect url
+    connect @url
+
+  onopen: (event) ->
+  onclose: (event) ->
+  onconnecting: (event) ->
+  onmessage: (event) ->
+  onerror: (event) ->

   send: (data) ->
     if @ws
-      console.debug "ReconnectingWebSocket", "send", url, data  if @debug
+      console.debug "ReconnectingWebSocket", "send", @url, data  if @debug
       @ws.send data
     else
       throw "INVALID_STATE_ERR : Pausing to reconnect websocket"

   close: ->
     if @ws
-      forcedClose = true
+      @forcedClose = true
       @ws.close()

   ###


Increase autocompile rollout to 5%

Increase autocompile rollout to 5%

--- services/web/app/coffee/Features/Project/ProjectController.coffee
+++ services/web/app/coffee/Features/Project/ProjectController.coffee
@@ -276,7 +276,7 @@
 				# Extract data from user's ObjectId
 				timestamp = parseInt(user_id.toString().substring(0, 8), 16)

-				rolloutPercentage = 1 # Percentage of users to roll out to
+				rolloutPercentage = 5 # Percentage of users to roll out to
 				if !ProjectController._isInPercentageRollout('autocompile', user_id, rolloutPercentage)
 					# Don't show if user is not part of roll out
 					return cb(null, { enabled: false, showOnboarding: false })


#143910149  removing media no longer pops up to the top of the page on new and edit.

--- app/assets/javascripts/hanuman/hanuman-cloudinary-uploads.js.coffee
+++ app/assets/javascripts/hanuman/hanuman-cloudinary-uploads.js.coffee
@@ -203,7 +203,8 @@
       $(element).find('.upload-sort-order').val(idx+1)

   # on click removes uploaded files on survey new.
-  $('.file-upload').on 'click', '.remove-upload', ->
+  $('.file-upload').on 'click', '.remove-upload', (e) ->
+    e.preventDefault()
     file = $(@).attr('id')
     containerClass = "."+file+"-preview"
     fileToDelete = $(@).closest(".file-upload-input-button")


reverse order of tags to account for float:right

--- app/assets/javascripts/species/helpers/handlebars_helpers.js.coffee
+++ app/assets/javascripts/species/helpers/handlebars_helpers.js.coffee
@@ -15,7 +15,7 @@
   escaped = Handlebars.Utils.escapeExpression(value)
   formatted = escaped.split(',').map((item) ->
   	"" + item + ""
-  ).join ""
+  ).reverse().join ""
   new Handlebars.SafeString(formatted)

 Ember.Handlebars.registerHelper('highlight', (suggestion, options) ->


cleanup preview tab logic

cleanup preview tab logic

--- app/assets/javascripts/uploadcare/widget/tabs/preview-tab.js.coffee
+++ app/assets/javascripts/uploadcare/widget/tabs/preview-tab.js.coffee
@@ -21,42 +21,31 @@
       @dialogApi.fileColl.onAdd.add @__setFile

     __setFile: (@file) =>
-      @__setState 'unknown'

-      stateKnown = utils.once (info) =>
-        if info.isImage
-          @__setState 'image'
-        else
-          @__setState 'regular'
-
-      file = @file
       ifCur = (fn) =>
         => fn.apply(null, arguments) if file == @file

-      @file.done ifCur (info) =>
-        stateKnown info
+      @file.progress ifCur utils.once (info) =>
+        @__setState 'unknown', {file: info.incompleteFileInfo}

-      @file.fail ifCur (error) =>
-        @__setState 'error', {error}
+      @file.done ifCur (file) =>
+        state = if file.isImage then 'image' else 'regular'
+        @__setState state, {file}
+
+      @file.fail ifCur (error, file) =>
+        @__setState 'error', {error, file}

     # error
     # unknown
     # image
     # regular
     __setState: (state, data) ->
-      render = utils.once (fileInfo) =>
-        data = $.extend {file: fileInfo}, data
-        @container.empty().append tpl("tab-preview-#{state}", data)
-        @__afterRender state
-
-      @file.progress (progressInfo) -> render progressInfo.incompleteFileInfo
-      @file.done render
-      @file.fail (error, fileIfo) -> render fileIfo
+      @container.empty().append tpl("tab-preview-#{state}", data)
+      @__afterRender state

     __afterRender: (state) ->
-      if state is 'unknown'
-        if @__doCrop
-          @__hideDoneButton()
+      if state is 'unknown' and @__doCrop
+        @__hideDoneButton()
       if state is 'image' and @__doCrop
         @__initCrop()


fix queuing songs by double clicking library

--- src/mpd.coffee
+++ src/mpd.coffee
@@ -577,13 +577,13 @@

     @raiseEvent 'playlistupdate'

-  queueFile: (file) => queueFiles [file]
+  queueFile: (file) => @queueFiles [file]

   queueFilesNext: (files) =>
     new_pos = (@status.current_item?.pos ? -1) + 1
     @queueFilesAtPos files, new_pos

-  queueFileNext: (file) => queueFilesNext [file]
+  queueFileNext: (file) => @queueFilesNext [file]

   clear: =>
     @sendCommand "clear"


insert comment in update status

insert comment in update status

--- src/mpd.coffee
+++ src/mpd.coffee
@@ -431,6 +431,8 @@
             @raiseEvent 'onPlaylistUpdate'

   updateStatus: (callback=noop) =>
+    # can't use await/defer yet:
+    # https://github.com/jashkenas/coffee-script/pull/1942#issuecomment-3707044
     @sendCommands ["stats", "replay_gain_status", "status"], (msg) =>
       # no dict comprehensions :(
       # https://github.com/jashkenas/coffee-script/issues/77


This is going to get annoying

This is going to get annoying

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -1,7 +1,7 @@
 'editor':
-  'fontSize': 15
   'showIndentGuide': true
   'fontFamily': 'Menlo'
+  'fontSize': 15
 'core':
   'excludeVcsIgnoredPaths': true
   'ignoredNames': [
@@ -31,5 +31,5 @@
 'welcome':
   'showOnStartup': false
 'release-notes':
-  'viewedVersion': '0.85.0'
+  'viewedVersion': '0.89.0'
 'tree-view': {}


update atom stuff

update atom stuff

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -17,6 +17,7 @@
     ruby:
       default_beautifier: "Ruby Beautify"
   core:
+    closeDeletedFileTabs: true
     disabledPackages: [
       "background-tips"
       "exception-reporting"
@@ -71,3 +72,6 @@
 ".js.jsx.react.source":
   editor:
     autoIndent: true
+".php.source.wordpress":
+  editor:
+    scrollPastEnd: true


updated atom prefs

updated atom prefs

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -1,34 +1,35 @@
-'editor':
-  'showIndentGuide': true
-  'fontFamily': 'Menlo'
-  'fontSize': 15
-  'invisibles': {}
-'core':
-  'ignoredNames': [
-    '.bundle'
-    '.git'
-    'log'
-    'repositories'
-    'tmp'
-    'vendor'
-  ]
-  'themes': [
-    'unity-ui'
-    'glacier-syntax'
-  ]
-  'hideGitIgnoredFiles': true
-  'disabledPackages': [
-    'background-tips'
-    'exception-reporting'
-    'metrics'
-  ]
-'spell-check':
-  'grammars': [
-    'text.plain'
-    'source.gfm'
-    'text.git-commit'
-  ]
-'welcome':
-  'showOnStartup': false
-'release-notes':
-  'viewedVersion': '0.89.0'
+'global':
+  'editor':
+    'showIndentGuide': true
+    'fontFamily': 'Menlo'
+    'fontSize': 12
+    'invisibles': {}
+  'core':
+    'ignoredNames': [
+      '.bundle'
+      '.git'
+      'log'
+      'repositories'
+      'tmp'
+      'vendor'
+    ]
+    'themes': [
+      'yosemite-unity-ui'
+      'glacier-syntax'
+    ]
+    'hideGitIgnoredFiles': false
+    'disabledPackages': [
+      'background-tips'
+      'exception-reporting'
+      'metrics'
+    ]
+  'spell-check':
+    'grammars': [
+      'text.plain'
+      'source.gfm'
+      'text.git-commit'
+    ]
+  'welcome':
+    'showOnStartup': false
+  'release-notes':
+    'viewedVersion': '0.89.0'


updating atom config

updating atom config

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -19,19 +19,16 @@
       "one-dark-ui"
       "base16-tomorrow-dark-theme"
     ]
-  csslint:
-    useFoldModeAsDefault: true
+  csslint: {}
   editor:
+    backUpBeforeSaving: true
     confirmCheckoutHeadRevision: false
     fontFamily: "Hack"
-    fontSize: 13
     invisibles:
       space: "⋅"
     lineHeight: 1.3
     scrollPastEnd: true
-    showIndentGuide: true
     showInvisibles: true
-    tabType: "soft"
   emmet: {}
   "exception-reporting":
     userId: "3961d551-c48e-c888-425b-ee566f6d7bf0"
@@ -45,6 +42,18 @@
   jslint:
     useFoldModeAsDefault: true
   jsonlint: {}
+  linter:
+    errorPanelHeight: 143
+    gutterEnabled: false
+  "linter-js-standard":
+    checkStyleDevDependencies: true
+    lintMarkdownFiles: true
+  "linter-php":
+    executablePath: "/usr/local/bin/php"
+  "linter-stylelint":
+    disableWhenNoConfig: true
+    enableHtmlLinting: true
+    useStandard: true
   metrics:
     userId: "05382f6ef5bae6bc567c0fa72c018288be4ed6eb"
   "one-dark-ui":


pass 'env' when loading an AssociationSet

--- src/model/associations/association_set.coffee
+++ src/model/associations/association_set.coffee
@@ -16,9 +16,10 @@
       loadOptions.data = Batman.extend(loadOptions.data, options)

     return callback(undefined, @) unless @foreignKeyValue?
-    @association.getRelatedModel().loadWithOptions loadOptions, (err, records) =>
+    @association.getRelatedModel().loadWithOptions loadOptions, (err, records, env) =>
       @markAsLoaded() unless err
-      callback(err, @)
+
+      callback(err, @, env)

   _getLoadOptions: ->
     loadOptions = data: {}


Unsure initial location range on autofocus. Fixes failing tests in FF.

--- src/trix/controllers/editor_controller.coffee
+++ src/trix/controllers/editor_controller.coffee
@@ -15,7 +15,9 @@

     @setEditor(new Trix.Editor @document)

-    @documentController.focus() if @config.autofocus
+    if @config.autofocus
+      @documentController.focus()
+      @setLocationRange([0, 0]) unless @getLocationRange()

   setEditor: (editor) ->
     return if @editor is editor
@@ -77,6 +79,9 @@

   getSelectionManager: ->
     @selectionManager
+
+  @proxy "getSelectionManager().setLocationRange"
+  @proxy "getSelectionManager().getLocationRange"

   # Attachment manager delegate

@@ -267,7 +272,7 @@
     @documentController.render()

   updateLocationRange: ->
-    @selectionManager.setLocationRange(@editor.locationRange) if @editor.locationRange
+    @setLocationRange(@editor.locationRange) if @editor.locationRange

   render: ->
     @documentController.render()


Serialize HTML when attachments preload. Fixes #347

--- src/trix/controllers/editor_controller.coffee
+++ src/trix/controllers/editor_controller.coffee
@@ -71,6 +71,7 @@

   compositionDidChangeAttachmentPreviewURL: (attachment) ->
     @compositionController.invalidateViewForObject(attachment)
+    @editorElement.notify("change")

   compositionDidRemoveAttachment: (attachment) ->
     managedAttachment = @attachmentManager.unmanageAttachment(attachment)


Fix 'Invalid property value' for trix toolbar default CSS

Chrome tells me that 'collapse' is not a valid property for 'white-space'. Based on trix.css, I think it should be 'nowrap'?

--- src/trix/elements/trix_toolbar_element.coffee
+++ src/trix/elements/trix_toolbar_element.coffee
@@ -3,7 +3,7 @@
 Trix.registerElement "trix-toolbar",
   defaultCSS: """
     %t {
-      white-space: collapse;
+      white-space: nowrap;
     }

     %t .dialog {


Remove spec that was targeting dashboard

--- test/components/student-dashboard.spec.coffee
+++ test/components/student-dashboard.spec.coffee
@@ -52,16 +52,3 @@
       tasks = state.div.querySelectorAll('.-upcoming .task .title')
       expect(_.pluck(tasks, 'textContent'))
         .to.have.deep.equal(['Homework #3', 'Homework #4 (final)'])
-
-  it 'renders only as many events as are available', ->
-    # Time is set after only one event has occured
-    TimeActions.setNow(new Date('2015-04-12T09:15:58.856Z'))
-    renderDashBoard().then (state) ->
-      events = state.div.querySelectorAll('.dont-forget .panel-body .title')
-      # For now all we're doing is rendering the 4 most recent past-due events
-      expect(_.pluck(events, 'textContent'))
-        .to.have.deep.equal([
-          'iReading 1: Force'
-        ])
-      expect(state.div.querySelector('.dont-forget .panel-body>div').className)
-        .to.include('col-xs-12')


Fix side effect from support Serrano 2.1.1

Now that key-based field are allowed to expose a distribution endpoint,
FieldForm must guard against rendering if the field is deemed searchable.

--- src/coffee/cilantro/ui/field/form.coffee
+++ src/coffee/cilantro/ui/field/form.coffee
@@ -125,8 +125,8 @@
             # enumerable condition is a hack since the above control
             # may already have chart-like display...and the hack grows deeper
             # to prevent a chart being added when dealing with dates...
-            if not @model.get('enumerable') and
-               not (@model.get('simple_type') == 'datetime')
+            if not @model.get('enumerable') and not @model.get('searchable') \
+                    and @model.get('simple_type') isnt 'datetime'
                 if @options.showChart and @model.links.distribution?
                     @addControl charts.FieldChart,
                         chart:


Updated window resize docs and unbind event on scope destroy.

--- src/directives/events/window_resize.coffee
+++ src/directives/events/window_resize.coffee
@@ -1,9 +1,25 @@
+###
+@chalk overview
+@name Windows Resize
+
+@description
+Binding custom behavior on window resize event
+
+@param {Expression} mac-window-resize Expression to evaulate on window resize
+###
+
 angular.module("Mac").
   directive("macWindowResize", ["$parse", "$window", ($parse, $window) ->
     restrict: "A"
     link: ($scope, element, attrs) ->
-      callbackFn = $parse attrs.macWindowResize
-      $($window).bind "resize", ($event) ->
+      handler = ($event) ->
+        callbackFn = $parse attrs.macWindowResize
         $scope.$apply -> callbackFn $scope, {$event}
         return true
+
+      angular.element($window).bind "resize", handler
+
+      $scope.$on "destroy", ->
+        angular.element($window).unbind "resize", handler
+
   ])


cb paradise

cb paradise

--- public/js/map.coffee
+++ public/js/map.coffee
@@ -36,6 +36,8 @@
 			color = 'green'

 			coordFocus = e.latlng
+			onMapClick coordFocus
+
 			ad =
 				location: coordinates: coordFocus
 				radius: 500
@@ -63,11 +65,9 @@

 	displayMe: (coord) ->
 		markerMe = L.marker(coord, {icon: icons.red()})
+		.addTo(map)
 		.on 'click', () ->
-			console.log 'TODO'
-		.addTo(map)
-
-
+			console.log 'Its Me =)'

 	addAd: (ad, color = 'blue') ->


Improve selected file label

Improve selected file label

--- src/ext/components/import-files-input.coffee
+++ src/ext/components/import-files-input.coffee
@@ -29,7 +29,12 @@
     for file in files
       dictionary[file.path] = yes
     dictionary
-  _selectedFileCount = lift _selectedFiles, (files) -> "#{Flow.Util.describeCount files.length, 'file'} selected:"
+  _selectedFileCount = lift _selectedFiles, (files) ->
+    if files.length
+      "#{Flow.Util.describeCount files.length, 'file'} selected:"
+    else
+      "(No files selected)"
+
   _hasSelectedFiles = lift _selectedFiles, (files) -> files.length > 0

   importFiles = (files) ->


client: don't depend on dynamic requires

--- client/app/lib/kodingappscontroller.coffee
+++ client/app/lib/kodingappscontroller.coffee
@@ -8,6 +8,30 @@

 FSHelper         = require './util/fs/fshelper'
 registerAppClass = require './util/registerAppClass'
+
+AppClasses =
+  about: require 'about'
+  account: require 'account'
+  ace: require 'ace'
+  activity: require 'activity'
+  admin: require 'admin'
+  dashboard: require 'dashboard'
+  features: require 'features'
+  feeder: require 'feeder'
+  finder: require 'finder'
+  home: require 'home'
+  ide: require 'ide'
+  kites: require 'kites'
+  legal: require 'legal'
+  members: require 'members'
+  migratefromsolo: require 'migratefromsolo'
+  pricing: require 'pricing'
+  showcase: require 'showcase'
+  'stack-editor': require 'stack-editor'
+  stacks: require 'stacks'
+  testrunner: require 'testrunner'
+  # viewer: require 'viewer'
+

 module.exports = class KodingAppsController extends KDController

@@ -29,7 +53,9 @@
     app = globals.config.apps[name]

     @putAppScript app, (err, res) =>
-      AppClass = require res.app.identifier
+
+      AppClass = AppClasses[res.app.identifier] or kd.Object
+
       register = (klass) ->
         registerAppClass klass
         callback err, res


Switched order.  Matches on username first then name

--- client/views/app/messagePopupConfig.coffee
+++ client/views/app/messagePopupConfig.coffee
@@ -13,7 +13,7 @@
 			getFilter: (collection, filter) ->
 				exp = new RegExp("^#{filter}", 'i')
 				Meteor.subscribe 'onlineUsers', filter
-				items = onlineUsers.find({$or: [{name: exp}, {username: exp}]}, {limit: 5}).fetch()
+				items = onlineUsers.find({$or: [{username: exp}, {name: exp}]}, {limit: 5}).fetch()

 				all =
 					_id: '@all'


fix path

fix path

--- components/fair_layout/client/index.coffee
+++ components/fair_layout/client/index.coffee
@@ -3,7 +3,7 @@
 FooterView = require '../../main_layout/footer/view.coffee'
 lightDetector = require '../../light_detector/index.coffee'
 FairNavView = require './nav.coffee'
-MarketingSignupModal = require '../marketing_signup_modal/index.coffee'
+MarketingSignupModal = require '../../marketing_signup_modal/index.coffee'

 module.exports = (options) =>
   globalClientSetup()


added default 0 for field aviso

added default 0 for field aviso

--- layout/Bradesco/Pagamento/Pagamento.coffee
+++ layout/Bradesco/Pagamento/Pagamento.coffee
@@ -231,7 +231,7 @@
         endPos: 230
         length: 1
         required: false
-        type: 'alphanumeric'
+        default: '0'
     }
     {
         field: 'codigos_ocorrencias'


remove restriction on birth year

remove restriction on birth year

--- _source/lib/collections/people.coffee
+++ _source/lib/collections/people.coffee
@@ -86,7 +86,7 @@
   birthYear:
     type: Number
     decimal: false
-    min: 1900
+    # min: 1900
     # max: 2050
     optional: true
   weddingDay:


Added isActiveLanguageRuby function to workspace manager

--- lib/common/levels-workspace-manager.coffee
+++ lib/common/levels-workspace-manager.coffee
@@ -30,6 +30,9 @@
   getActiveLanguage: ->
     return @levelsWorkspace?.getActiveLanguage()

+  isActiveLanguageRuby: ->
+    return @getActiveLanguage()?.getName() == 'Ruby'
+
   getActiveLevel: ->
     return @levelsWorkspace?.getActiveLevel()


Fix UnifiedCollection for unique collections

--- src/marbles/unified_collection.js.coffee
+++ src/marbles/unified_collection.js.coffee
@@ -32,7 +32,7 @@
   # each collection with with collection_id and models
   #
   # Returns an array of models
-  filterAndSortCollectionsModels: (models, callback) =>
+  filterAndSortCollectionsModels: (models, callbacks) =>

     # Sort all collections' models seperately
     # and find the collection with the lowest value
@@ -61,10 +61,15 @@

     for collection_cid, _models of models
       _models = _.filter(_models, (model) =>
-        @sortModelsBy(model) <= lowest_last_value
+
+        if @sortModelsBy(model) <= lowest_last_value
+          true
+        else
+          callbacks.rejectModel?(collection_cid, model)
+          false
       )

-      callback?(collection_cid, _models)
+      callbacks.complete?(collection_cid, _models)

       final_models = final_models.concat(_models)

@@ -84,6 +89,8 @@
     models = {}
     responses = {}
     xhrs = {}
+    collections_params = {}
+    collections_options = {}
     collections_success = {}
     is_success = false

@@ -99,33 +106,41 @@
     failureFn = (collection) =>
       collections_success[collection.cid] = false

-    completeFn = (collection, _models, res, xhr) =>
+    completeFn = (collection, _models, res, xhr, _params, _options) =>
       num_pending_fetches -= 1

       responses[collection.cid] = res
       xhrs[collection.cid] = xhr
+      collections_params[collection.cid] = _.extend {}, _params, params[collection.cid] || {}
+      collections_options[collection.cid] = _.extend {}, _options, options[collection.cid] || {}

       if num_pending_fetches <= 0
-        models = @filterAndSortCollectionsModels(models, (collection_cid, _models) =>
-          if collections_success[collection_cid]
-            options[collection.cid]?.success?(_models, responses[collection_cid], xhrs[collection_cid], params[collection_cid], options[collection_cid])
-          else
-            options[collection.cid]?.failure?(responses[collection_cid], xhrs[collection_cid], params[collection_cid], options[collection_cid])
+        models = @filterAndSortCollectionsModels(models,
+          rejectModel: (collection_cid, model) =>
+            # remove discarded models from collection
+            # to prevent it from being lost in a unique filter
+            @constructor.collection.find(cid: collection_cid).removeIds(model.cid)

-          options[collection_cid]?.complete?(_models, responses[collection_cid], xhrs[collection_cid], params[collection_cid], options[collection_cid])
+          complete: (collection_cid, _models) =>
+            if collections_success[collection_cid]
+              options[collection.cid]?.success?(_models, responses[collection_cid], xhrs[collection_cid], collections_params[collection_cid], collections_options[collection_cid])
+            else
+              options[collection.cid]?.failure?(responses[collection_cid], xhrs[collection_cid], collections_params[collection_cid], collections_options[collection_cid])
+
+            options[collection_cid]?.complete?(_models, responses[collection_cid], xhrs[collection_cid], collections_params[collection_cid], collections_options[collection_cid])
         )

         if is_success
           models = @fetchSuccess(models, options)
-          options.success?(models, responses, xhrs, params, options)
-          @trigger('fetch:success', models, xhrs, params, options)
+          options.success?(models, responses, xhrs, collections_params, options)
+          @trigger('fetch:success', models, xhrs, collections_params, options)
         else
           models = null
-          options.failure?(responses, xhrs, params, options)
-          @trigger('fetch:failure', responses, xhrs, params, options)
+          options.failure?(responses, xhrs, collections_params, options)
+          @trigger('fetch:failure', responses, xhrs, collections_params, options)

-        options.complete?(models, responses, xhrs, params, options)
-        @trigger('fetch:complete', models, responses, xhrs, params, options)
+        options.complete?(models, responses, xhrs, collections_params, options)
+        @trigger('fetch:complete', models, responses, xhrs, collections_params, options)

     for collection in collections
       do (collection) =>


Fix loss of user scenarios promise

Fix loss of user scenarios promise

Fixed the promise containing the user scenarios data so that loading the
interconnector upload form more than once does not cause the loss of the
"Select a scenario" option.

Closes #3571

--- app/assets/javascripts/lib/app.coffee
+++ app/assets/javascripts/lib/app.coffee
@@ -237,12 +237,10 @@
   # deferred will resolve with the collection once the data is available, or
   # immediately if it has eben initialized previously.
   customCurves: =>
-    deferred = $.Deferred()
-
     if @customCurvesDeferred
-      return @customCurvesDeferred
+      return @customCurvesDeferred.promise()
     else
-      @customCurvesDeferred = deferred
+      @customCurvesDeferred = $.Deferred()

       # Ajax request.
       req = $.ajax(
@@ -251,27 +249,27 @@
       )

       req.success((data) =>
-        deferred.resolve(new CustomCurveCollection(data))
+        @customCurvesDeferred.resolve(new CustomCurveCollection(data))
       )

-    return deferred.promise()
+    return @customCurvesDeferred.promise()

   userScenarios: =>
-    deferred = $.Deferred()
-
-    if @userScenariosArray
-      deferred.resolve(@userScenarios)
+    if @userScenariosDeferred
+      return @userScenariosDeferred.promise()
     else
+      @userScenariosDeferred = $.Deferred()
+
       req = $.ajax({
         url: '/saved_scenarios',
         method: 'GET'
       })

       req.done((data) =>
-        @userScenariosArray = data
-        deferred.resolve(@userScenariosArray)
+        @userScenariosDeferred.resolve(data)
       )
-    return deferred.promise()
+
+    return @userScenariosDeferred.promise()

   # Triggered exactly once. Loads custom curve states and disables and sliders which are overridden
   # by the curves.


Improve heatmap

Improve heatmap

--- app/assets/javascripts/maps.js.coffee
+++ app/assets/javascripts/maps.js.coffee
@@ -1,7 +1,7 @@
 jQuery ->
   geocoder = new google.maps.Geocoder()

-  default_latlng = new google.maps.LatLng 43.66365, -79.377594
+  default_latlng = new google.maps.LatLng 43.66365, -79.407594

   if document.getElementById("organization_map") != null
     organization_map = new google.maps.Map document.getElementById("organization_map"),
@@ -79,7 +79,7 @@
   if document.getElementById("preview_map") != null
     preview_map = new google.maps.Map document.getElementById("preview_map"),
       center: default_latlng,
-      zoom: 13,
+      zoom: 12,
       mapTypeId: google.maps.MapTypeId.ROADMAP,
       streetViewControl: false,
       mapTypeControl: false,
@@ -136,7 +136,8 @@

       heatmap_layer = new google.maps.visualization.HeatmapLayer
         data: heatmapData
+        radius: 10

-      heatmap.setCenter heatmapBounds.getCenter()
-      heatmap.fitBounds heatmapBounds
+      # heatmap.setCenter heatmapBounds.getCenter()
+      # heatmap.fitBounds heatmapBounds
       heatmap_layer.setMap heatmap


Update options to maps js

Update options to maps js

--- app/assets/javascripts/maps.js.coffee
+++ app/assets/javascripts/maps.js.coffee
@@ -22,7 +22,13 @@
         )
         mapElement.prop("map", map)
         if options.background?
-          L.tileLayer(options.background.url, options.background.options).addTo map
+          opts = {
+            attribution: options.background.attribution if options.background.attribution?
+            minZoom: options.background.minZoom if options.background.minZoom?
+            maxZoom: options.background.maxZoom if options.background.maxZoom?
+          }
+          opts['bounds'] = options.view.boundingBox if options.view.boundingBox
+          L.tileLayer(options.background.url, opts).addTo map
         else
           # Add an OpenStreetMap tile layer
           L.tileLayer.provider("OpenStreetMap.HOT").addTo map
@@ -51,11 +57,8 @@
           maxWidth: 200
         )

-        #Fixme: handle bad url
-        try
-          # Bounding box
-          map.fitBounds L.latLngBounds(options.view.boundingBox)  if options.view and options.view.boundingBox
-        catch e then console.log e
+        # Bounding box
+        map.fitBounds L.latLngBounds(options.view.boundingBox)  if options.view and options.view.boundingBox

         mapElement.prop "mapLoaded", true
       return


clean messages on the wall if messages are deleted entirely

--- app/assets/javascripts/wall.js.coffee
+++ app/assets/javascripts/wall.js.coffee
@@ -23,7 +23,12 @@
       main.draw(0)
   draw: (start) ->
     current = wall.messages.slice(start, start + SIZE)
-    if current.length > 0
+    if wall.messages.length == 0
+      way.set "messages", []
+      setTimeout ->
+        main.load(wall_id)
+      , 2000
+    else if current.length > 0
       way.set "messages", current

       setTimeout ->


Model events already propagate to collection

--- app/js/lib/backbone-selectable.coffee
+++ app/js/lib/backbone-selectable.coffee
@@ -22,8 +22,7 @@
     previous = @selected
     @selected = model
     previous.trigger 'unselected', model, args... if previous
-    model.trigger 'selected', previous, args... if model
-    @trigger 'selected', model, previous, args...
+    model.trigger 'selected', model, previous, args... if model

   next: ->
     index = @indexOf(@selected)


Add delay before starting end sequence

Add delay before starting end sequence

--- app/scripts/lazerscripts/lunch.coffee
+++ app/scripts/lazerscripts/lunch.coffee
@@ -253,8 +253,8 @@
         @setScenery 'Skyline'
         @gainHeight(600, duration: 0)
         @setSpeed 150
-        @wait 3000
-      )
+      )
+      @wait 3000
       @parallel(
         @sequence(
           @disableControls()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -30,8 +30,10 @@
 Controllable = (I={}, self) ->
   Object.reverseMerge I,
     speed: 1
+    velocity: Point(0, 0)

   self.bind "update", ->
+    I
     if keydown.left
       I.x -= I.speed


resurrection.teamwork: destroy workspace itself

--- client/landing/CollaborativeWorkspace/collaborativeworkspace.coffee
+++ client/landing/CollaborativeWorkspace/collaborativeworkspace.coffee
@@ -163,7 +163,7 @@
     @loader.hide()

   startNewSession: ->
-    @destroySubViews()
+    @destroy()
     options = @getOptions()
     delete options.sessionKey
     @addSubView new CollaborativeWorkspace options
@@ -184,9 +184,8 @@
     options                = @getOptions()
     options.sessionKey     = newOptions.sessionKey.trim()
     options.joinedASession = yes
-    @destroySubViews()
-
     @forceDisconnect()
+    @destroy()

     @addSubView new CollaborativeWorkspace options


'Updating for version 0.12.5dev16'

'Updating for version 0.12.5dev16'

--- bokehjs/src/coffee/version.coffee
+++ bokehjs/src/coffee/version.coffee
@@ -1 +1 @@
-export version = '0.12.5dev15'
+export version = '0.12.5dev16'


fixes typo in command creation

fixes typo in command creation

--- tests/infrastructure/commit_store.unit.coffee
+++ tests/infrastructure/commit_store.unit.coffee
@@ -41,7 +41,7 @@

       sourceId = '123'
       testEvent = new TestEvent sourceId: sourceId
-      testCommand = new TestCommand sourceId: sourceId
+      testCommand = new TestCommand targetId: sourceId

       changes = events: [testEvent], commands: [testCommand]
       expectedVersion = 0


More unit tests.

More unit tests.

--- tests/jasmine/client/unit/editing_spec.coffee
+++ tests/jasmine/client/unit/editing_spec.coffee
@@ -1,6 +1,4 @@
 describe 'The edited item', ->
-  it 'is undefined by default', ->
-    expect(Session.get('itemEdited')).toEqual undefined
   it 'can be set', ->
     start_editing {_id: 'item'}
     expect(Session.get('itemEdited')).toEqual 'item'
@@ -8,3 +6,20 @@
     start_editing {_id: 'item'}
     stop_editing()
     expect(Session.get('itemEdited')).toEqual null
+  it 'is null when the user starts submitting an item', ->
+    start_editing {_id: 'item'}
+    start_submitting 'source'
+    expect(Session.get('itemEdited')).toEqual null
+
+describe 'The submitted kind', ->
+  it 'can be set', ->
+    start_submitting 'source'
+    expect(Session.get('kindSubmitting')).toEqual 'source'
+  it 'is null after stopping the submitting', ->
+    start_submitting 'source'
+    stop_submitting()
+    expect(Session.get('kindSubmitting')).toEqual null
+  it 'is null when the user starts editing an item', ->
+    start_submitting 'source'
+    start_editing {_id: 'item'}
+    expect(Session.get('kindSubmitting')).toEqual null


Pass user to User::redactFor

Pass user to User::redactFor

--- server/packages/user/index.coffee
+++ server/packages/user/index.coffee
@@ -97,7 +97,7 @@
 	User::redactFor = (user) ->

 		# Decrypt the e-mail if redacting for the same user.
-		redactFor.call(this).bind({}).then((@redacted) ->
+		redactFor.call(this, user).bind({}).then((@redacted) ->
 			return @redacted.email unless user.id?
 			return @redacted.email if user.id isnt @redacted.id


update record count on insert

update record count on insert

--- shared/ironTableController.coffee
+++ shared/ironTableController.coffee
@@ -436,7 +436,7 @@
           CoffeeAlerts.error("Error updating " + @_recordName() + " : #{error.reason}")
         else
           CoffeeAlerts.success(@_recordName() + " updated")
-
+          @fetchRecordCount()

   newRecord: ->
     CoffeeModal.form(@formTemplate, @formData('insert'), @saveNewRecord, 'New ' + @_recordName().capitalize())
@@ -446,12 +446,13 @@
     if yesNo
       if @collection().insertOk(rec) and @checkRequiredFields(rec)
         if @collection.methodOnInsert
-          Meteor.call @collection.methodOnInsert, rec, (error, number) ->
+          Meteor.call @collection.methodOnInsert, rec, (error, number) =>
             if error
               console.log("Error saving " + @_recordName(), error)
               CoffeeAlerts.error("Error saving " + @_recordName() + " : #{error.reason}")
             else
               CoffeeAlerts.success(@_recordName() + " created")
+              @fetchRecordCount()
         else
           @collection().insert rec, (error, effectedCount) =>
             if error
@@ -459,6 +460,7 @@
               CoffeeAlerts.error("Error saving " + @_recordName() + " : #{error.reason}")
             else
               CoffeeAlerts.success(@_recordName() + " created")
+              @fetchRecordCount()
       else
         CoffeeAlerts.error("Error could not save new " + @_recordName())


Fix spec for ignoring numbers over 1000

Fix spec for ignoring numbers over 1000

--- coffee/calculator.coffee
+++ coffee/calculator.coffee
@@ -7,7 +7,7 @@
     for numberString in numberStrings
       num = parseInt numberString
       negatives.push num if num && num < 0
-      result += num if num
+      result += num if num && num <= 1000
     throw ('Negative numbers are not allowed: ' + negatives.join(', ')) if negatives.length > 0
     result


fix missig anon user

fix missig anon user

--- vue/src/shared/models/event_model.coffee
+++ vue/src/shared/models/event_model.coffee
@@ -1,6 +1,7 @@
 import BaseModel from '@/shared/record_store/base_model'
 import i18n from '@/i18n.coffee'
 import {invokeMap, without} from 'lodash'
+import AnonymousUserModel   from '@/shared/models/anonymous_user_model'

 export default class EventModel extends BaseModel
   @singular: 'event'


Remove pretty formatting of React HTML output.

It’s not really necessary given the inspector and causes that annoying layout issue due to whitespace.

--- compiler/emitFile.coffee
+++ compiler/emitFile.coffee
@@ -27,9 +27,6 @@
                 if React?.isValidComponent(file_content)
                     output_content = React.renderComponentToStaticMarkup(file_content)
                     output_content = "<!doctype html>#{ output_content }"
-                    unless process.env.NODE_ENV is 'production'
-                        { prettyPrint } = require 'html'
-                        output_content = prettyPrint(output_content)
                     return ['text/html', output_content]

                 # It looks like a React component but somehow React wasn't


remove mystery missing user debug

remove mystery missing user debug

--- resources/assets/coffee/react/beatmap-discussions/discussion.coffee
+++ resources/assets/coffee/react/beatmap-discussions/discussion.coffee
@@ -79,7 +79,6 @@

     user = @props.users[@props.discussion.user_id] ? @props.users[null]
     group = if user.id == @props.beatmapset.user_id then mapperGroup else user.groups?[0]
-    console.log user

     div
       className: topClasses


Thoughts

Thoughts

--- src/modules/zwadonk/ZwadonkController.coffee
+++ src/modules/zwadonk/ZwadonkController.coffee
@@ -1,5 +1,6 @@
 ###*
 ZwadonkController. Responsible for the zwadonk view.
+This is supposed to be dispatched work from the ModulesController
 ###
 app.controller "ZwadonkController", ($scope, LinkBagData, LinkBagUtils) ->
   $scope.whatsMyName = "Zwadonk"


Rich text component saves data as object

--- src/js/components/RichTextComponent.coffee
+++ src/js/components/RichTextComponent.coffee
@@ -34,17 +34,17 @@
       placeholder: 'Type your text'

     @editor = $element[0].getElementsByClassName('rich-text-editor')[0]
-    @editor.innerHTML = initialContent or ''
+    @editor.innerHTML = initialContent.html or ''

     @editorInstance = new MediumEditorExtended @editor, opts

     for event in ['keyup', 'input', 'blur', 'paste']
       @editor.addEventListener event, ()=>
-        update @editor.innerHTML
+        update html: @editor.innerHTML

   selected: ($scope, $element, update)->
     $element[0].getElementsByClassName('rich-text-editor')[0].focus()

   unselected: ($scope, $element, update)->
-    update @editor.innerHTML
+    update html: @editor.innerHTML


added null check into response.outputFiles?

--- services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
+++ services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
@@ -50,8 +50,9 @@

 			# make a cache to look up files by name
 			fileByPath = {}
-			for file in response.outputFiles
-				fileByPath[file.path] = file
+			if response?.outputFiles?
+				for file in response?.outputFiles
+					fileByPath[file.path] = file

 			if response.status == "timedout"
 				$scope.pdf.view = 'errors'


prevent scroll behavior when changing org categories

--- app/main.cjsx
+++ app/main.cjsx
@@ -21,10 +21,11 @@
 shouldUpdateScroll = (prevRouterProps, routerProps) ->
   pathname = routerProps.location.pathname.split('/')
   isStats = ('stats' in pathname) and ('projects' in pathname)
-  isSubjectTalk = ('talk' in pathname) and ('talk' in pathname)
+  isSubjectTalk = ('talk' in pathname) and ('subjects' in pathname)
   isLabVisibility = ('lab' in pathname) and ('visibility' in pathname)
+  isOrganization = ('organizations' in pathname)
   hashChange = routerProps.location.hash.length
-  if isStats or hashChange or isSubjectTalk or isLabVisibility
+  if isStats or hashChange or isSubjectTalk or isLabVisibility or isOrganization
     false
   else
     true


Make 'options' parameter for ledger.utils.promise.throttle optional ;p

--- app/src/utils/promise/throttle.coffee
+++ app/src/utils/promise/throttle.coffee
@@ -3,7 +3,7 @@

 _.extend ledger.utils.promise,

-  throttle: (func, wait, {immediate}) ->
+  throttle: (func, wait, {immediate} = {}) ->
     deferred = null
     args = undefined
     immediate ?= no


Consider length of added text when allowing newline deletion mutations

--- src/trix/controllers/input_controller.coffee
+++ src/trix/controllers/input_controller.coffee
@@ -103,7 +103,7 @@
           if unexpectedNewlineAddition
             textAdded.replace(/\n$/, "").length or -1
           else
-            1
+            textAdded?.length or 1
         if @responder?.positionIsBlockBreak(range[1] + offset)
           return true


#387: deferreds for translator

#387: deferreds for translator

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

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

 Zotero.BetterBibTeX.CAYW.Formatter = {
   latex: (citations, config) ->
@@ -268,4 +273,16 @@
     prefix = if citekeys.length == 1 && citekeys[0].toLowerCase() == citekeys[0] then '@' else '#'
     citekeys = ("#{prefix}#{citekey}" for citekey in citekeys).join(',')
     return "[#{label}](#{citekeys})"
+
+  bibtex: (citations, options = {}) ->
+    items = Zotero.Items.get((citation.id for citation of citations))
+
+    deferred = Q.defer()
+    Zotero.BetterBibTeX.translate(Zotero.BetterBibTeX.getTranslator('bibtex'), {items: items}, {}, (err, result) ->
+      if err
+        deferred.reject(err)
+      else
+        deferred.fulfill(result)
+    )
+    return deferred.promise
 }


Social: add data field to post for storing extra data like embed.ly

--- client/Social/Activity/AppController.coffee
+++ client/Social/Activity/AppController.coffee
@@ -24,10 +24,10 @@

   post: (options = {}, callback = noop) ->

-    {body}      = options
+    {body, data} = options
     {socialapi} = KD.singletons

-    socialapi.message.post {body}, callback
+    socialapi.message.post {body, data}, callback

   edit: (options = {}, callback = noop) ->


fix update and reboot missing function call

--- client/app/views/config_applications.coffee
+++ client/app/views/config_applications.coffee
@@ -40,6 +40,7 @@
         @applications = new Application()
         @stackApps.fetch reset: true
         @displayDevices()
+        @stackApplications = new StackApplication

     openUpdatePopover: (slug) ->
         @applicationList.openUpdatePopover slug


Sidebar: #koding special casing removed from topic items

--- client/Social/Activity/sidebar/topicitem.coffee
+++ client/Social/Activity/sidebar/topicitem.coffee
@@ -10,7 +10,7 @@

     super options, data

-    @followButton = if name in ['koding', 'public']
+    @followButton = if name is 'public'
     then new KDCustomHTMLView
     else new TopicFollowButton {}, @getData()


Activity: removed bug app related code

Activity: removed bug app related code

--- client/Social/Activity/views/inputwidget.coffee
+++ client/Social/Activity/views/inputwidget.coffee
@@ -32,15 +32,6 @@

     @buttonBar    = new KDCustomHTMLView
       cssClass    : "widget-button-bar"
-
-    @bugNotification = new KDCustomHTMLView
-      cssClass : 'bug-notification hidden'
-      partial  : '

Posts tagged
-        with #bug will be
-        moved to
-        Bug Tracker.'
-
-    @bugNotification.bindTransitionEnd()

     @previewIcon = new KDCustomHTMLView
       tagName    : "span"
@@ -60,7 +51,6 @@

     @input.on "TokenAdded", (type, token) =>
       if token.slug is "bug" and type is "tag"
-        @bugNotification.show()
         @setClass "bug-tagged"

     # FIXME we need to hide bug warning in a proper way ~ GG
@@ -71,15 +61,8 @@
       @helperView?.checkForCommonQuestions val
       if val.indexOf("5051003840118f872e001b91") is -1
         @unsetClass 'bug-tagged'
-        @bugNotification.hide()
-
-    @on "SubmitStarted", =>
-
-      @hidePreview()  if @preview
-
-      @unsetClass "bug-tagged"
-      @bugNotification.once 'transitionend', =>
-        @bugNotification.hide()
+
+    @on "SubmitStarted", => @hidePreview()  if @preview

   submit: (value) ->
@@ -246,7 +229,6 @@
     @addSubView @input
     @addSubView @embedBox
     @addSubView @buttonBar
-    @addSubView @bugNotification
     @addSubView @helperView
     @buttonBar.addSubView @submitButton
     @buttonBar.addSubView @previewIcon


Activity: isPageBottom helper added

Activity: isPageBottom helper added

--- client/Social/Activity/views/messagepane.coffee
+++ client/Social/Activity/views/messagepane.coffee
@@ -127,7 +127,12 @@

-
+  isPageAtBottom: ->
+
+    {innerHeight}             = window
+    {scrollHeight, scrollTop} = document.body
+
+    scrollTop + innerHeight >= scrollHeight

   scrollDown: (item) ->


wip with Son

wip with Son

--- client/landing/app/MainApp/mq.config.coffee
+++ client/landing/app/MainApp/mq.config.coffee
@@ -3,7 +3,7 @@
   options = {
     encrypted: yes
     sockURL: 'http://localhost:8008/subscribe'
-    authEndPoint: 'http://localhost:8008/auth'
+    authEndPoint: 'http://localhost:3000/auth'
   }
   switch KD.env
     when 'beta'