fix(thread-list): Random error when rapidly clicking row after launch

--- src/flux/stores/observable-list-data-source.coffee
+++ src/flux/stores/observable-list-data-source.coffee
@@ -58,6 +58,7 @@
     @_resultSet.modelAtOffset(offset)

   getById: (id) ->
+    return null unless @_resultSet
     @_resultSet.modelWithId(id)

   indexOfId: (id) ->


Remove unused @numberOfFrozenCols from Gradebook (#260)

`@numberOfFrozenCols` was removed in an upstream change:

  > instructure/canvas-lms@62db47a269d68126c4e7183effbddc0d55285b20

Our SFU MOD to modify `@numberOfFrozenCols` is no longer needed because
the frozen column count is now determined by the length of
`@parentColumns`, which is where we have always added the SIS ID column.

This commit should not result in any functional changes.

--- app/coffeescripts/gradebook/Gradebook.coffee
+++ app/coffeescripts/gradebook/Gradebook.coffee
@@ -120,8 +120,6 @@
       @showInactiveEnrollments =
         @options.settings['show_inactive_enrollments'] == "true"
       @totalColumnInFront = UserSettings.contextGet 'total_column_in_front'
-      @numberOfFrozenCols = if @totalColumnInFront then 3 else 2
-      ++@numberOfFrozenCols # SFU MOD - CANVAS-188 Add extra column for SIS ID
       @gradingPeriods = GradingPeriodsApi.deserializePeriods(@options.active_grading_periods)
       if @options.grading_period_set
         @gradingPeriodSet = GradingPeriodSetsApi.deserializeSet(@options.grading_period_set)


protect cosign to admins

protect cosign to admins

--- apps/consignments/routes.coffee
+++ apps/consignments/routes.coffee
@@ -7,6 +7,7 @@
 landing = new JSONPage name: 'consignments/landing'

 @landing = (req, res, next) ->
+  return next() unless req.user?.get('type') is 'Admin'
   recentlySold = new Items [], item_type: 'Artwork'
   inDemand = new Items [], item_type: 'Artist'


Reformatted the for loops.

Reformatted the for loops.

--- src/rambling.slider.coffee
+++ src/rambling.slider.coffee
@@ -277,17 +277,16 @@
     if settings.controlNav
       ramblingControl = $('

')
       slider.append ramblingControl
-      for i in [0..(kids.length - 1)]
-        do (i) ->
-          if settings.controlNavThumbs
-            child = kids.eq i
-            child = child.find('img:first') if not child.is('img')
-            if settings.controlNavThumbsFromRel
-              ramblingControl.append('')
-            else
-              ramblingControl.append('')
-
-          else ramblingControl.append('' + (i + 1) + '')
+      for i in [0..(kids.length - 1)] then do (i) ->
+        if settings.controlNavThumbs
+          child = kids.eq i
+          child = child.find('img:first') if not child.is('img')
+          if settings.controlNavThumbsFromRel
+            ramblingControl.append('')
+          else
+            ramblingControl.append('')
+
+        else ramblingControl.append('' + (i + 1) + '')

       ###
       Set initial active link
@@ -368,12 +367,11 @@
     Add slices for slice animations
     ###
     createSlices = (slider, settings, vars) ->
-      for i in [0..(settings.slices - 1)]
-        do (i) ->
-          sliceWidth = Math.round(slider.width() / settings.slices)
-          animationContainer = slider
-          animationContainer = slider.find('#rambling-animation') if settings.adaptImages
-          animationContainer.append(functions.getRamblingSlice(sliceWidth, i, settings.slices, vars))
+      for i in [0..(settings.slices - 1)] then do (i) ->
+        sliceWidth = Math.round(slider.width() / settings.slices)
+        animationContainer = slider
+        animationContainer = slider.find('#rambling-animation') if settings.adaptImages
+        animationContainer.append(functions.getRamblingSlice(sliceWidth, i, settings.slices, vars))

     ###
     Add boxes for box animations
@@ -382,13 +380,11 @@
       boxWidth = Math.round(slider.width() / settings.boxCols)
       boxHeight = Math.round(slider.height() / settings.boxRows)

-      for rows in [0..(settings.boxRows - 1)]
-        do (rows) ->
-          for cols in [0..(settings.boxCols - 1)]
-            do (cols) ->
-              animationContainer = slider
-              animationContainer = slider.find('#rambling-animation') if settings.adaptImages
-              animationContainer.append(functions.getRamblingBox(boxWidth, boxHeight, rows, cols, settings, vars))
+      for rows in [0..(settings.boxRows - 1)] then do (rows) ->
+        for cols in [0..(settings.boxCols - 1)] then do (cols) ->
+          animationContainer = slider
+          animationContainer = slider.find('#rambling-animation') if settings.adaptImages
+          animationContainer.append(functions.getRamblingBox(boxWidth, boxHeight, rows, cols, settings, vars))

     ###
     Private run method
@@ -648,29 +644,27 @@
         ###
         Run animation
         ###
-        for cols in [0..(settings.boxCols * 2 - 1)]
-          do (cols) ->
-            prevCol = cols
-            for rows in [0..(settings.boxRows - 1)]
-              do (rows) ->
-                if prevCol >= 0 and prevCol < settings.boxCols
-                  ((row, col, time, i, totalBoxes) ->
-                    box = $(box2Darr[row][col])
-                    w = box.width()
-                    h = box.height()
-                    if settings.effect is 'boxRainGrow' or vars.randAnim is 'boxRainGrow' or settings.effect is 'boxRainGrowReverse' or vars.randAnim is 'boxRainGrowReverse'
-                      box.width(0).height(0)
-                    if i is totalBoxes - 1
-                      setTimeout (-> box.animate({ opacity:'1', width:w, height:h }, settings.animSpeed / 1.3, '', -> slider.trigger('rambling:animFinished'))),
-                        100 + time
-                    else
-                      setTimeout (-> box.animate({ opacity:'1', width:w, height:h }, settings.animSpeed / 1.3)), 100 + time
-                  )(rows, prevCol, timeBuff, i, totalBoxes)
-                  i++
-
-                prevCol--
-
-            timeBuff += 100
+        for cols in [0..(settings.boxCols * 2 - 1)] then do (cols) ->
+          prevCol = cols
+          for rows in [0..(settings.boxRows - 1)] then do (rows) ->
+            if prevCol >= 0 and prevCol < settings.boxCols
+              ((row, col, time, i, totalBoxes) ->
+                box = $(box2Darr[row][col])
+                w = box.width()
+                h = box.height()
+                if settings.effect is 'boxRainGrow' or vars.randAnim is 'boxRainGrow' or settings.effect is 'boxRainGrowReverse' or vars.randAnim is 'boxRainGrowReverse'
+                  box.width(0).height(0)
+                if i is totalBoxes - 1
+                  setTimeout (-> box.animate({ opacity:'1', width:w, height:h }, settings.animSpeed / 1.3, '', -> slider.trigger('rambling:animFinished'))),
+                    100 + time
+                else
+                  setTimeout (-> box.animate({ opacity:'1', width:w, height:h }, settings.animSpeed / 1.3)), 100 + time
+              )(rows, prevCol, timeBuff, i, totalBoxes)
+              i++
+
+            prevCol--
+
+          timeBuff += 100

     ###
     For debugging


Simplify koding-config-manager module

Simplify koding-config-manager module

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>

--- node_modules_koding/koding-config-manager/config.coffee
+++ node_modules_koding/koding-config-manager/config.coffee
@@ -1,40 +1,16 @@
-# author: devrim@koding
-# 1/7/2013
-# update: 6/22/2014
+_  = require 'underscore'

-fs              = require 'fs'
-{extend}        = require 'underscore'
+KONFIG = {}

-do ->
-    unless 'first' of Array.prototype
-      Object.defineProperty Array.prototype, 'first',
-        get : -> this[0]
-    unless 'last' of Array.prototype
-      Object.defineProperty Array.prototype, 'last',
-        get : -> this[this.length-1]
+try
+  module.exports = KONFIG = JSON.parse process.env.KONFIG_JSON
+catch err
+  console.error 'error: could not parse KONFIG_JSON environment variable'
+  console.error err
+  process.exit 1

-
-class Config
-  @load: (name)->
-
-    try
-      cfg = JSON.parse process.env['KONFIG_JSON']
-      cfg.getConfigScriptTag = (mixin={})->
-        config = extend {}, @client.runtimeOptions, mixin
-        """
-        <script>var KD = #{JSON.stringify {config}};</script>
-        """
-
-      return cfg
-    catch err
-      console.log "COULDN'T FIND/PARSE env[KONFIG_JSON]"
-      console.log "exiting now.",err
-      process.exit()
-
-
-  @printJson:(name)->
-    console.log JSON.stringify @load name
-
-
-
-module.exports = Config
+KONFIG.getConfigScriptTag = (mixin = {}) ->
+  config = _.extend {}, @client.runtimeOptions, mixin
+  """
+  <script>var KD = #{JSON.stringify {config}};</script>
+  """


fixed epoch-stats graphs

fixed epoch-stats graphs

--- src/art/engine/dev_tools/global_epoch_stats.coffee
+++ src/art/engine/dev_tools/global_epoch_stats.coffee
@@ -129,7 +129,6 @@
       {w, h} = size
       {sampleTimeRange, stats} = @
       sampleWidth = floor (w / sampleTimeRange) / 60
-
       for stat in stats
         stat.drawSample bitmap, drawMatrix, sampleWidth, sampleField, h
       null
@@ -142,7 +141,7 @@
         sampleWidth = floor (w / sampleTimeRange) / 60
         xScale = (w - sampleWidth) / sampleTimeRange
         yScale = h / maxMs
-        Matrix.scale(1, -1).translate(-minSampleTime, 0).scale(xScale, yScale).translate(legendWidth, h)
+        Matrix.scaleXY(1, -1).translateXY(-minSampleTime, 0).scaleXY(xScale, yScale).translateXY(legendWidth, h)

     drawLabeledHLine: (bitmap, x1, x2, y, clr, label) ->
       bitmap.drawRectangle null, rect(x1, y, x2-x1, 1), color:color clr


Updating parse_config_file resource snippet

--- snippets/parse_config_file.cson
+++ snippets/parse_config_file.cson
@@ -1,4 +1,4 @@
 '.source.inspec':
   'Describe parse_config_file':
     'prefix': 'parse_config_file'
-    'body': "# http://inspec.io/docs/reference/resources/parse_config_file/\ndescribe parse_config_file('${1:name}') do\n  it { should${2:_not} ${3:exist} }${4}\nend"
+    'body': "# http://inspec.io/docs/reference/resources/parse_config_file/\noptions = {\n  assignment_re: /${2:pattern}/,\n  multiple_values: ${3:true||false}\n}\n\ndescribe parse_config_file('${1:path}', options) do\n  its('${4:setting}') { should${5:_not} eq '${6:value}' }${7}\nend"


Move undo/redo/zoom/clear to bottom

Move undo/redo/zoom/clear to bottom

--- src/reactGUI/Picker.coffee
+++ src/reactGUI/Picker.coffee
@@ -26,9 +26,16 @@
       ),
       if toolButtonComponents.length % 2 != 0
         (div {className: 'toolbar-button thin-button disabled'})
-      UndoRedoButtons({lc, imageURLPrefix}),
-      ZoomButtons({lc, imageURLPrefix})
-      ClearButton({lc})
+      (div style: {
+          position: 'absolute',
+          bottom: 0,
+          left: 0,
+          right: 0,
+        },
+        UndoRedoButtons({lc, imageURLPrefix}),
+        ZoomButtons({lc, imageURLPrefix})
+        ClearButton({lc})
+      )
     )


Explain the scope of the loop inside tessellate: (poly) =>

--- src/ThreeCSG.coffee
+++ src/ThreeCSG.coffee
@@ -78,6 +78,9 @@
     {f, b, count} = {f: [], b: [], count: poly.vertices.length}

     return [poly] unless @classifySide(poly) == SPANNING
+    # vi and vj are the current and next Vertex
+    # i  and j  are the indexes of vi and vj
+    # ti and tj are the classifications of vi and vj
     for vi, i in poly.vertices
       vj = poly.vertices[(j = (i + 1) % count)]
       [ti, tj] = (@classifyVertex v for v in [vi, vj])


Pull creation of expectations out of ExpectationList

--- src/TinyMock.coffee
+++ src/TinyMock.coffee
@@ -132,7 +132,9 @@
     expectation._returns

   mock_method.create_expectation = ->
-    expectations.create_expectation()
+    expectation = new Expectation()
+    expectations.add(expectation)
+    expectation

   mock_method.restore_original_method = ->
     if original_method?
@@ -207,11 +209,9 @@

   constructor: ->
     @_list = []
-
-  create_expectation: ->
-    expectation = new Expectation()
+
+  add: (expectation) ->
     @_list.push(expectation)
-    expectation

   # returns undefined if not found
   find_expectation: (args...) ->


Add se TODOs

Add se TODOs

--- dev/se.cson
+++ dev/se.cson
@@ -1,4 +1,4 @@
-information_border_se:
+'information_border_se':
   category: 'information'
   name: 'European border crossing into Sweden'
   elements: [
@@ -6,3 +6,45 @@
     { type: 'europe_stars', color: 'yellow' }
     { type: 'content-5', color: 'white', content: 'SVERIGE' }
   ]
+
+'cyclists-crossing-ahead':
+  category: 'danger'
+  name: 'cyclists crossing ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'pedestrian-crossing':
+  category: 'danger'
+  name: 'pedestrian crossing'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'children':
+  category: 'danger'
+  name: 'children'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'give-way-to-oncoming-traffic':
+  category: 'priority'
+  name: 'give way to oncoming traffic'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'no-bicycles':
+  category: 'prohibitory'
+  name: 'no bicycles'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'no-pedestrians':
+  category: 'prohibitory'
+  name: 'no pedestrians'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]


Fix file location error

Fix file location error

--- pi.coffee
+++ pi.coffee
@@ -1,4 +1,4 @@
-Buzzer = require('buzzer')
+Buzzer = require('./buzzer')
 Manager = require('./manager')
 express = require('express');
 buzzer = new Buzzer()


Change view generating according to changes in core

--- src/runners/repeat.coffee
+++ src/runners/repeat.coffee
@@ -8,7 +8,7 @@
     super

     unless @scope.view
-      @scope.template = @el.html()
+      @template = @el.html()

     # Clear runner body
     @el.empty()
@@ -35,6 +35,7 @@
             parentScope: @scope
             item: item
             itemName: @runAttr[0]
+            template: @template

           @append itemController
           itemController
@@ -67,12 +68,6 @@

     @checkFilter()

-  view: ->
-    if @scope.view
-      @el = $(require("#{Neck.Controller.viewsPath}/#{@scope.view}")(@scope))
-    else
-      @el = $(@scope.template)
-
   checkFilter: ->
     if !@scope.filter and !@visible
       @el.show()


Editor#getPosition

Editor#getPosition

--- src/trix/models/editor.coffee
+++ src/trix/models/editor.coffee
@@ -52,12 +52,15 @@

   # Selection

+  getSelectedRange: ->
+    @composition.getSelectedRange()
+
+  getPosition: ->
+    @composition.getPosition()
+
   getClientRectAtPosition: (position) ->
     location = @getDocument().locationFromPosition(position)
     @selectionManager.getClientRectAtLocation(location)
-
-  getSelectedRange: ->
-    @composition.getSelectedRange()

   expandSelectionInDirection: (direction) ->
     @composition.expandSelectionInDirection(direction)


fix personalities hopefully

fix personalities hopefully

--- shared/IdleController.coffee
+++ shared/IdleController.coffee
@@ -379,7 +379,7 @@
         if _.isNaN (my/total) then 0 else val

       $scope.getStatFor = (player, key) ->
-        if key is "__personality" then player.personalityStrings?.length else player.statistics?[key]
+        if key is "__personalities" then player.personalityStrings?.length else player.statistics?[key]

       $scope.totalFromStatistics = (key) ->
         (_.reduce $scope.players, ((prev, player) => prev+((@getStatFor player, key) or 0)), 0) or 0


Silence logging during specs

Silence logging during specs

--- spec/archive-editor-spec.coffee
+++ spec/archive-editor-spec.coffee
@@ -5,6 +5,7 @@
 describe "ArchiveEditor", ->
   describe ".deserialize", ->
     it "returns undefined if no file exists at the given path", ->
+      spyOn(console, 'warn') # Don't log during specs
       editor = new ArchiveEditor(path: path.join(__dirname, 'fixtures', 'nested.tar'))
       state = editor.serialize()
       expect(ArchiveEditor.deserialize(state)).toBeDefined()


apply negative offset to handles, centers close icon

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -116,8 +116,8 @@
     mouseLocation = @toCanvasXPoint(e)
     markBelow = Math.abs mouseLocation - @closestXBelow(mouseLocation)
     markAbove = Math.abs mouseLocation - @closestXAbove(mouseLocation)
-    # 18 is width of mark plus some room on each side
-    markBelow < (18*scale) or markAbove < (18*scale) or mouseLocation in @sortedXCoords()
+    # 24 is width of mark plus some room on each side
+    markBelow < (24*scale) or markAbove < (18*scale) or mouseLocation in @sortedXCoords()

 class Mark
   constructor: (e, @canvasGraph) ->
@@ -131,10 +131,10 @@

-

+

III

-

+

III

     """
@@ -160,7 +160,7 @@

   maxWidth: -> 150 * (@canvasGraph.scale || 1)

-  handleWidth: -> 14 * (@canvasGraph.scale || 1)
+  handleWidth: -> 16 * (@canvasGraph.scale || 1)

   draw: (e) ->
     markLeftX = Math.max @startingPoint + @minWidth() - @maxWidth(),


delete func working

delete func working

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -60,8 +60,6 @@
   add: (mark) -> @all.push(mark)

   remove: (mark) ->
-    console.log "REMOVING", mark
-    console.log (mark in @all)
     console.log @all.splice(@all.indexOf(mark), 1)
     document.getElementById('marks-container').removeChild(mark.element)

@@ -70,7 +68,7 @@
     @all = []

 class Mark
-  MIN_WIDTH = 10
+  MIN_WIDTH = 15
   MAX_WIDTH = 150

   #move to end of marks-container on mousedown => for mousedown event
@@ -160,8 +158,6 @@
     else if e.target.className is "mark"
       @moving = true
       @pointerOffset = (@toCanvasXPoint(e) - @canvasXMin)
-    else if e.target.className is "top-bar"
-      @canvasGraph.marks.remove(@)

   onMouseUp: (e) =>
     e.preventDefault()
@@ -171,11 +167,10 @@
       mark.dragging = false
       mark.moving = false
     @canvasGraph.marks.add(@) unless (@ in @canvasGraph.marks.all)
+    @canvasGraph.marks.remove(@) if e.target.className is "top-bar"

   toCanvasXPoint: (e) -> e.pageX - @canvas.getBoundingClientRect().left - window.scrollX

   pointerXInElement: (e) -> e.offsetX || e.clientX - e.target.offsetLeft + window.pageXOffset - @canvas.getBoundingClientRect().left

-  pointerYInElement: (e) -> e.offsetY || e.clientY - e.target.offsetTop + window.pageYOffset - @canvas.getBoundingClientRect().top
-
 module?.exports = CanvasGraph: CanvasGraph, Marks: Marks, Mark: Mark


Set box-sizing to border-box cause it's better

--- src/utils/createStyles.coffee
+++ src/utils/createStyles.coffee
@@ -68,9 +68,14 @@
   #{normalize}

   html {
+    box-sizing: border-box;
     font-size: #{vr.establishBaseline().fontSize};
     line-height: #{vr.establishBaseline().lineHeight};
     overflow-y: scroll;
+  }
+
+  *, *:before, *:after {
+    box-sizing: inherit;
   }

   body {


fix iscoco again

fix iscoco again

--- app/models/Classroom.coffee
+++ app/models/Classroom.coffee
@@ -208,9 +208,9 @@
       currentLevel = courseLevels.models[currentIndex]
       currentPlaytime = levelSessionMap[currentLevel.get('original')]?.get('playtime') ? 0
       needsPractice = utils.needsPractice(currentPlaytime, currentLevel.get('practiceThresholdMinutes')) and not currentLevel.get('assessment')
-      unless utils.orderedCourseIDs.includes(courseID)
+      if utils.isCodeCombat || !utils.orderedCourseIDs.includes(courseID)
         nextIndex = utils.findNextLevel(levels, currentIndex, needsPractice)
-    if utils.orderedCourseIDs.includes(courseID)
+    if utils.isOzaria and utils.orderedCourseIDs.includes(courseID)
       nextLevelOriginal = findNextLevelsBySession(sessions, courseLevels.models)
       nextLevel = new Level(getLevelsDataByOriginals(courseLevels.models, [nextLevelOriginal])[0])
     else


adicionando todo conteudo de _config.yml para o docpad.cson

--- docpad.cson
+++ docpad.cson
@@ -1,5 +1,7 @@
 {
   templateData:
+
+    # Conference info
     conf:
       name: "Conference name"
       description: "Conference description"
@@ -8,6 +10,8 @@
       address: "Boulevard Kukulcan, 30, México"
       venue: "Coco Bongo"
       city: "Cancún"
+
+    # Site info
     site:
       url: "http://confboilerplate.com"
       favicon: "http://braziljs.org/favicon.ico"
@@ -15,6 +19,8 @@
       images:
         cover: "http://f.cl.ly/items/2X28422q1e3w0C2U1P3H/866591_24254643.jpg"
         facebook: "http://braziljs.org/img/fb-share.jpg"
+
+    # Active sections on the website
     sections:
       about: true
       location: true
@@ -23,4 +29,90 @@
       sponsors: true
       partners: true
       contact: false
+
+    # The entire schedule
+    schedule: [
+      name: "Check-in / Breakfast"
+      time: "9h00"
+    ,
+      name: "Linus Torvalds"
+      photo: "http://f.cl.ly/items/2A3p1N0C3c0n3N3R1w2B/speaker.jpg"
+      bio: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+      company: "Linux Foundation"
+      twitter: "linus"
+      presentation:
+        title: "Digging into a Linux Kernel"
+        description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+        time: "10h00"
+    ,
+      name: "Bill Gates"
+      photo: "http://f.cl.ly/items/2A3p1N0C3c0n3N3R1w2B/speaker.jpg"
+      bio: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+      company: "Microsoft"
+      twitter: "billy95"
+      presentation:
+        title: "Introducing Windows 12"
+        description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+        time: "11h00"
+    ,
+      name: "Lunch"
+      time: "12h00"
+    ,
+      name: "Chuck Norris"
+      photo: "http://f.cl.ly/items/2A3p1N0C3c0n3N3R1w2B/speaker.jpg"
+      bio: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+      company: "Delta Command"
+      twitter: "littlechuck"
+      presentation:
+        title: "How to kill a elephant with one finger"
+        description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+        time: "13h00"
+    ,
+      name: "Steve Jobs"
+      photo: "http://f.cl.ly/items/2A3p1N0C3c0n3N3R1w2B/speaker.jpg"
+      bio: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+      company: "Apple, Inc."
+      twitter: "stevie"
+      presentation:
+        title: "Presenting iPad"
+        description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+        time: "14h00"
+    ,
+      name: "Coffee-break"
+      time: "15h00"
+    ,
+      name: "Mark Zuckerberg"
+      photo: "http://f.cl.ly/items/2A3p1N0C3c0n3N3R1w2B/speaker.jpg"
+      bio: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+      company: "Facebook"
+      twitter: "zuck"
+      presentation:
+        title: "Revealing Facebook Secrets"
+        description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+        time: "16h00"
+    ,
+      name: "Steve Wozniak"
+      photo: "http://f.cl.ly/items/2A3p1N0C3c0n3N3R1w2B/speaker.jpg"
+      bio: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+      company: "Apple, Inc."
+      twitter: "woz"
+      presentation:
+        title: "Why do I prefer Android over iPhone"
+        description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"
+        time: "17h00"
+    ]
+
+    # List of Sponsors
+    sponsors: [
+      name: "Eventick"
+      logo: "http://f.cl.ly/items/1U3p0Z0e2w0I1i3z1X22/Image%202012.09.25%203:00:58%20PM.png"
+      url: "http://eventick.com.br"
+    ]
+
+    # # List of Partners
+    partners: [
+      name: "BrazilJS"
+      logo: "http://f.cl.ly/items/2N3i2W0X2f3c2g2Z2N0f/Untitled-1.png"
+      url: "http://braziljs.org"
+    ]
 }


user imported function

user imported function

--- app/pages/lab/workflow.cjsx
+++ app/pages/lab/workflow.cjsx
@@ -28,11 +28,10 @@
     forceReloader: 0

   workflowLink: ->
-    router = require '../../router'
     [owner, name] = @props.project.slug.split('/')
     viewParams = {owner, name}
     viewQuery = workflow: @props.workflow.id, reload: @state.forceReloader
-    router.makeHref 'project-classify', viewParams, viewQuery
+    @makeHref 'project-classify', viewParams, viewQuery

   render: ->
     window.editingWorkflow = @props.workflow


wait languageMode set since it done in async with TreeSitter

--- spec/motion-general-spec.coffee
+++ spec/motion-general-spec.coffee
@@ -212,6 +212,7 @@
           ensure 'k k', cursor: [0, 3], selectedText: "defg\n\nabcd"

     describe "the j, k keybinding in hardTab text", ->
+      languageModePromse = null
       beforeEach ->
         jasmine.attachToDOM(atom.workspace.getElement())

@@ -223,6 +224,9 @@
           {set, ensure} = vimEditor

         runs ->
+          languageModePromse = new Promise (resolve, reject) ->
+            editor.buffer.onDidChangeLanguageMode(resolve)
+
           set
             grammar: 'source.go'
             textC: """
@@ -238,6 +242,9 @@
             \t}
             }\n
             """
+
+        waitsForPromise -> languageModePromse
+

       it "[tabLength = 2] move up/down bufferRow wise with aware of tabLength", ->
         editor.update(tabLength: 2)


Fix skeleton.

Fix skeleton.

--- src/commands/create.coffee
+++ src/commands/create.coffee
@@ -16,7 +16,7 @@

 copySkeleton = (skeletonPath, rootPath, callback) ->
   skeletonDir = sysPath.join __dirname, '..', '..', 'skeletons'
-  skeletonPath ?= sysPath.join skeletonDir, 'simple-coffee'
+  skeletonPath ?= sysPath.join skeletonDir, 'brunch-with-chaplin'
   logger.debug 'info', "Copying skeleton from #{skeletonPath}"

   copyDirectory = (from) ->


Reinstall attachment editor after rendering

--- src/trix/controllers/document_controller.coffee
+++ src/trix/controllers/document_controller.coffee
@@ -21,6 +21,7 @@
   render: ->
     @delegate?.documentControllerWillRender?()
     @documentView.render()
+    @reinstallAttachmentEditor()
     @delegate?.documentControllerDidRender?()

   focus: ->
@@ -40,6 +41,12 @@
   uninstallAttachmentEditor: ->
     @attachmentEditor?.uninstall()

+  reinstallAttachmentEditor: ->
+    if @attachmentEditor
+      attachment = @attachmentEditor.attachment
+      @uninstallAttachmentEditor()
+      @installAttachmentEditorForAttachment(attachment)
+
   # Attachment controller delegate

   didUninstallAttachmentEditor: ->


Fixed some issues in the benchmarking script

--- scripts/bench.coffee
+++ scripts/bench.coffee
@@ -17,7 +17,7 @@
 			sinceRecord -= flagfall
 			now = Date.now()
 			elapsed = now - lastTime
-			console.log "#{flagfall} ops in #{elapsed} ms - #{1000 * flagfall / elapsed} ops per second"
+			console.log "#{flagfall} ops in #{elapsed} ms - #{Math.floor(1000 * flagfall / elapsed)} ops per second"
 			lastTime = now

 generator = (doc) ->
@@ -58,10 +58,11 @@
 		op

 generate = ->
-	c = new Connection('localhost', 8000)
-	c.open 'spam3', (doc) ->
+	c = new Connection('http://localhost:8000/sjs')
+	c.open 'spam6', (doc, error) ->
+		console.error error if error
 		apply = ->
-			#			console.log "v: #{doc.version} length #{doc.snapshot.length}"
+			#console.log "v: #{doc.version} length #{doc.snapshot.length}"
 			op = generator doc.snapshot
 			doc.submitOp op, ->
 				mark()
@@ -69,4 +70,5 @@

 		apply()

-generate() for [1..10]
+process.title = "benchmark"
+generate() for [1..1]


Do not crash on rx

Do not crash on rx

--- src/test/test_game.coffee
+++ src/test/test_game.coffee
@@ -38,6 +38,6 @@
     t.end()

   test 'game world', (t) ->
-    world = god({}, example)
+    #world = god({}, example)
     t.end()


implement aux pageref reader

implement aux pageref reader

--- src/tex-writer-aux.coffee
+++ src/tex-writer-aux.coffee
@@ -35,12 +35,27 @@
   pipeline.push source
   pipeline.push PS.$split()
   # pipeline.push PS.$show()
+  pipeline.push @$read_pagerefs     S, Z
   pipeline.push @$read_linerefs     S, Z
   pipeline.push @$read_xypositions  S, Z
   pipeline.push on_stop.add PS.$drain()
   PS.pull pipeline...
   #.........................................................................................................
   return null
+
+#-----------------------------------------------------------------------------------------------------------
+@$read_pagerefs = ( S, Z ) ->
+  ### Reads references made by package `zref` provided reference labels start with `mkts-pagenr-` ###
+  ### \zref@newlabel{mkts-pagenr-guide-𤴓}{\default{}\page{4}} ###
+  pattern = /^\\zref@newlabel\{mkts-pagenr-([^}]+)\}.*\page\{([\d]+)\}\}$/
+  target  = Z.pagenrs = {}
+  return $ ( line, send ) ->
+    match                         = line.match pattern
+    return send line unless match?
+    [ _, name, pagenr, ]          = match
+    pagenr                        = parseInt pagenr, 10
+    target[ name ]                = { pagenr, }
+    return null

 #-----------------------------------------------------------------------------------------------------------
 @$read_linerefs = ( S, Z ) ->


:lipstick:

:lipstick:

--- src/packages/command-panel/lib/preview-list.coffee
+++ src/packages/command-panel/lib/preview-list.coffee
@@ -20,7 +20,7 @@
     @on 'core:move-down', => @selectNextOperation(); false
     @on 'core:move-up', => @selectPreviousOperation(); false
     @on 'scroll', =>
-      @renderOperations() if @scrollBottom() >= (@prop('scrollHeight'))
+      @renderOperations() if @scrollBottom() >= @prop('scrollHeight')
     @command 'command-panel:collapse-all', => @collapseAllPaths()
     @command 'command-panel:expand-all', => @expandAllPaths()


Use hasParent() instead of isOnDom()

Use hasParent() instead of isOnDom()

--- src/packages/go-to-line/lib/go-to-line-view.coffee
+++ src/packages/go-to-line/lib/go-to-line-view.coffee
@@ -31,7 +31,7 @@
       @attach()

   detach: ->
-    return unless @isOnDom()
+    return unless @hasParent()

     @detaching = true
     @miniEditor.setText('')


UpdateStackTemplate: pass through description field

--- client/admin/lib/views/stacks/updatestacktemplate.coffee
+++ client/admin/lib/views/stacks/updatestacktemplate.coffee
@@ -3,7 +3,7 @@

 module.exports = updateStackTemplate = (data, callback) ->

-  { template, templateDetails, credentials
+  { template, templateDetails, credentials, description
     title, stackTemplate, machines, config } = data

   title or= 'Default stack template'
@@ -12,7 +12,8 @@

     dataToUpdate = if machines \
       then { machines } else {
-        title, template, credentials, templateDetails, config
+        title, template, credentials
+        templateDetails, config, description
       }

     stackTemplate.update dataToUpdate, (err) ->
@@ -21,6 +22,7 @@
   else

     remote.api.JStackTemplate.create {
-      title, template, credentials, templateDetails, config
+      title, template, credentials
+      templateDetails, config, description
     }, callback


StackTemplateListController: use getGroup helper simply

--- client/app/lib/stacks/stacktemplatelistcontroller.coffee
+++ client/app/lib/stacks/stacktemplatelistcontroller.coffee
@@ -1,6 +1,7 @@
 kd                          = require 'kd'
 remote                      = require('app/remote').getInstance()

+getGroup                    = require 'app/util/getGroup'
 showError                   = require 'app/util/showError'
 AccountListViewController   = require 'account/controllers/accountlistviewcontroller'

@@ -30,8 +31,7 @@
       return if showError err, \
         KodingError : "Failed to fetch stackTemplates, try again later."

-      { groupsController } = kd.singletons
-      currentGroup = groupsController.getCurrentGroup()
+      currentGroup = getGroup()

       stackTemplates.map (template) ->
         template.inuse = template._id in (currentGroup.stackTemplates or [])


Fixed --watch (issue #17).

Fixed --watch (issue #17).

--- bin/zappa.coffee
+++ bin/zappa.coffee
@@ -5,6 +5,7 @@
 fs = require 'fs'
 path = require 'path'
 puts = console.log
+inspect = require('util').inspect
 spawn = require('child_process').spawn
 OptionParser = require('coffee-script/optparse').OptionParser
 child = null
@@ -34,7 +35,10 @@
     fs.writeFile js_path, js

 remove_watch_option = ->
-  process.argv.splice(process.argv.indexOf('-w'), 1).splice(process.argv.indexOf('--watch'), 1)
+  i = process.argv.indexOf('-w')
+  process.argv.splice(i, 1) if i > -1
+  i = process.argv.indexOf('--watch')
+  process.argv.splice(i, 1) if i > -1

 spawn_child = ->
   child = spawn 'zappa', process.argv


Fixed apply already in progress

Fixed apply already in progress

--- src/coffee/control/index.coffee
+++ src/coffee/control/index.coffee
@@ -98,12 +98,11 @@
     PubNub.ngHereNow {channel: 'online'}

     $scope.$on PubNub.ngPrsEv('online'), (event, payload)->
-      $scope.$apply ->
-        if payload.channel == 'online'
-          if payload.event.action == 'leave'
-            setOnlineStatus(payload.event.uuid, false)
-          else if payload.event.action == 'join'
-            setOnlineStatus(payload.event.uuid, true)
+      if payload.channel == 'online'
+        if payload.event.action == 'leave'
+          setOnlineStatus(payload.event.uuid, false)
+        else if payload.event.action == 'join'
+          setOnlineStatus(payload.event.uuid, true)

     getRoomIndex = (id) ->


Rework price and tax amount for export.

Rework price and tax amount for export.

--- src/coffee/export/orders.coffee
+++ src/coffee/export/orders.coffee
@@ -209,13 +209,13 @@
     lineItemXML.e('TaxRate').t(@_toTax(lineItem.taxRate.amount))

   _toAmount: (centAmount) ->
-    amount = _s.numberFormat(_s.toNumber(centAmount / 100, 2), 2)
+    amount = _s.numberFormat(centAmount / 100, 2)

   _toTax: (rate) ->
-    rate = _s.numberFormat(_s.toNumber(rate * 100), 2)
+    rate = _s.numberFormat(rate * 100, 2)

   _toProductPriceFromTotal: (centAmount, quantity) ->
-    amount = _s.numberFormat(_s.toNumber(centAmount / quantity / 100), 2)
+    amount = _s.numberFormat(centAmount / quantity / 100, 2)

   _addressToXML: (address, xml, name) ->
     el = xml.ele(name)


Hallelujah it's not broked'

Hallelujah it's not broked'

--- src/coffee/server/models.coffee
+++ src/coffee/server/models.coffee
@@ -54,11 +54,11 @@

 # callback (err, doc) -> ...
 School.get = (school_id, callback) ->
-  db.collection('schools').findOne school_id, (err, doc) ->
+  db.collection('schools').findById school_id, (err, doc) ->
     async.map doc.plans, (plan_id, callbackOnComplete) ->
-      db.collection('plans').findOne(plan_id, callbackOnComplete)
-    ,(err, plans) ->
-      doc.plans = plans
+      db.collection('plans').findById(plan_id, callbackOnComplete)
+    ,(err, plans2) ->
+      doc.plans = plans2
       callback err, doc


Consistent highlighting of search suggestions.

Also suppress highlighting of matching text in previous suggestions.

(It looks odd to have highlighting in some suggestions but not others,
with no apparent difference to the user.)

--- background_scripts/completion.coffee
+++ background_scripts/completion.coffee
@@ -503,7 +503,7 @@
       for url, suggestion of @previousSuggestions
         continue unless RankingUtils.matches queryTerms, suggestion.title
         # Reset various fields, they may not be correct wrt. the current query.
-        extend suggestion, relevancy: null, html: null, highlightTerms: true, queryTerms: queryTerms
+        extend suggestion, relevancy: null, html: null, queryTerms: queryTerms
         suggestion.relevancy = null
         suggestion


add global input history

add global input history

--- src/ui/views/input.coffee
+++ src/ui/views/input.coffee
@@ -3,6 +3,30 @@
 {later} = require './vutil'

 isModifierKey = (ev) -> ev.ctrlKey || ev.metaKey || ev.shiftKey
+
+cursorToEnd = (el) -> el.selectionStart = el.selectionEnd = el.value.length
+
+history = []
+historyIndex = 0
+historyLength = 100
+historyBackup = ""
+
+historyPush = (data) ->
+  history.push data
+  if history.length == historyLength then history.shift()
+  historyIndex = history.length
+
+historyWalk = (el, offset) ->
+  # if we are starting to dive into history be backup current message
+  if offset is -1 and historyIndex is history.length then historyBackup = el.value
+  historyIndex = historyIndex + offset
+  # constrain index
+  if historyIndex < 0 then historyIndex = 0
+  if historyIndex > history.length then historyIndex = history.length
+  # if don't have history value restore 'current message'
+  val = history[historyIndex] or historyBackup
+  el.value = val
+  setTimeout (-> cursorToEnd el), 1

 module.exports = view (models) ->
     div class:'input', -> div ->
@@ -16,4 +40,7 @@
             if e.keyCode == 13
                 e.preventDefault()
                 action 'sendmessage', e.target.value
+                historyPush e.target.value
                 e.target.value = ''
+            if e.keyIdentifier is "Up" then historyWalk e.target, -1
+            if e.keyIdentifier is "Down" then historyWalk e.target, +1


corrects cursor position when hitting space

--- src/ui/views/input.coffee
+++ src/ui/views/input.coffee
@@ -110,8 +110,14 @@
                 #check for emojis after pressing space
                 if e.keyCode == 32
                     element = document.getElementById "message-input"
+                    # get cursor position
+                    startSel = element.selectionStart
+                    endSel  = element.selectionEnd
                     # Converts emojicodes (e.g. :smile:, :-) ) to unicode
                     element.value = convertEmoji(element.value)
+                    # Set cursor position (otherwise it would go to end of inpu)
+                    element.selectionStart = startSel
+                    element.selectionEnd = endSel
             , onpaste: (e) ->
                 setTimeout () ->
                     if not clipboard.readImage().isEmpty() and not clipboard.readText()


CUI: Added 'sftp' protocol in the url regex. See #52311

--- src/base/CUI.coffee
+++ src/base/CUI.coffee
@@ -740,7 +740,7 @@
 	@urlRegex: new RegExp(
 		"^" +
 		# protocol identifier
-		"(?:(?:(ftp|ftps|https|http))://|)" +
+		"(?:(?:(sftp|ftp|ftps|https|http))://|)" +
 		# user:pass authentication
 		"(?:(\\S+?)(?::(\\S*))?@)?" +
 		"((?:(?:" +


newcourse store has been removed

newcourse store has been removed

--- tutor/src/api/index.coffee
+++ tutor/src/api/index.coffee
@@ -41,7 +41,6 @@
 {ReferenceBookActions} = require '../flux/reference-book'
 {ReferenceBookPageActions} = require '../flux/reference-book-page'
 {ReferenceBookExerciseActions} = require '../flux/reference-book-exercise'
-{NewCourseActions, NewCourseStore} = require '../flux/new-course'
 {NotificationActions} = require '../flux/notifications'

 {CourseEnrollmentActions} = require '../flux/course-enrollment'


mintinline support

mintinline support

--- snippets/language-latex2e.cson
+++ snippets/language-latex2e.cson
@@ -70,6 +70,9 @@
   '\\footnotetext{}':
     'prefix': '\\footnotetext'
     'body': '\\\\footnotemtext{$1}$0'
+  '\\mintinline{}':
+    'prefix': '\\mintinline'
+    'body': '\\\\mintinline{$1}$0'

 # These snippets are available in mathmode
 '.text.tex.latex .string.other.math':


Apply disabled state on new buttons

Apply disabled state on new buttons

--- resources/assets/coffee/react/_components/bbcode-editor.coffee
+++ resources/assets/coffee/react/_components/bbcode-editor.coffee
@@ -110,11 +110,13 @@
     button
       type: 'button'
       className: 'js-bbcode-preview--hide btn-osu-big btn-osu-big--forum-secondary'
+      disabled: @props.disabled
       osu.trans('forum.topic.create.preview_hide')
   renderPreviewShowButton: ->
     button
       type: 'button'
       className: 'js-bbcode-preview--show btn-osu-big btn-osu-big--forum-secondary'
+      disabled: @props.disabled
       osu.trans('forum.topic.create.preview')


Prevent sending event if mode somehow ends up being undefined

--- resources/assets/coffee/react/_components/playmode-tabs.coffee
+++ resources/assets/coffee/react/_components/playmode-tabs.coffee
@@ -46,7 +46,7 @@
     e.preventDefault()
     mode = e.target.dataset.mode

-    return if @props.currentMode == mode
+    return if @props.currentMode == mode || mode == undefined
     return if e.target.dataset.disabled == 'true'

     $.publish 'playmode:set', mode: mode


remove contribution from collection when you delete it

--- src/node_modules/bucket-item/bucket-item-link.coffee
+++ src/node_modules/bucket-item/bucket-item-link.coffee
@@ -85,6 +85,7 @@

           scope.saveContribution(myContribution).then ->
             if myContribution.amount.eq(0)
+              bucket.contributions.remove(myContribution)
               scope.model.inputStatus = "ready"
               debug("ready")
             else


Test 4.

Test 4.

--- app/assets/javascripts/app/controllers/_application_controller.coffee
+++ app/assets/javascripts/app/controllers/_application_controller.coffee
@@ -640,22 +640,20 @@

 class App.ControllerPermanent extends App.Controller
   constructor: ->
-    super
-
-    # check authentication
     if @requiredPermission
       @permissionCheckRedirect(@requiredPermission, true)

+    super
+
     $('.content').addClass('hide')
     @navShow()

 class App.ControllerContent extends App.Controller
   constructor: ->
-    super
-
-    # check authentication
     if @requiredPermission
       @permissionCheckRedirect(@requiredPermission)
+
+    super

     $('.content').addClass('hide')
     $('#content').removeClass('hide')


Follow up - f54df501 - Fixes #3040: Make event handling more stable to stabilize browser tests.

--- app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee
+++ app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee
@@ -515,7 +515,7 @@
       options:
         duration: duration
         easing: 'easeOutQuad'
-        complete: => $(window).on 'click.ticket-zoom-textarea', @closeTextarea
+        complete: => $(window).off 'click.ticket-zoom-textarea'.on 'click.ticket-zoom-textarea', @closeTextarea

     @textBubble.velocity
       properties:


constrain rectangle (mark) height to yLower-yUpper

--- app/assets/javascripts/components/mark/tools/text-row-tool/index.cjsx
+++ app/assets/javascripts/components/mark/tools/text-row-tool/index.cjsx
@@ -71,7 +71,7 @@
         onMouseDown={@props.onSelect unless @props.disabled}
       >
         <Draggable onDrag={@handleDrag}>
-          <rect x={0-@props.mark.x} y={0} width="100%" height={DEFAULT_HEIGHT} />
+          <rect x={0-@props.mark.x} y={0} width="100%" height={@props.mark.yLower-@props.mark.yUpper} />
         </Draggable>

         { if @props.selected


[skip] Handled challenges for an Oauth token (5m)

--- app/assets/javascripts/houston-scheduler/views/new_ticket_view.coffee
+++ app/assets/javascripts/houston-scheduler/views/new_ticket_view.coffee
@@ -102,6 +102,8 @@
       errors = Errors.fromResponse(response)
       if errors.missingCredentials or errors.invalidCredentials
         App.promptForCredentialsTo @project.ticketTrackerName
+      else if errors.oauthLocation
+        App.oauth(errors.oauthLocation)
       else
         errors.renderToAlert().prependTo($('#houston_scheduler_view')).alert()


WEB better candidacy selection

WEB better candidacy selection

--- app/assets/javascripts/web/views/candidacies/candidacy_cell.js.coffee
+++ app/assets/javascripts/web/views/candidacies/candidacy_cell.js.coffee
@@ -13,7 +13,7 @@
     # change state
     # can't add more than two candidacies
     if @model.collection.selected().length == 2 && not @model.get 'selected'
-      return false
+      @model.collection.unselect()
     @model.set selected: !@model.get('selected')

     if @model.collection.selected().length == 2
@@ -27,4 +27,4 @@
     else
       # css
       $(@el).removeClass 'selected'
-    @+    @


removed PrettyError.toMarkup()

removed PrettyError.toMarkup()

--- scripts/coffee/lib/PrettyError.coffee
+++ scripts/coffee/lib/PrettyError.coffee
@@ -2,12 +2,6 @@
 ParsedError = require './ParsedError'
 RenderKid = require 'RenderKid'
 {object} = require 'utila'
-
-q = RenderKid.quote
-
-wrap = (tag, around = '') ->
-
-	"\n<#{tag}>\n#{around}\n</#{tag}>\n"

 module.exports = class PrettyError

@@ -114,71 +108,6 @@

 		obj

-	toMarkup: (e, skipModules = no) ->
-
-		unless e instanceof ParsedError
-
-			e = new ParsedError e
-
-		unless typeof skipModules is 'boolean' or Array.isArray skipModules
-
-			throw Error "skipModules only accepts a boolean or an array of module names"
-
-		header = do ->
-
-			kind = wrap 'kind', q e.kind
-
-			colon = wrap 'colon', ':'
-
-			message = wrap 'message', q e.message
-
-			wrap 'header', kind + colon + ' ' + message
-
-		traceItems = ''
-
-		for item, i in e.trace
-
-			if skipModules isnt no and i > 0
-
-				continue if skipModules is yes and item.modName is '[current]'
-
-				continue if item.modName in skipModules
-
-			itemHeader = do ->
-
-				pointer = do ->
-
-					unless item.file?
-
-						return ''
-
-					file = wrap 'file', q item.file
-
-					colon = wrap 'colon', ':'
-
-					line = wrap 'line', q item.line
-
-					file + colon + line
-
-				what = wrap 'what', q item.what or ''
-
-				wrap 'header', pointer + what
-
-			itemFooter = do ->
-
-				addr = wrap 'addr', q item.shortenedAddr
-
-				wrap 'footer', addr
-
-			traceItems += wrap 'item', itemHeader + itemFooter
-
-		trace = wrap 'trace', traceItems
-
-		markup = wrap 'pretty-error', header + trace
-
-		markup
-
-
 for prop in ['renderer'] then do ->

 	methodName = '_get' + prop[0].toUpperCase() + prop.substr(1, prop.length)


Categories and organization classifiers.

--- picoCTF-web/web/coffee/management-problems.coffee
+++ picoCTF-web/web/coffee/management-problems.coffee
@@ -68,23 +68,24 @@

 ProblemClassifierList = React.createClass
   render: ->
-    fakeAll = [
-      {
-        name: "Show all problems",
-        size: 20,
-        classifier: (problem) -> true
-      },
-      {
-        name: "Show none of the problems",
-        size: 0,
-        classifier: (problem) -> false
-      }
-    ]
+    categories = _.groupBy @props.problems, "category"
+    categoryData = _.map categories, (problems, category) ->
+      name: "Only #{category} problems"
+      size: problems.length
+      classifier: (problem) ->
+        problem.category == category
+
+    organizations = _.groupBy @props.problems, "organization"
+    organizationData = _.map organizations, (problems, organization) ->
+      name: "Created by #{organization}"
+      size: problems.length
+      classifier: (problem) ->
+        problem.organization == organization

     <PanelGroup className="problem-classifier" collapsible>
-      <ProblemClassifier name="All" data={fakeAll} {...@props}/>
-      <ProblemClassifier name="Categories" data={[]} {...@props}/>
-      <ProblemClassifier name="Organizations" data={[]} {...@props}/>
+      <ProblemClassifier name="Categories" data={categoryData} {...@props}/>
+      <ProblemClassifier name="Organizations" data={organizationData} {...@props}/>
+      <ProblemClassifier name="Bundles" data={[]} {...@props}/>
     </PanelGroup>

 ClassifierItem = React.createClass
@@ -94,14 +95,14 @@
   handleClick: (e) ->
     activeState = !@state.active
     @setState {active: activeState}
-    console.log @props
+    console.log @props, activeState
     @props.setClassifier activeState, @props.classifier

   render: ->
-    <ListGroupItem
-      onClick={@handleClick}
-      active={if @state.active then "active" else ""}>
-        {@props.name}
<Badge>{@props.size}</Badge>

+    glyph = <Glyphicon glyph="ok"/>
+
+    <ListGroupItem onClick={@handleClick}>
+        {@props.name} {if @state.active then glyph}
<Badge>{@props.size}</Badge>

     </ListGroupItem>

 ProblemClassifier = React.createClass
@@ -109,7 +110,7 @@
     <Panel header={@props.name} defaultExpanded collapsible>
       <ListGroup fill>
         {@props.data.map ((data, i) ->
-          <ClassifierItem key={i} setClassifier={@props.setClassifier} {...data}/>
+          <ClassifierItem key={i+data} setClassifier={@props.setClassifier} {...data}/>
         ).bind this}
       </ListGroup>
     </Panel>
@@ -194,7 +195,10 @@
     @setState update @state,
       activeSort: $set: {name: name, ascending: ascending}

-  setClassifier: (state, classifier) ->
+  setClassifier: (classifierState, classifier) ->
+    if not classifierState
+      classifier = (problem) -> true
+
     @setState update @state,
       problemClassifier: $set: classifier

@@ -210,16 +214,17 @@
       sortedProblems.reverse()

   render: ->
+    filteredProblems = @filterProblems @props.problems
     <Row className="pad">
       <Col xs={3} md={3}>
         <Row>
           <ProblemFilter onSortChange={@onSortChange} filter="" onFilterChange={@onFilterChange}/>
         </Row>
         <Row>
-          <ProblemClassifierList setClassifier={@setClassifier}/>
+          <ProblemClassifierList setClassifier={@setClassifier} problems={filteredProblems}/>
         </Row>
       </Col>
       <Col xs={9} md={9}>
-        <ProblemList problems={@filterProblems(@props.problems)}/>
+        <ProblemList problems={filteredProblems}/>
       </Col>
     </Row>


Updated web service url hakanensari/oblique.io#1

--- src/scripts/oblique.coffee
+++ src/scripts/oblique.coffee
@@ -23,7 +23,7 @@
 module.exports = (robot) ->
   robot.respond /.*strategy/i, (msg) ->
     mentions = msg.message.text.match(/(@\w+)/g)
-    robot.http('http://oblique.io')
+    robot.http('http://obliqueio.herokuapp.com')
       .get() (err, res, body) ->
         strategy = JSON.parse body
         strategy = "#{mentions.join(', ')}: #{strategy}" if mentions


Joining competitions

Joining competitions

--- http.coffee
+++ http.coffee
@@ -15,10 +15,10 @@
     ['/competitions/all', 'get', listCompetitions]
     ['/competitions/by-owner', 'get', listCompetitionsByOwner]
     ['/competitions/by-membership', 'get', listCompetitionsByMembership]
-    ['/competition', 'post', startCompetition]
     ['/competition/end', 'post', endCompetition]
     ['/competition/join', 'post', joinCompetition]
-    # [/competition\/\w+/withdraw/, 'post', withdrawFromCompetition]
+    ['/competition/withdraw', 'post', withdrawFromCompetition]
+    ['/competition', 'post', startCompetition]
   ]

 # Endpoint handler methods
@@ -110,6 +110,18 @@
   .done()

 joinCompetition = (req, res) ->
+  @api.joinCompetition(req.body.userId, req.body.competitionId)
+  .then((competitionMembership) ->
+    res.json(201, competitionMembership)
+    console.log("[HTTP] 201: joined competition: #{competitionMembership}")
+  )
+  .fail((err) ->
+    errorMsg = "failed to join competition. reason: #{err}"
+    res.json(500, {error: errorMsg})
+    console.error("[HTTP] 500: #{errorMsg}")
+    console.error(err.stack)
+  )
+  .done()

 withdrawFromCompetition = (req, res) ->


redis rtm adapter now supports index method

--- lib/realtime_models/adapters/redis.coffee
+++ lib/realtime_models/adapters/redis.coffee
@@ -2,9 +2,6 @@
 # -----------------
 # A dead simple Redis adapter that obeys the same interface as Mongoose where possible
 # TODO: Add casting as per schema and validations
-
-# Set Redis key prefix
-key = "ss:rtm"

 # Instance methods
 class Model
@@ -27,7 +24,7 @@

     true

-  save: (cb = ->) ->
+  save: (cb = ->) ->
     throw ['rtm_redis_no_id', 'You must specify an "id" before saving the model to Redis'] unless @doc.id?
     R.hmset key(@doc.id), @doc, (err, data) =>
       if err
@@ -62,8 +59,18 @@
       obj = new Model(data)
       obj.isNew = false
       cb(null, obj)
-

+# Finds all the keys associated with this model, in no particular order
+Model.find = (where, fields, cb) ->
+  output = []
+  R.keys "#{key()}*", (err, keys) ->
+    total_num = keys.length
+    keys.forEach (key, i) ->
+      id = key.split(':').pop()
+      Model.findById id, {}, (err, data) ->
+        output.push(data)
+        return cb(null, output) if (i+1) == total_num # Only send reply once all callbacks have completed
+
 # Export model
 exports.init = (name, definition, rtm) ->
   Model.rtm_name = name
@@ -72,5 +79,7 @@
   Model

 # Private methods
-key = (id) ->
-  "#{key}:#{Model.rtm_name}:#{id}"+key = (id = null) ->
+  k = ["ss:rtm", Model.rtm_name]
+  k.push(id) if id?
+  k.join(':')


When creating the Redis client with custom options, the first argument should be the port number instead of the hostname

--- src/server/db/redis.coffee
+++ src/server/db/redis.coffee
@@ -30,7 +30,7 @@
 	keyForOps = (docName) -> "#{options.prefix}ops:#{docName}"
 	keyForDoc = (docName) -> "#{options.prefix}doc:#{docName}"

-	client = redis.createClient options.hostname, options.port, options.redisOptions
+	client = redis.createClient options.port, options.hostname, options.redisOptions

 	client.select 15 if options.testing


Fix issue with the panel disappearing because it has a height of 0

--- lib/unified/php-debug-unified-view.coffee
+++ lib/unified/php-debug-unified-view.coffee
@@ -83,7 +83,7 @@
             target.style.width = target.style.height = null
           else
             return # No-Op
-        if event.rect.width < 262
+        else if event.rect.width < 262
           if event.rect.width < 1
             target.style.width = target.style.height = null
           else
@@ -104,7 +104,10 @@
     @resizer = @resizer.on('resizeend', (event) =>
         if (@panelMode == "bottom")
           event.target.style.width = 'auto'
-          atom.config.set('php-debug.currentPanelHeight',event.target.style.height);
+          if event.target.style.height == '0px'
+            atom.config.set('php-debug.currentPanelHeight','25px');
+          else
+            atom.config.set('php-debug.currentPanelHeight',event.target.style.height);
         else
           event.target.style.height = 'auto'
           atom.config.set('php-debug.currentPanelWidth',event.target.style.width);


Better fix for changing to project page on sign in.

--- salvus/page/account.coffee
+++ salvus/page/account.coffee
@@ -199,6 +199,7 @@
                     # should never ever happen
                     alert_message(type:"error", message: "The server responded with invalid message when signing in: #{JSON.stringify(mesg)}")

+first_login = true
 signed_in = (mesg) ->
     # record account_id in a variable global to this file, and pre-load and configure the "account settings" page
     account_id = mesg.account_id
@@ -215,6 +216,17 @@
             top_navbar.show_page_button("worksheet1")
             # Load the default worksheet (for now)
             require('worksheet1').load_scratch_worksheet()
+
+            # If this is the initial login, switch to the project
+            # page.  We do this because if the user's connection is
+            # flakie, they might get dropped and re-logged-in multiple
+            # times, and we definitely don't want to switch to the
+            # projects page in that case.  Also, if they explicitly
+            # log out, then log back in as another user, seeing
+            # the account page by default in that case makes sense.
+            if first_login and top_navbar.current_page_id == 'account'
+                first_login = false
+                top_navbar.switch_to_page("projects")

 # Listen for pushed sign_in events from the server.  This is one way that
 # the sign_in function above can be activated, but not the only way.


go-webserver: add hack for listing machines bug

--- client/Main/providers/machine.coffee
+++ client/Main/providers/machine.coffee
@@ -22,7 +22,12 @@

     { machine, stack } = options
     unless machine?.bongo_?.constructorName is 'JMachine'
-      error 'Data should be a JMachine instance'
+
+      # TODO: Idk what's happening here, but machine here is a view object
+      # not JMachine object. This happens only when fetching vms if payload
+      # doens't contain the machines :SA
+      machine = machine.getData()
+
       # throw new Error 'Data should be a JMachine instance'

     delete options.machine


Reimplement Quad._index_data() to support categorical axes

--- bokehjs/src/coffee/models/glyphs/quad.coffee
+++ bokehjs/src/coffee/models/glyphs/quad.coffee
@@ -2,16 +2,42 @@
 rbush = require "rbush"

 Glyph = require "./glyph"
+CategoricalMapper = require "../mappers/categorical_mapper"
 hittest = require "../../common/hittest"

 class QuadView extends Glyph.View

   _index_data: () ->
+    map_to_synthetic = (mapper, array) ->
+      if mapper instanceof CategoricalMapper.Model
+        mapper.v_map_to_target(array, true)
+      else
+        array
+
+    left = map_to_synthetic(@renderer.xmapper, @_left)
+    right = map_to_synthetic(@renderer.xmapper, @_right)
+
+    top = map_to_synthetic(@renderer.ymapper, @_top)
+    bottom = map_to_synthetic(@renderer.ymapper, @_bottom)
+
     index = rbush()
     pts = []
-    for i in [0...@_left.length]
-      if not isNaN(@_left[i] + @_right[i] + @_top[i] + @_bottom[i])
-        pts.push([@_left[i], @_bottom[i], @_right[i], @_top[i], {'i': i}])
+
+    for i in [0...left.length]
+      l = left[i]
+      if isNaN(l) or not isFinite(l)
+        continue
+      r = right[i]
+      if isNaN(r) or not isFinite(r)
+        continue
+      t = top[i]
+      if isNaN(t) or not isFinite(t)
+        continue
+      b = bottom[i]
+      if isNaN(b) or not isFinite(b)
+        continue
+      pts.push([l, b, r, t, {'i': i}])
+
     index.load(pts)
     return index


check to ensure renderers are in the bounds for this plot's update

--- bokehjs/src/coffee/range/data_range1d.coffee
+++ bokehjs/src/coffee/range/data_range1d.coffee
@@ -61,7 +61,8 @@

       result = new bbox.empty()
       for r in @get('renderers')
-        result = bbox.extend(result, bounds[r.id])
+        if bounds[r.id]?
+          result = bbox.extend(result, bounds[r.id])

       [min, max] = result[dimension]


Persist event through input cycle for convenience

--- src/trix/controllers/level_2_input_controller.coffee
+++ src/trix/controllers/level_2_input_controller.coffee
@@ -4,8 +4,8 @@

 class Trix.Level2InputController extends Trix.InputController
   mutationIsExpected: (mutationSummary) ->
-    expected = @handledInput
-    @handledInput = false
+    expected = @event?
+    @event = null
     console.log("unexpected mutation! #{JSON.stringify(mutationSummary)}") unless expected
     expected

@@ -14,14 +14,14 @@

   events:
     beforeinput: (event) ->
-      @handledInput = false
+      @event = null
       if handler = @inputTypes[event.inputType]
-        @handledInput = true
-        updateSelectionForEvent(event)
-        handler.call(this, event)
+        @event = event
+        @updateSelection()
+        handler.call(this)

     input: (event) ->
-      updateSelectionForEvent(event)
+      Trix.selectionChangeObserver.reset()

     compositionend: (event) ->
       if @composing
@@ -29,134 +29,134 @@
         @requestRender()

   inputTypes:
-    deleteByComposition: (event) ->
-      @responder?.deleteInDirection("backward")
-
-    deleteByCut: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.deleteInDirection("backward")
-
-    # deleteByDrag: (event) ->
-
-    deleteCompositionText: (event) ->
-      @responder?.deleteInDirection("backward")
-
-    deleteContent: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.deleteInDirection("backward")
-
-    deleteContentBackward: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.deleteInDirection("backward")
-
-    deleteContentForward: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.deleteInDirection("forward")
-
-    deleteEntireSoftLine: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.deleteInDirection("forward")
-
-    deleteHardLineBackward: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.deleteInDirection("backward")
-
-    deleteHardLineForward: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.deleteInDirection("forward")
-
-    deleteSoftLineBackward: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.deleteInDirection("backward")
-
-    deleteSoftLineForward: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.deleteInDirection("forward")
-
-    deleteWordBackward: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.deleteInDirection("backward")
-
-    deleteWordForward: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.deleteInDirection("forward")
-
-    formatBackColor: (event) ->
-      @activateAttributeIfSupported("backgroundColor", event.data)
-
-    formatBold: (event) ->
+    deleteByComposition: ->
+      @responder?.deleteInDirection("backward")
+
+    deleteByCut: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.deleteInDirection("backward")
+
+    # deleteByDrag: ->
+
+    deleteCompositionText: ->
+      @responder?.deleteInDirection("backward")
+
+    deleteContent: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.deleteInDirection("backward")
+
+    deleteContentBackward: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.deleteInDirection("backward")
+
+    deleteContentForward: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.deleteInDirection("forward")
+
+    deleteEntireSoftLine: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.deleteInDirection("forward")
+
+    deleteHardLineBackward: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.deleteInDirection("backward")
+
+    deleteHardLineForward: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.deleteInDirection("forward")
+
+    deleteSoftLineBackward: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.deleteInDirection("backward")
+
+    deleteSoftLineForward: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.deleteInDirection("forward")
+
+    deleteWordBackward: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.deleteInDirection("backward")
+
+    deleteWordForward: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.deleteInDirection("forward")
+
+    formatBackColor: ->
+      @activateAttributeIfSupported("backgroundColor", @event.data)
+
+    formatBold: ->
       @toggleAttributeIfSupported("bold")

-    formatFontColor: (event) ->
-      @activateAttributeIfSupported("color", event.data)
-
-    formatFontName: (event) ->
-      @activateAttributeIfSupported("font", event.data)
-
-    formatIndent: (event) ->
+    formatFontColor: ->
+      @activateAttributeIfSupported("color", @event.data)
+
+    formatFontName: ->
+      @activateAttributeIfSupported("font", @event.data)
+
+    formatIndent: ->
       if @responder?.canIncreaseNestingLevel()
         @responder?.increaseNestingLevel()

-    formatItalic: (event) ->
+    formatItalic: ->
       @toggleAttributeIfSupported("italic")

-    formatJustifyCenter: (event) ->
+    formatJustifyCenter: ->
       @toggleAttributeIfSupported("justifyCenter")

-    formatJustifyFull: (event) ->
+    formatJustifyFull: ->
       @toggleAttributeIfSupported("justifyFull")

-    formatJustifyLeft: (event) ->
+    formatJustifyLeft: ->
       @toggleAttributeIfSupported("justifyLeft")

-    formatJustifyRight: (event) ->
+    formatJustifyRight: ->
       @toggleAttributeIfSupported("justifyRight")

-    formatOutdent: (event) ->
+    formatOutdent: ->
       if @responder?.canDecreaseNestingLevel()
         @responder?.decreaseNestingLevel()

-    formatRemove: (event) ->
+    formatRemove: ->
       for attributeName of @responder?.getCurrentAttributes()
         @responder?.removeCurrentAttribute(attributeName)

-    formatSetBlockTextDirection: (event) ->
-      @activateAttributeIfSupported("blockDir", event.data)
-
-    formatSetInlineTextDirection: (event) ->
-      @activateAttributeIfSupported("textDir", event.data)
-
-    formatStrikeThrough: (event) ->
+    formatSetBlockTextDirection: ->
+      @activateAttributeIfSupported("blockDir", @event.data)
+
+    formatSetInlineTextDirection: ->
+      @activateAttributeIfSupported("textDir", @event.data)
+
+    formatStrikeThrough: ->
       @toggleAttributeIfSupported("strike")

-    formatSubscript: (event) ->
+    formatSubscript: ->
       @toggleAttributeIfSupported("sub")

-    formatSuperscript: (event) ->
+    formatSuperscript: ->
       @toggleAttributeIfSupported("sup")

-    formatUnderline: (event) ->
+    formatUnderline: ->
       @toggleAttributeIfSupported("underline")

-    historyRedo: (event) ->
+    historyRedo: ->
       @delegate?.inputControllerWillPerformRedo()

-    historyUndo: (event) ->
+    historyUndo: ->
       @delegate?.inputControllerWillPerformUndo()

-    insertCompositionText: (event) ->
+    insertCompositionText: ->
       @composing = true
       @delegate?.inputControllerWillPerformTyping()
-      @responder?.insertString(event.data)
-
-    insertFromComposition: (event) ->
+      @responder?.insertString(@event.data)
+
+    insertFromComposition: ->
       @composing = false
       @delegate?.inputControllerWillPerformTyping()
-      @responder?.insertString(event.data)
-
-    # insertFromDrop: (event) ->
-
-    insertFromPaste: (event) ->
+      @responder?.insertString(@event.data)
+
+    # insertFromDrop: ->
+
+    insertFromPaste: ->
       {dataTransfer} = event
       paste = {dataTransfer}

@@ -181,38 +181,38 @@
         @responder?.insertFile(paste.file)
         @delegate?.inputControllerDidPaste(paste)

-    insertFromYank: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.insertString(event.data)
-
-    # insertHorizontalRule: (event) ->
-
-    insertLineBreak: (event) ->
+    insertFromYank: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.insertString(@event.data)
+
+    # insertHorizontalRule: ->
+
+    insertLineBreak: ->
       @delegate?.inputControllerWillPerformTyping()
       @responder?.insertString("\n")

-    # insertLink: (event) ->
-
-    insertOrderedList: (event) ->
+    # insertLink: ->
+
+    insertOrderedList: ->
       @toggleAttributeIfSupported("number")

-    insertParagraph: (event) ->
+    insertParagraph: ->
       @delegate?.inputControllerWillPerformTyping()
       @responder?.insertLineBreak()

-    insertReplacementText: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.insertString(event.dataTransfer.getData("text/plain"))
-
-    insertText: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.insertString(event.data)
-
-    insertTranspose: (event) ->
-      @delegate?.inputControllerWillPerformTyping()
-      @responder?.insertString(event.data)
-
-    insertUnorderedList: (event) ->
+    insertReplacementText: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.insertString(@event.dataTransfer.getData("text/plain"))
+
+    insertText: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.insertString(@event.data)
+
+    insertTranspose: ->
+      @delegate?.inputControllerWillPerformTyping()
+      @responder?.insertString(@event.data)
+
+    insertUnorderedList: ->
       @toggleAttributeIfSupported("bullet")

   toggleAttributeIfSupported: (attributeName) ->
@@ -225,16 +225,16 @@
       @delegate?.inputControllerWillPerformFormatting()
       @responder?.setCurrentAttribute(attributeName, value)

-updateSelectionForEvent = (event) ->
-  if domRange = getDOMRangeForEvent(event)
-    Trix.setDOMRange(domRange)
-  else
-    Trix.selectionChangeObserver.reset()
-
-getDOMRangeForEvent = (event) ->
-  if targetRanges = event.getTargetRanges?()
-    if targetRanges.length
-      staticRangeToRange(targetRanges[0])
+  updateSelection: ->
+    if domRange = @getTargetDOMRange()
+      Trix.setDOMRange(domRange)
+    else
+      Trix.selectionChangeObserver.reset()
+
+  getTargetDOMRange: ->
+    if targetRanges = @event.getTargetRanges?()
+      if targetRanges.length
+        staticRangeToRange(targetRanges[0])

 staticRangeToRange = (staticRange) ->
   range = document.createRange()


Deprecate editor:detached

Deprecate editor:detached

--- src/workspace-view.coffee
+++ src/workspace-view.coffee
@@ -422,6 +422,8 @@
           deprecate('Use Editor::onDidChangeCursorPosition instead')
         when 'editor:attached'
           deprecate('Use Editor::onDidAddTextEditor instead')
+        when 'editor:detached'
+          deprecate('Use Editor::onDidDestroy instead')
         when 'editor:will-be-removed'
           deprecate('Use Editor::onDidDestroy instead')
         when 'pane:active-item-changed'
@@ -460,6 +462,8 @@
           deprecate('Use Editor::onDidChangeCursorPosition instead')
         when 'editor:attached'
           deprecate('Use Editor::onDidAddTextEditor instead')
+        when 'editor:detached'
+          deprecate('Use Editor::onDidDestroy instead')
         when 'editor:will-be-removed'
           deprecate('Use Editor::onDidDestroy instead')
         when 'selection:changed'
@@ -473,6 +477,8 @@
           deprecate('Use Editor::onDidChangeCursorPosition instead')
         when 'editor:attached'
           deprecate('Use Editor::onDidAddTextEditor instead')
+        when 'editor:detached'
+          deprecate('Use Editor::onDidDestroy instead')
         when 'editor:will-be-removed'
           deprecate('Use Editor::onDidDestroy instead')
         when 'pane:active-item-changed'


Update getEditors references to getEditorViews

--- src/workspace-view.coffee
+++ src/workspace-view.coffee
@@ -264,7 +264,7 @@
   #
   # Returns an {Array} of {String}s.
   getOpenBufferPaths: ->
-    _.uniq(_.flatten(@getEditors().map (editorView) -> editorView.getOpenBufferPaths()))
+    _.uniq(_.flatten(@getEditorViews().map (editorView) -> editorView.getOpenBufferPaths()))

   # Public: Returns the currently focused {Pane}.
   getActivePane: ->
@@ -307,13 +307,13 @@

   # Public: Fires a callback on each open {EditorView}.
   eachEditorView: (callback) ->
-    callback(editor) for editor in @getEditors()
+    callback(editor) for editor in @getEditorViews()
     attachedCallback = (e, editor) -> callback(editor)
     @on('editor:attached', attachedCallback)
     off: => @off('editor:attached', attachedCallback)

   # Private: Destroys everything.
   remove: ->
-    editorView.remove() for editorView in @getEditors()
+    editorView.remove() for editorView in @getEditorViews()
     atom.project?.destroy()
     super


Reorganised functions

Reorganised functions

--- scripts/steam.coffee
+++ scripts/steam.coffee
@@ -182,22 +182,22 @@

         communityID

-    getHero = (heroID) ->
-        return hero for hero in heroes when hero.id is heroID
-
-    getTowers = (dec) ->
-        for status, tower in "00000000000#{(+dec).toString(2)}".slice(-11).split('')
-            if parseInt(status) then towers[tower] else continue
-
-    steam_request = (msg, endpoint, params = {}, handler, version = 1) ->
-        params.key = STEAM_API_KEY
-
-        msg.http("http://api.steampowered.com#{endpoint}/v#{version}/")
-            .query(params)
-            .get() (err, res, body) ->
-                if err or res.statusCode isnt 200
-                    err = "Bad request (invalid Steam web API key)" if res.statusCode is 400
-                    msg.reply "An error occurred while attempting to process your request."
-                    return robot.logger.error err
-
-                handler JSON.parse(body)+getHero = (heroID) ->
+    return hero for hero in heroes when hero.id is heroID
+
+getTowers = (dec) ->
+    for status, tower in "00000000000#{(+dec).toString(2)}".slice(-11).split('')
+        if parseInt(status) then towers[tower] else continue
+
+steam_request = (msg, endpoint, params = {}, handler, version = 1) ->
+    params.key = STEAM_API_KEY
+
+    msg.http("http://api.steampowered.com#{endpoint}/v#{version}/")
+        .query(params)
+        .get() (err, res, body) ->
+            if err or res.statusCode isnt 200
+                err = "Bad request (invalid Steam web API key)" if res.statusCode is 400
+                msg.reply "An error occurred while attempting to process your request."
+                return robot.logger.error err
+
+            handler JSON.parse(body)


:racehorse: Use a Set in CompositeDisposable

--- src/composite-disposable.coffee
+++ src/composite-disposable.coffee
@@ -28,7 +28,7 @@

   # Public: Construct an instance, optionally with one or more disposables
   constructor: ->
-    @disposables = []
+    @disposables = new Set
     @add(disposable) for disposable in arguments

   # Public: Dispose all disposables added to this composite disposable.
@@ -37,8 +37,9 @@
   dispose: ->
     unless @disposed
       @disposed = true
-      while disposable = @disposables.shift()
+      @disposables.forEach (disposable) ->
         disposable.dispose()
+      @disposables = null
     return

   ###
@@ -52,7 +53,7 @@
   # * `disposable` {Disposable} instance or any object with a `.dispose()`
   #   method.
   add: (disposable) ->
-    @disposables.push(disposable) unless @disposed
+    @disposables.add(disposable) unless @disposed
     return

   # Public: Remove a previously added disposable.
@@ -60,12 +61,11 @@
   # * `disposable` {Disposable} instance or any object with a `.dispose()`
   #   method.
   remove: (disposable) ->
-    index = @disposables.indexOf(disposable)
-    @disposables.splice(index, 1) if index isnt -1
+    @disposables.delete(disposable) unless @disposed
     return

   # Public: Clear all disposables. They will not be disposed by the next call
   # to dispose.
   clear: ->
-    @disposables.length = 0
+    @disposables.clear() unless @disposed
     return


clean up coffeescript in init.coffee - remove semicolons - shorten variable assignment to < 80 chars

--- init.coffee
+++ init.coffee
@@ -2,7 +2,7 @@
 path = require 'path'
 exec = Promise.promisify(require('child_process').exec)
 fs = require 'fs'
-mkdirp = require('mkdirp');
+mkdirp = require 'mkdirp'

 exports.configure = [
   {
@@ -26,8 +26,8 @@
   _check_go_installed()

 exports.beforeRender = (utils, config) ->
-  package_name = path.join(config.project_base_dir, config.user_id, config.project_name)
-  config.package_path = package_name
+  pkg = path.join(config.project_base_dir, config.user_id, config.project_name)
+  config.package_path = pkg

   _get_go_path()
     .then (go_path) ->
@@ -36,7 +36,7 @@
       config.dest = path.join(config.go_path, 'src', config.package_path)
       if fs.existsSync(config.dest) is on
         strMessage = 'Already exists: ' + config.dest
-        Promise.reject(new Error(strMessage));
+        Promise.reject(new Error(strMessage))

 exports.after = (utils, config) ->
@@ -69,4 +69,4 @@
       errorMsg = 'Go path is either not set or empty'
       Promise.reject(new Error(errorMsg))
     else
-      Promise.resolve(go_path)+      Promise.resolve(go_path)


fix labeling mistake in readme

fix labeling mistake in readme

--- init.coffee
+++ init.coffee
@@ -27,7 +27,7 @@
   }

   # EXAMPLE:
-  # adding a new `food` local to be passed to our templates
+  # adding a new `emoji` local to be passed to our templates
   config.emoji = emojis[config.food]

 exports.after = (utils, config) ->


cleanup: removed emoji config

cleanup: removed emoji config

--- client/app/lib/maincontroller.coffee
+++ client/app/lib/maincontroller.coffee
@@ -1,7 +1,6 @@
 kd                             = require 'kd'
 KDController                   = kd.Controller
 KDNotificationView             = kd.NotificationView
-emojify                        = require 'emojify.js'
 Promise                        = require 'bluebird'
 kookies                        = require 'kookies'
 globals                        = require 'globals'
@@ -80,15 +79,6 @@
     @attachListeners()

     @detectIdleUser()
-    @startCachingAssets()  unless globals.isLoggedInOnLoad
-
-    emojify?.setConfig
-      img_dir      : 'https://s3.amazonaws.com/koding-cdn/emojis'
-      ignored_tags :
-        'TEXTAREA' : 1,
-        'A'        : 1,
-        'PRE'      : 1,
-        'CODE'     : 1

   createSingletons:->


Show trumpet text on login/public pages too

Also update body.margin-top on resize event

--- static/script/settlers.coffee
+++ static/script/settlers.coffee
@@ -168,6 +168,18 @@
         url:			'/logout/'
       return false

+  else
+    new window.hlib.Ajax
+      url:			'/trumpet'
+      keep_focus:		true
+      handlers:
+        h200:			(response, ajax) ->
+          if response.trumpet != false
+            $('#trumpet_board_dialog p').html response.trumpet
+            $('#trumpet_board_dialog').show()
+            $('body').css 'margin-top', ($('#trumpet_board_dialog').height() + 'px')
+            window.hlib.MESSAGE.hide()
+
   # needed for iOS mobile devices (well, afaik...)
   adapt_to_orientation = () ->
     if window.orientation == 0 or window.orientation == 180
@@ -184,6 +196,9 @@

   adapt_to_orientation()

+  window.addEventListener 'resize', () ->
+    $('body').css 'margin-top', ($('#trumpet_board_dialog').height() + 'px')
+
   window.addEventListener 'orientationchange', () ->
     adapt_to_orientation()


explicity use promise from request-promise

--- src/adaptor/googleSpreadsheet.coffee
+++ src/adaptor/googleSpreadsheet.coffee
@@ -37,6 +37,7 @@
     request
       uri: @json_url
       json: true
+    .promise()
     .then (result) =>
       @sphere_from result
     .catch (error) =>


sidebar: pass data to more link

sidebar: pass data to more link

--- client/Main/navigation/navigationitem.coffee
+++ client/Main/navigation/navigationitem.coffee
@@ -28,9 +28,11 @@

     @setClass 'sub-title'
     { activitySidebar } = KD.singletons.mainView
+    { title } = @getData()
     data.delegate = @getDelegate()

     @child = new SidebarMoreLink
+      title   : title
       tagName : 'a'
       click   : (event) ->
         KD.utils.stopDOMEvent event


payment: add back legacy code to be backwards compatible

--- client/Main/payment/paymentcontroller.coffee
+++ client/Main/payment/paymentcontroller.coffee
@@ -27,3 +27,312 @@

   api:-> KD.remote.api.Payment

+
+  ##########################################################
+
+  fetchPaymentMethods: (callback) ->
+
+    {dash}                 = Bongo
+    methods                = null
+    preferredPaymentMethod = null
+    {appStorageController} = KD.singletons
+    appStorage             = appStorageController.storage 'Account', '1.0'
+
+    queue      = [
+
+      -> appStorage.fetchStorage ->
+        preferredPaymentMethod = appStorage.getValue 'preferredPaymentMethod'
+        queue.fin()
+
+      => KD.whoami().fetchPaymentMethods (err, paymentMethods) ->
+        methods = paymentMethods
+        queue.fin err
+    ]
+
+    dash queue, (err) -> callback err, {
+      preferredPaymentMethod
+      methods
+      appStorage
+    }
+
+  observePaymentSave: (modal, callback) ->
+    modal.on 'PaymentInfoSubmitted', (paymentMethodId, updatedPaymentInfo) =>
+      updatedPaymentInfo.challenge = Recaptcha.get_challenge()
+      updatedPaymentInfo.response  = Recaptcha.get_response()
+
+      @updatePaymentInfo paymentMethodId, updatedPaymentInfo, (err, savedPaymentInfo) =>
+        if err
+          modal.paymentForm?.stopLoader()
+
+          modal.emit 'FormValidationFailed', err
+          return callback err
+
+        callback null, savedPaymentInfo
+        @emit 'PaymentDataChanged'
+
+  removePaymentMethod: (paymentMethodId, callback) ->
+    { JPayment } = KD.remote.api
+    JPayment.removePaymentMethod paymentMethodId, (err) =>
+      return callback err  if err
+      @emit 'PaymentDataChanged'
+
+  fetchSubscription: do ->
+    findActiveSubscription = (subscriptions, planCode, callback) ->
+      for own paymentMethodId, subs of subscriptions
+        for sub in subscriptions
+          if sub.planCode is planCode and sub.status in ['canceled', 'active']
+            return callback null, sub
+
+      callback null
+
+    fetchSubscription = (type, planCode, callback) ->
+      { JPaymentSubscription } = KD.remote.api
+
+      if type is 'group'
+        KD.getGroup().checkPayment (err, subs) =>
+          findActiveSubscription subs, planCode, callback
+      else
+        JPaymentSubscription.fetchUserSubscriptions (err, subs) ->
+          findActiveSubscription subs, planCode, callback
+
+  fetchPlanByCode: (planCode, callback) ->
+
+    { JPaymentPlan } = KD.remote.api
+
+    JPaymentPlan.fetchPlanByCode planCode, callback
+
+  fetchPaymentInfo: (type, callback) ->
+
+    { JPaymentPlan } = KD.remote.api
+
+    switch type
+      when 'group', 'expensed'
+        KD.getGroup().fetchPaymentInfo callback
+      when 'user'
+        JPaymentPlan.fetchAccountDetails callback
+
+  isCaptchaValid: (challenge, response, callback)->
+    $.ajax
+      type    : "POST"
+      url     : "/recaptcha"
+      data    : {
+        challenge : challenge,
+        response  : response,
+      }
+      error   : -> callback "fail"
+      success : (data)-> callback data
+      timeout : 5000
+
+  updatePaymentInfo: (paymentMethodId = null, paymentMethod, callback) ->
+    {JPayment} = KD.remote.api
+    paymentMethod[key] = value.trim()  for own key, value of paymentMethod
+    JPayment.setPaymentInfo paymentMethodId, paymentMethod, callback
+
+  createPaymentInfoModal: -> new PaymentFormModal
+
+  createUpgradeForm: (parent) ->
+    buyPacksButton = new KDButtonView
+      cssClass     : "buy-packs"
+      style        : "solid green medium"
+      title        : "Buy Resource Packs"
+      callback     : ->
+        parent   or= @parent
+        parent.emit "Cancel"
+        KD.singleton("router").handleRoute "/Pricing"
+
+    return new JView
+      pistachioParams:
+        button  : buyPacksButton
+      pistachio :
+        """
+        <h2>
+          You do not have enough resources, you need to buy at least one "Resource Pack" to be able to create an extra VM.
+        </h2>
+        {{> button}}
+        """
+
+  createUpgradeWorkflow: (options = {}) ->
+    {tag, productForm, confirmForm} = options
+
+    productForm or= @createUpgradeForm()
+    confirmForm or= new PlanUpgradeConfirmForm
+      name : 'overview'
+    workflow      = new PaymentWorkflow {productForm, confirmForm}
+
+    productForm
+      .on 'PlanSelected', (plan, planOptions) ->
+        callback = ->
+          workflow.collectData productData: { plan, planOptions }
+
+        {oldSubscription} = workflow.collector.data
+        unless oldSubscription
+        then callback()
+        else
+          usage = oldSubscription?.usage ? {}
+          plan.checkQuota {usage}, (err) ->
+            return  if KD.showError err
+            callback()
+
+      .on 'CurrentSubscriptionSet', (oldSubscription) ->
+        workflow.collectData { oldSubscription }
+
+    workflow
+      .on 'DataCollected', (data) =>
+        @transitionSubscription data, (err, subscription, rest...) ->
+          return workflow.emit 'Failed', err  if err
+          workflow.emit 'SubscriptionTransitionCompleted', subscription  unless err
+          workflow.emit 'Finished', data, err, subscription, rest...
+
+      .on 'Finished', (data, err, subscription, rest...) =>
+        { plan, email, createAccount, paymentMethod: {billing} } = data
+        if err?.short is 'existing_subscription'
+          { existingSubscription } = err
+          if existingSubscription.status is 'active'
+            new KDNotificationView title: "You are already subscribed to this plan!"
+            KD.getSingleton('router').handleRoute '/Account/Subscriptions'
+          else
+            existingSubscription.plan = plan
+            @confirmReactivation existingSubscription, (err, subscription) =>
+              return KD.showError err  if err
+              @emit "SubscriptionReactivated", subscription
+        else if createAccount
+          { cardFirstName: firstName, cardLastName: lastName } = billing
+          { JUser } = KD.remote.api
+          JUser.convert { firstName, lastName, email }, (err) =>
+            return KD.showError err  if err
+            JUser.logout ->
+        else
+          @emit "SubscriptionCompleted"
+
+        log 'KD.singletons.dock.getView().show()'
+
+      .enter()
+
+    workflow
+
+  confirmReactivation: (subscription, callback) ->
+    modal = KDModalView.confirm
+      title       : 'Inactive subscription'
+      description :
+        """
+        Your existing subscription for this plan has been canceled.  Would
+        you like to reactivate it?
+        """
+      subView     : new SubscriptionView {}, subscription
+      ok          :
+        title     : 'Reactivate'
+        callback  : -> subscription.resume (err) ->
+          return callback err  if err
+
+          modal.destroy()
+
+          callback null, subscription
+
+  createSubscription: (options, callback) ->
+    { plan, planOptions, promotionType, paymentMethod } = options
+    { paymentMethodId } = paymentMethod
+    planOptions ?= {}
+    { planApi } = planOptions
+
+    options = {
+      planOptions
+      promotionType
+      paymentMethodId
+      planCode: plan.planCode
+    }
+
+    if planApi
+      planApi.subscribe options, callback
+    else
+      plan.subscribe paymentMethodId, planOptions, callback
+
+  transitionSubscription: (formData, callback) ->
+    { productData, oldSubscription, promotionType, paymentMethod, createAccount, email } = formData
+    { plan, planOptions } = productData
+    { planCode } = plan
+    { paymentMethodId } = paymentMethod
+    if oldSubscription
+      oldSubscription.transitionTo { planCode, paymentMethodId }, callback
+    else
+      @createSubscription {
+        plan
+        planOptions
+        promotionType
+        email
+        paymentMethod
+        createAccount
+      }, callback
+
+  debitSubscription: (subscription, pack, callback) ->
+    subscription.debit { pack }, (err, nonce) =>
+      return callback err  if err
+      @emit 'SubscriptionDebited', subscription
+      callback null, nonce
+
+  creditSubscription: (subscription, pack, callback) ->
+    subscription.credit { pack }, (err) =>
+      return callback err  if err
+      @emit 'SubscriptionCredited', subscription
+      callback()
+
+  fetchActiveSubscription: (tags, callback) ->
+    if KD.getGroup()?.slug is "koding"
+      return callback()  if KD.whoami().type isnt "registered"
+      status = $in: ["active", "canceled"]
+      @fetchSubscriptionsWithPlans {tags, status}, (err, subscriptions) ->
+        return callback err  if err
+        noSync = null
+        active = null
+
+        for subscription in subscriptions
+          if "nosync" in subscription.tags
+            noSync = subscription
+          else
+            active = subscription
+
+        subscription = active or noSync
+
+        if subscription
+        then callback null, subscription
+        else callback message: "Subscription not found", code: "no subscription"
+    else
+      @fetchGroupSubscription callback
+
+  fetchGroupSubscription: (callback) ->
+    KD.getGroup().fetchSubscription callback
+
+  fetchSubscriptionsWithPlans: (options, callback) ->
+    [callback, options] = [options, callback]  unless callback
+
+    options ?= {}
+
+    KD.whoami().fetchPlansAndSubscriptions options, (err, plansAndSubs) =>
+      return callback err  if err
+
+      { subscriptions } = @groupPlansBySubscription plansAndSubs
+
+      callback null, subscriptions
+
+  groupPlansBySubscription: (plansAndSubscriptions = {}) ->
+
+    { plans, subscriptions } = plansAndSubscriptions
+
+    plansByCode = plans.reduce( (memo, plan) ->
+      memo[plan.planCode] = plan
+      memo
+    , {})
+
+    for subscription in subscriptions
+      subscription.plan = plansByCode[subscription.planCode]
+
+    { plans, subscriptions }
+
+  canDebitPack: (options = {}, callback = noop) ->
+    {subscriptionTag, packTag, multiplyFactor} = options
+    multiplyFactor ?= 1
+
+    return warn "missing parameters"  unless subscriptionTag or packTag
+
+    @fetchActiveSubscription tags: subscriptionTag, (err, subscription) ->
+      KD.remote.api.JPaymentPack.one tags: packTag, (err, pack) ->
+        subscription.checkUsage pack, multiplyFactor, callback


ComputeProvider: MachineListItem (wip) machine list item for MachineList

--- client/Main/providers/computeprovider.coffee
+++ client/Main/providers/computeprovider.coffee
@@ -73,6 +73,36 @@
   getName:->
     @publicAddress or @uid or @label or "one of #{KD.nick()}'s machine"

+
+class MachineItem extends KDListItemView
+
+  JView.mixin @prototype
+
+  constructor:(options = {}, data)->
+    options.type = 'machine'
+    options.buttonTitle or= 'select'
+    super options, data
+
+    machineReady = data.jMachine.state isnt 'NotInitialized'
+
+    @actionButton = new KDButtonView
+      title    : @getOption 'buttonTitle'
+      cssClass : 'solid green mini action-button'
+      callback : =>
+        @getDelegate().emit "MachineSelected", @getData()
+      disabled : machineReady
+
+    @setClass 'disabled'  if machineReady
+
+  pistachio:->
+
+    {label, description, html_url} = @getData()
+
+    """
+    {h1{#(jMachine.uid)}}
+    {{> @actionButton}}
+    """
+
 class MachineList extends KDModalView

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


Troubleshoot: internet health checker url is changed

--- client/Main/troubleshoot/troubleshoot.coffee
+++ client/Main/troubleshoot/troubleshoot.coffee
@@ -75,6 +75,7 @@
       liveUpdateChecker = new LiveUpdateChecker
       @registerItem "liveUpdate",
         troubleshoot: liveUpdateChecker.healthCheck.bind liveUpdateChecker
+        recover     : liveUpdateChecker.healthCheck.bind liveUpdateChecker

     @registerItem "version",
       speedCheck   : no
@@ -88,7 +89,7 @@

   registerConnections: ->
     #register connection
-    externalUrl = "https://s3.amazonaws.com/koding-ping/ping.json"
+    externalUrl = "https://s3.amazonaws.com/koding-ping/healthcheck.json"
     item = new ConnectionChecker crossDomain: yes, externalUrl
     @registerItem "connection", troubleshoot: item.ping.bind item


activity: don't invoke `ActivityLikeLink::update` method after remote procedure is returned

--- client/Social/Activity/views/likelink.coffee
+++ client/Social/Activity/views/likelink.coffee
@@ -18,11 +18,7 @@
     {like, unlike} = KD.singletons.socialapi.message

     fn = if isInteracted then unlike else like
-    fn {id}, (err) =>
-
-      return @showError err  if err
-
-      @update()
+    fn {id}, (err) => @showError err  if err

   update: ->


Activity: minor code styling

Activity: minor code styling

--- client/Social/Activity/views/likeview.coffee
+++ client/Social/Activity/views/likeview.coffee
@@ -18,10 +18,4 @@
     , data

-  viewAppended: ->
-    super
-
-
-  pistachio: ->
-
-    "{{> @link}}{{> @count}}"
+  pistachio: -> "{{> @link}}{{> @count}}"


Teamwork: Implemented watch mode in teamwork tab view.

--- client/Teamwork/Views/teamworktabview.coffee
+++ client/Teamwork/Views/teamworktabview.coffee
@@ -32,8 +32,9 @@
         @requestRef.remove()

   listenPaneDidShow: ->
-    @tabView.on "PaneDidShow", (pane) =>
-      @indexRef.set pane.getOptions().indexKey
+    # @tabView.on "PaneDidShow", (pane) =>
+    #   @indexRef.set
+    #     indexKey: pane.getOptions().indexKey

   listenChildRemovedOnKeysRef: ->
     @keysRef.on "child_removed", (snapshot) =>
@@ -63,18 +64,21 @@

   listenIndexRef: ->
     @indexRef.on "value", (snapshot) =>
-      key = snapshot.val()
-      return unless key
-
-      for pane in @tabView.panes
-        if pane.getOptions().indexKey is key
-          @tabView.showPaneByIndex @tabView.getPaneIndex pane
+      data       = snapshot.val()
+      {watchMap} = @workspace
+      username   = KD.nick()
+      return unless data
+
+      if watchMap[username] is "everybody" or watchMap[username] is data.by
+        for pane in @tabView.panes
+          if pane.getOptions().indexKey is data.indexKey
+            @tabView.showPaneByIndex @tabView.getPaneIndex pane

   createElements: ->
     @tabHandleHolder = new ApplicationTabHandleHolder delegate: this
     @tabView         = new ApplicationTabView
       delegate                  : this
-      lastTabHandleMargin       : 200
+      lastTabHandleMargin       : 80
       tabHandleContainer        : @tabHandleHolder
       closeAppWhenAllTabsClosed : no

@@ -83,9 +87,14 @@
         paneOptions = pane.getOptions()
         @workspace.addToHistory
           message    : "$0 switched to new pane, #{paneOptions.title}"
+          by         : KD.nick()
           data       :
             title    : paneOptions.title
             indexKey : paneOptions.indexKey
+
+        @indexRef.set
+          indexKey: pane.getOptions().indexKey
+          by      : KD.nick()

   addNewTab: ->
     @createPlusHandleDropDown()
@@ -129,7 +138,9 @@
         type : paneType
         by   : KD.nick()

-    @track "$0 opened a new #{paneType}"
+    @workspace.addToHistory
+      message: "$0 opened a new #{paneType}"
+      by     : KD.nick()

   createTabFromFirebaseData: (data) ->
     {sessionKey, indexKey} = data
@@ -263,9 +274,6 @@
     pane.addSubView childView
     @tabView.addPane pane

-  track: (data) ->
-    @workspace.addToHistory data
-
   viewAppended: ->
     super
     @createDashboard()  if @amIHost


activity: remove the ugly hack for css

activity: remove the ugly hack for css

--- client/activity/lib/views/messagepane.coffee
+++ client/activity/lib/views/messagepane.coffee
@@ -43,19 +43,8 @@

     {typeConstant} = @getData()

-    # this hack is mainly for css.
-    # collaboration chat pane depends on the css
-    # from activity private message, especially
-    # the specifity of the list items.
-    # This way, the activity list controller of collaboration
-    # chat window will have `kdlistview-privatemessage`
-    # cssClass. ~Umut
-    listType = if typeConstant is 'collaboration'
-    then 'privatemessage'
-    else typeConstant
-
     @listController = new ActivityListController {
-      type          : listType
+      type          : typeConstant
       viewOptions   :
         itemOptions : {channelId}
       wrapper


Fix reply-to-all wrong binding

Fix reply-to-all wrong binding

--- client/app/components/toolbar_message.coffee
+++ client/app/components/toolbar_message.coffee
@@ -32,7 +32,9 @@

     render: ->
-        nav className: 'toolbar toolbar-message btn-toolbar',
+        nav
+            className: 'toolbar toolbar-message btn-toolbar'
+            onClick: (event) -> event.stopPropagation()
             # inverted order due to `pull-right` class
             div(className: cBtnGroup,
                 @renderToolboxMove()


fix check on missing options

fix check on missing options

--- src/drawable/Sprite.coffee
+++ src/drawable/Sprite.coffee
@@ -17,6 +17,7 @@
     # @option options {boolean} hidden Whether to show your sprite or not
     constructor: (asset_name, options) ->
         super
+        if options is undefined then options = {}

         # Image of your sprite
         @image = Rotten.TextureCache[asset_name] or throw new Error "[Rotten.Drawables.Sprite] You did not preloaded one of your assets (name: #{asset_name})"


Fix unresolved conflict

Fix unresolved conflict

--- client/components/filter_search/index.coffee
+++ client/components/filter_search/index.coffee
@@ -42,11 +42,7 @@
               div {
                 className: 'filter-search__checkcircle'
                 dangerouslySetInnerHTML: __html: $(icons()).filter('.check-circle').html()
-<<<<<<< HEAD
                 onClick: => @props.selected(result)
-=======
-                onClick: => @selected(result.id)
->>>>>>> d4633a9... Work towards updating state
               }
             div { className: 'filter-search__article' },
               div { className: 'filter-search__image paginated-list-img', style: backgroundImage: "url(#{result.thumbnail_image})" }


Log when the event is hit

Log when the event is hit

--- scripts/poo-tracker.coffee
+++ scripts/poo-tracker.coffee
@@ -30,6 +30,7 @@
       return res.send "Latest poo: #{reply}"

   robot.on 'connected', ->
+    console.log("Connected event for poop tracker")
     setInterval ->
       checkRedisForShit()
     , 1000


Delete the intercom tab on docs page

Delete the intercom tab on docs page

--- js/outer/docs.coffee
+++ js/outer/docs.coffee
@@ -88,6 +88,8 @@
       categories

     render: (cx) =>
+      # TODO title
+      $('#IntercomTab').text "" # clear the intercom tab
       rewrite = @rewrite_old_name cx.params.splat[0]
       if rewrite != false
         return cx.redirect "/docs" + rewrite