project_server: converted a big switch statement to a namespace.

--- salvus/project_server.coffee
+++ salvus/project_server.coffee
@@ -186,9 +186,17 @@
     # Do all of the tasks laid out above.
     async.series(tasks, cb)

+########################################################################
+# Event Handlers -- these handle various messages as documented
+# in the file "message.coffee".  The function foo handles the message
+# event foo.
+########################################################################
+
+events = {}
+
 # Open the project described by the given mesg, which was sent over
 # the socket.
-open_project = (socket, mesg) ->
+events.open_project = (socket, mesg) ->
     # The first step in opening a project is to wait to receive all of
     # the bundle blobs.  We do the extract step below in _open_project2.
     mesg.bundles = misc.pairs_to_obj( (u, null) for u in mesg.bundle_uuids )
@@ -306,7 +314,7 @@
     )

 # Save the project
-save_project = (socket, mesg) ->
+events.save_project = (socket, mesg) ->
     path     = userpath(mesg.project_id)
     bundles  = "#{path}/.git/bundles"
     resp     = message.project_saved
@@ -373,7 +381,7 @@

 # Close the given project, which involves killing all processes of
 # this user and deleting all of their associated files.
-close_project = (socket, mesg) ->
+events.close_project = (socket, mesg) ->
     uname = username(mesg.project_id)
     async.series([
         # Kill all processes that the user is running.
@@ -395,7 +403,7 @@
 # the readFile function fails, e.g., if the file doesn't exist or the
 # project is not open.  We send the read file over the socket as a
 # blob message.
-read_file_from_project = (socket, mesg) ->
+events.read_file_from_project = (socket, mesg) ->
     data = undefined
     async.series([
         (cb) ->
@@ -413,7 +421,7 @@
     )

 # Write a file to the project
-write_file_to_project = (socket, mesg) ->
+events.write_file_to_project = (socket, mesg) ->
     data_uuid = mesg.data_uuid

     if mesg.path[mesg.path.length-1] == '/'
@@ -478,7 +486,7 @@
             socket.write_mesg('json', message.error(id:mesg.id, error:err))
     )

-move_file_in_project = (socket, mesg) ->
+events.move_file_in_project = (socket, mesg) ->
     user = username(mesg.project_id)
     async.series([
         (c) ->
@@ -504,8 +512,7 @@
             socket.write_mesg('json', message.error(id:mesg.id, error:err))
     )

-
-remove_file_in_project = (socket, mesg) ->
+events.remove_file_in_project = (socket, mesg) ->
     user = username(mesg.project_id)
     async.series([
         (c) ->
@@ -523,33 +530,16 @@
         if err
             socket.write_mesg('json', message.error(id:mesg.id, error:err))
     )
-
-
-

 server = net.createServer (socket) ->
     misc_node.enable_mesg(socket)
     socket.on 'mesg', (type, mesg) ->
         if type == 'json' # other types are handled elsewhere
-            switch mesg.event
-                when 'open_project'
-                    open_project(socket, mesg)
-                when 'save_project'
-                    save_project(socket, mesg)
-                when 'close_project'
-                    close_project(socket, mesg)
-                when 'read_file_from_project'
-                    read_file_from_project(socket, mesg)
-                when 'write_file_to_project'
-                    write_file_to_project(socket, mesg)
-                when 'make_directory_in_project'
-                    make_directory_in_project(socket, mesg)
-                when 'move_file_in_project'
-                    move_file_in_project(socket, mesg)
-                when 'remove_file_in_project'
-                    remove_file_in_project(socket, mesg)
-                else
-                    socket.write(message.error("Unknown message event '#{mesg.event}'"))
+            handler = events[mesg.event]
+            if handler?
+                handler(socket, mesg)
+            else
+                socket.write(message.error("Unknown message event '#{mesg.event}'"))

 # Start listening for connections on the socket.
 exports.start_server = start_server = () ->


remove console.log

remove console.log

--- generators/app/templates/scripts/states/menu.coffee
+++ generators/app/templates/scripts/states/menu.coffee
@@ -2,7 +2,6 @@

   create : ->

-    console.log 'menu'
     @backgroundColor = 0xFFFFFF
     @game.state.start 'main'


KD.extend: add info as helper from console.info

--- client/Main/KD.extend.coffee
+++ client/Main/KD.extend.coffee
@@ -142,7 +142,7 @@
   resetAppScripts    :-> @appScripts = {}

   disableLogs:->
-    for method in ['log','warn','error','trace','time','timeEnd']
+    for method in ['log','warn','error','trace','info','time','timeEnd']
       window[method] = noop
       KD[method]     = noop
     delete KD.logsEnabled
@@ -153,6 +153,7 @@
     KD.log     = window.log     = console.log.bind     console
     KD.warn    = window.warn    = console.warn.bind    console
     KD.error   = window.error   = console.error.bind   console
+    KD.info    = window.info    = console.info.bind    console
     KD.time    = window.time    = console.time.bind    console
     KD.timeEnd = window.timeEnd = console.timeEnd.bind console
     KD.logsEnabled = yes


Use new meteor API to authenticate users

--- client/app/app.run.ng.coffee
+++ client/app/app.run.ng.coffee
@@ -9,9 +9,54 @@

     $rootScope.$on '$stateChangeError', (event, toState, toParams, fromState, fromParams, error)->
       if error is 'AUTH_REQUIRED'
+        $rootScope.returnToState = toState
+        $rootScope.returnToStateParams = toParams
         console.log 'Auth required'
         $state.go 'login'

+    Meteor.autorun ->
+      console.group "Autoruning"
+      console.log Meteor.user()
+      console.groupEnd()
+      unless Meteor.user()
+        if Meteor.loggingIn()
+          console.log "LoggingIn"
+          if Meteor.user()
+            console.log "User: #{Meteor.user()}"
+            $state.go 'bookings'
+          else
+            console.log "go login"
+            console.log $state
+            $state.go 'login'
+        else
+          console.log "go login"
+          console.log $state
+          $state.go "login"
+      else
+        console.log "Meteor has user"
+        console.log $state.is 'login'
+
+        $state.go 'bookings' if $state.is 'login'
+
+    #Accounts.onLogin ->
+      #console.group "Login successful ..."
+      #if $state.is 'login'
+        #if $rootScope.returnToState
+          #console.log "redirecting to #{$rootScope.returnToState}"
+          #$state.go $rootScope.returnToStateParams
+        #else
+          #console.log "redirecting to bookings"
+          #$state.go('bookings').then (ok)->
+
+            #console.log "ok"
+          #.catch (err)->
+            #console.error "err"
+
+      #console.groupEnd()
+
+    #Accounts.onLoginFailure ->
+      #console.error "Login failed"
+      #$state.go 'login'

   stateChangeStart: (ev, toState, toParams, fromState, fromParams)->
     console.log 'State change start',


use express in rendering crashreport view

--- server/app.coffee
+++ server/app.coffee
@@ -6,6 +6,7 @@
 _          = require('underscore')
 import_api = require('import-api')
 query_api  = require('query-api')
+fs         = require('fs')

 create_app = (settings, db) ->
     basedir = settings.app.root
@@ -78,6 +79,12 @@
               'Content-Type': 'text/html'
             return res.end body

+    app.get "/crashreport/:id", (req, res) ->
+        #id = req.params.id
+        fs.readFile "#{PUBLIC}/crash_report.html", 'utf8', (err, html) ->
+            return res.send {"ok":0,"errors":err} if err?
+            return res.send html
+
     # for debugging crashreport parsing
     app.get "/crashreports_parse/:id", (req, res) ->
         id = req.params.id


Checkbox: Added test for checkbox.

Checkbox: Added test for checkbox.

--- src/tests/components/inputs/test_checkbox.coffee
+++ src/tests/components/inputs/test_checkbox.coffee
@@ -25,6 +25,10 @@
     expect(checkbox.getElement().checked).toBeTruthy()

+  it 'should be type checkbox', ->
+    expect(checkbox.getAttribute('type')).toBe 'checkbox'
+
+
   it 'should check/uncheck the element and return actual state', ->
     cb = new spark.components.Checkbox


pixel ratio

pixel ratio

--- project/coffee/view/home/InteractiveCanvas.coffee
+++ project/coffee/view/home/InteractiveCanvas.coffee
@@ -60,8 +60,8 @@

     setDims : (renderer=false)=>
         return unless @renderer
-        @w = window.innerWidth
-        @h = window.innerHeight
+        @w = window.innerWidth / window.devicePixelRatio
+        @h = window.innerHeight / window.devicePixelRatio

         @$el.css
             'max-height' : @h


Updated: Atom config (nuclide: alt+cmd+click to open a file)

--- dotfiles/.atom/config.cson
+++ dotfiles/.atom/config.cson
@@ -84,6 +84,8 @@
     ]
     silent: true
   nuclide:
+    hyperclick:
+      darwinTriggerKeys: "altKey,metaKey"
     "nuclide-file-tree":
       allowKeyboardPrefixNavigation: false
       hideIgnoredNames: false


entire canvas need sot be redrawn

entire canvas need sot be redrawn

so this is not needed

--- views/gorillas.coffee
+++ views/gorillas.coffee
@@ -136,7 +136,6 @@
 class Banana
   constructor:(context, initial_x, initial_y) ->
     @context = context
-    @color = '#0000a0'
     @initx = initial_x
     @inity = initial_y
     @x = 0
@@ -153,8 +152,6 @@
     @draw_frame()

   draw: ->
-    @context.fillStyle = @color
-    @context.fillRect @initx+@x, @inity-@y, 40, 30
     @context.drawImage @image(), @initx+@x, @inity-@y

   draw_frame: ->


only handle return events when fields are visible

--- views/gorillas.coffee
+++ views/gorillas.coffee
@@ -494,6 +494,9 @@
   $('#'+player+'_angle').val('')
   $('#'+player+'_velocity').val('')

+window.is_field_visible = (player, field) ->
+  $('#'+player+'_'+field).is(":visible")
+

 jQuery = $
 $(document).ready ->
@@ -506,9 +509,12 @@
       window.show_player_field 'player_1', 'velocity'

   $('#player_1_velocity').bind "keydown", (event) ->
-    if event.keyCode == 13
+    if event.keyCode == 13 && window.is_field_visible  'player_1', 'angle'
+
+      console.log "----------"
       window.hide_player_field 'player_1', 'angle'
       window.hide_player_field 'player_1', 'velocity'
+

       parameters = window.read_angle_and_velocity('player_1')
       parameters.velocity = 0 unless parameters.velocity > 0
@@ -522,7 +528,7 @@
       window.show_player_field 'player_2', 'velocity'

   $('#player_2_velocity').bind "keydown", (event) ->
-    if event.keyCode == 13
+    if event.keyCode == 13 and window.is_field_visible 'player_2', 'angle'
       window.hide_player_field 'player_2', 'angle'
       window.hide_player_field 'player_2', 'velocity'


Name a confusing line of code.

Name a confusing line of code.

--- add-note-count-to-focus-task.coffee
+++ add-note-count-to-focus-task.coffee
@@ -54,8 +54,13 @@
   _countTextExpanderSuggestions: ->
     @_findTextExpanderSnippetsForGroup('Suggested Snippets').length

+  # MRF 2017-02-16
+  # Don't ask me to explain how this works. I stole it from the internet.
+  _createArrayFrom: (thingy) ->
+    [].slice.call(thingy)
+
   _contentForTextExpanderSuggestions: ->
-    snippets = [].slice.call(@_findTextExpanderSnippetsForGroup('Suggested Snippets'))
+    snippets = @_createArrayFrom(@_findTextExpanderSnippetsForGroup('Suggested Snippets'))

     foo = snippets.map((snippet) ->
       snippet.name()


Added a problem t tertiary search

Added a problem t tertiary search

--- server/materials/data/topics/tertiary_search.coffee
+++ server/materials/data/topics/tertiary_search.coffee
@@ -12,9 +12,10 @@
             problem(3398),
             problem(3859),
             problem({testSystem: "codeforces", contest: "1288", problem: "A"}),
+            problem({testSystem: "codeforces", contest: "782", problem: "B"}),
         ]),
         advancedProblems: [
             problem(1116),
             problem({testSystem: "codeforces", contest: "1354", problem: "C1"}),
         ]
-    }+    }


Group: add read group activity permission

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -42,6 +42,9 @@
       'list members'                      :
         public                            : ['moderator']
         private                           : ['moderator']
+      'read group activity'               :
+        public                            : ['guest','member','moderator']
+        private                           : ['member','moderator']
       'create groups'                     : ['moderator']
       'edit groups'                       : ['moderator']
       'edit own groups'                   : ['member','moderator']
@@ -844,7 +847,8 @@

   canEditGroup: permit 'grant permissions'

-  canReadActivity: permit 'read activity'
+  @canReadGroupActivity = permit 'read group activity'
+  canListMembers: permit 'list members'

   canOpenGroup: permit 'open group',
     failure:(client, callback)->


revert e307e20829c8f1f7c72c905a3e12425c509be40e

--- views/components/main/parts/ndock-panel.cjsx
+++ views/components/main/parts/ndock-panel.cjsx
@@ -142,7 +142,7 @@
             <span className="ndock-name">
               {i18n.resources.__ @state.docks[i].name}
             </span>
-            <Label className="ndock-timer" bsStyle="success">
+            <Label className="ndock-timer" bsStyle="default">
               {resolveTime 0}
             </Label>
           </div>


sticky table headers: add a warning when the table body doesn't have any columns (#9)

--- modules/sticky-table-headers/main/index.coffee
+++ modules/sticky-table-headers/main/index.coffee
@@ -68,11 +68,17 @@
     if table.classed('hx-table-full') and not options.fullWidth?
       options.fullWidth = true

-    if resolvedOptions.stickTableHead and table.select('thead').select('tr').empty()
+    if resolvedOptions.stickTableHead and table.select('thead').selectAll('tr').empty()
       # Cant stick something that isn't there
       hx.consoleWarning 'hx.StickyTableHeaders - ' + selector,
-        'Sticky table headers initialized without thead element'
+        'Sticky table headers initialized with stickTableHead of true without a thead element present'
       resolvedOptions.stickTableHead = false
+
+    if resolvedOptions.stickFirstColumn and table.select('tbody').select('tr').selectAll('th, td').empty()
+      # Cant stick something that isn't there
+      hx.consoleWarning 'hx.StickyTableHeaders - ' + selector,
+        'Sticky table headers initialized with stickFirstColumn of true without any columns to stick'
+      resolvedOptions.stickFirstColumn = false

     # Create the container, this will always be the root element.
     container = if tableIsRootElement


kbd prefs: show correct prefix key in legend on startup

--- client/prefs-keyboard.coffee
+++ client/prefs-keyboard.coffee
@@ -100,7 +100,7 @@
   return

 @load = load = (bq) -> # bq: current mappings, possibly not yet saved
-  $pk.val prefs.prefixKey()
+  $pk.val(prefs.prefixKey()).change() # fire a "change" event to update the legend
   loadBQMap bq || keymap.getBQMap()
   return


Fix one require on new test

Fix one require on new test

--- specs/unit/type_factory/factory/js_type_spec.coffee
+++ specs/unit/type_factory/factory/js_type_spec.coffee
@@ -1,4 +1,4 @@
-TypeFactory = require '../../../../lib/type/factory'
+TypeFactory = require '../../../../lib/support/factory'
 should      = require 'should'

 describe 'TypeFactory#jsType', ->


better logging of transport connection attempts and failover

--- lib/assets/javascripts/firehose/consumer.js.coffee
+++ lib/assets/javascripts/firehose/consumer.js.coffee
@@ -36,7 +36,7 @@
   connect: (delay=0) =>
     console?.log "Consumer#connect", delay
     # Get a list of transports that the browser supports
-    supportedTransports = (transport for transport in @config.transports when transport.supported())
+    supportedTransports = (t for t in @config.transports when t.supported())
     # Mmmkay, we've got transports supported by the browser, now lets try connecting
     # to them and dealing with failed connections that might be caused by firewalls,
     # or other network connectivity issues.
@@ -46,9 +46,13 @@
       # If the connection fails, try the next transport supported by the browser.
       @config.failed = =>
         # Map the next transport to connect inside of the current transport failures
-        if nextTransport = supportedTransports.pop()
-          new nextTransport(@config).connect delay
+        if nextTransportType = supportedTransports.pop()
+          nextTransport = new nextTransportType @config
+          console?.log "Attempting to fail over to transport: #{nextTransport.name()}"
+          .connect delay
         else originalFailFun?()
       new transport(@config)
     # Fire off the first connection attempt.
-    transports[0].connect delay
+    [firstTransport] = transports
+    console?.log "Attempting to connect via transport: #{firstTransport.name()}"
+    firstTransport.connect delay


Remove atom.workspaceView shim

Remove atom.workspaceView shim

--- src/atom.coffee
+++ src/atom.coffee
@@ -43,11 +43,6 @@
     @workspaceViewParentSelector = 'body'
     @deserializers = new DeserializerManager()

-    #TODO Remove once all `atom.workspaceView` references have been updated
-    Object.defineProperty this, 'workspaceView',
-      get: -> @workspaceView
-      set: (workspaceView) -> @workspaceView = workspaceView
-
   # Private: Initialize all the properties in this object.
   initialize: ->
     @unsubscribe()


Remove unused openWindow method

Remove unused openWindow method

--- src/atom.coffee
+++ src/atom.coffee
@@ -132,8 +132,6 @@

   open: (url...) ->
     ipc.sendChannel('open', [url...])
-  openWindow: (windowSettings) ->
-    ipc.sendChannel('open-window', windowSettings)

   confirm: (message, detailedMessage, buttonLabelsAndCallbacks...) ->
     buttons = []


D939 - fixing field being cleared

D939 - fixing field being cleared

--- c3/app/assets/javascripts/circs/s2_report_changes/g10_startedEmploymentAndOngoing.coffee
+++ c3/app/assets/javascripts/circs/s2_report_changes/g10_startedEmploymentAndOngoing.coffee
@@ -112,10 +112,10 @@
     S(o.whatDatePaid + "_alternativeDateContextualHelp").css('display', "block")

 hideBeenPaid = (ctx,reset,hideFunction) ->
-  S("beenPaidYetWrap").slideUp(0).attr 'aria-hidden', 'true', -> hideFunction(ctx,reset)
-  S("usuallyPaidSameAmountWrap").slideUp(0).attr 'aria-hidden', 'true'
-  S("monthlyPayDayWrap").slideUp(0).attr 'aria-hidden', 'true'
-  S("beenPaidYetWrap").slideDown(0).attr 'aria-hidden', 'false'
+  S("beenPaidYetWrap").slideUp 0, -> hideFunction(ctx,reset)
+  S("usuallyPaidSameAmountWrap").slideUp 0
+  S("monthlyPayDayWrap").slideUp 0
+  S("beenPaidYetWrap").slideDown 0
   if not (val(ctx.howOften) is "Other")
     S("howOften_wrap").slideUp(0, -> (val ctx.howOftenFrequencyOther, "") if reset)

@@ -138,9 +138,9 @@
   else if checked(ctx.beenPaidYetN)
     onBeenPaidYetN(ctx,false)()
   else
-    S("beenPaidYetWrap").slideUp(0).attr 'aria-hidden', 'true', -> hideBeenPaidYetWrapOnN(ctx,false)
-    S("usuallyPaidSameAmountWrap").slideUp(0).attr 'aria-hidden', 'true'
-    S("monthlyPayDayWrap").slideUp(0).attr 'aria-hidden', 'true'
+    S("beenPaidYetWrap").slideUp 0, -> hideBeenPaidYetWrapOnN(ctx,false)
+    S("usuallyPaidSameAmountWrap").slideUp 0
+    S("monthlyPayDayWrap").slideUp 0

   S(ctx.beenPaidYetY).on "click", onBeenPaidYetY(ctx,true)
   S(ctx.beenPaidYetN).on "click", onBeenPaidYetN(ctx,true)
@@ -151,11 +151,11 @@
 onHowOftenFrequency = (ctx,reset) -> #Double -> because it's a function returning a function, which the jQuqery .on function will receive.
   ->                           #The first function exists so the returned function has the "context" variable in scope
     if visible("usuallyPaidSameAmountWrap")
-      S("usuallyPaidSameAmountWrap").slideUp(0).attr 'aria-hidden', 'true', ->
+      S("usuallyPaidSameAmountWrap").slideUp 0, ->
         checked(ctx.usuallyPaidSameAmountY,false) if reset
         checked(ctx.usuallyPaidSameAmountN,false) if reset

-    S("monthlyPayDayWrap").slideUp(0).attr 'aria-hidden', 'true', -> val(ctx.monthlyPayDay,"") if visible("monthlyPayDayWrap") and reset
+    S("monthlyPayDayWrap").slideUp 0, -> val(ctx.monthlyPayDay,"") if visible("monthlyPayDayWrap") and reset

     if not (val(ctx.howOftenFrequency) is ctx.defaultValue or val(ctx.howOftenFrequency) is "")
       if checked(ctx.beenPaidYetN)
@@ -197,10 +197,10 @@

 window.whatFor = (payIntoPensionY, payIntoPensionN, whatFor) ->
   if not checked(payIntoPensionY)
-    S("whatForWrap").slideUp(0).attr 'aria-hidden', 'true', -> val(whatFor,"")
-  S(payIntoPensionY).on "click", -> S("whatForWrap").slideDown(0).attr 'aria-hidden', 'false'
-
-  S(payIntoPensionN).on "click", -> S("whatForWrap").slideUp(0).attr 'aria-hidden', 'true', -> val(whatFor,"")
+    S("whatForWrap").slideUp 0, -> val(whatFor,"")
+  S(payIntoPensionY).on "click", -> S("whatForWrap").slideDown 0
+
+  S(payIntoPensionN).on "click", -> S("whatForWrap").slideUp 0, -> val(whatFor,"")

 #
@@ -209,10 +209,10 @@

 window.whatThings = (payForThingsY, payForThingsN, whatThings) ->
   if not checked(payForThingsY)
-    S("whatThingsWrap").slideUp(0).attr 'aria-hidden', 'true', -> val(whatThings,"")
-  S(payForThingsY).on "click", -> S("whatThingsWrap").slideDown(0).attr 'aria-hidden', 'false'
-
-  S(payForThingsN).on "click", -> S("whatThingsWrap").slideUp(0).attr 'aria-hidden', 'true', -> val(whatThings,"")
+    S("whatThingsWrap").slideUp 0, -> val(whatThings,"")
+  S(payForThingsY).on "click", -> S("whatThingsWrap").slideDown 0
+
+  S(payForThingsN).on "click", -> S("whatThingsWrap").slideUp 0, -> val(whatThings,"")

 #
@@ -220,8 +220,8 @@
 #
 window.whatCosts = (careCostsForThisWorkY, careCostsForThisWorkN, whatCosts) ->
   if not checked(careCostsForThisWorkY)
-    S("whatCostsWrap").slideUp(0).attr 'aria-hidden', 'true', -> val(whatCosts,"")
-
-  S(careCostsForThisWorkY).on "click", -> S("whatCostsWrap").slideDown(0).attr 'aria-hidden', 'false'
-
-  S(careCostsForThisWorkN).on "click", -> S("whatCostsWrap").slideUp(0).attr 'aria-hidden', 'true', -> val(whatCosts,"")
+    S("whatCostsWrap").slideUp 0, -> val(whatCosts,"")
+
+  S(careCostsForThisWorkY).on "click", -> S("whatCostsWrap").slideDown 0
+
+  S(careCostsForThisWorkN).on "click", -> S("whatCostsWrap").slideUp 0, -> val(whatCosts,"")


update image url to https

update image url to https

--- src/javascript/post_view.coffee
+++ src/javascript/post_view.coffee
@@ -1,6 +1,6 @@
 $ = require 'jquery'

-img_url = "http://i.kinja-img.com/gawker-media/image/upload/c_fill,fl_progressive,g_center,h_180,q_80,w_320"
+img_url = "https://i.kinja-img.com/gawker-media/image/upload/c_fill,fl_progressive,g_center,h_180,q_80,w_320"

 module.exports = PostView =
   width: $(window).width()


Cleaned up getTwoGames random session simulation fetcher. Made it more efficient, made it have a better spread over all games submitted to a ladder with a better bias towards the most recently submitted games.

--- server/queues/scoring.coffee
+++ server/queues/scoring.coffee
@@ -98,130 +98,65 @@
         if taskPairError? then return cb taskPairError, null
         cb null

-selectRandomSkipIndex = (numberOfSessions) ->
-  numbers = [0...numberOfSessions]
-  numberWeights = []
-  lambda = 0.025
-
-  for number, index in numbers
-    numberWeights[index] = lambda*Math.exp(-1*lambda*number) + lambda/(numberOfSessions/15)
-  sum = numberWeights.reduce (a, b) -> a + b
-
-  for number, index in numberWeights
-    numberWeights[index] /= sum
-
-  rand = (min, max) -> Math.random() * (max - min) + min
-
-  totalWeight = 1
-  randomNumber = Math.random()
-  weightSum = 0
-
-  for number, i in numbers
-    weightSum += numberWeights[i]
-
-    if (randomNumber <= weightSum)
-      return numbers[i]
+earliestSubmissionCache = {}
+findEarliestSubmission = (queryParams, callback) ->
+  cacheKey = JSON.stringify queryParams
+  return callback null, cached if cached = earliestSubmissionCache[cacheKey]
+  LevelSession.findOne(queryParams).sort(submitDate: 1).lean().exec (err, earliest) ->
+    return callback err if err
+    result = earliestSubmissionCache[cacheKey] = earliest?.submitDate
+    callback null, result
+
+findRandomSession = (queryParams, callback) ->
+  # We pick a random submitDate between the first submit date for the level and now, then do a $lt fetch to find a session to simulate.
+  # We bias it towards recently submitted sessions.
+  queryParams.submitted = true
+  findEarliestSubmission queryParams, (err, startDate) ->
+    return callback err, null unless startDate
+    now = new Date()
+    interval = now - startDate
+    cutoff = new Date now - Math.pow(Math.random(), 5) * interval
+    queryParams.submitDate = $gte: startDate, $lt: cutoff
+    selection = 'team totalScore transpiledCode submittedCodeLanguage teamSpells levelID creatorName creator submitDate'
+    LevelSession.findOne(queryParams).sort(submitDate: -1).select(selection).lean().exec (err, session) ->
+      return callback err if err
+      callback null, session
+
+formatSessionInformation = (session) ->
+  sessionID: session._id
+  team: session.team ? 'No team'
+  transpiledCode: session.transpiledCode
+  submittedCodeLanguage: session.submittedCodeLanguage
+  teamSpells: session.teamSpells ? {}
+  levelID: session.levelID
+  creatorName: session.creatorName
+  creator: session.creator
+  totalScore: session.totalScore

 module.exports.getTwoGames = (req, res) ->
-  #if userIsAnonymous req then return errors.unauthorized(res, 'You need to be logged in to get games.')
+  #if isUserAnonymous req then return errors.unauthorized(res, 'You need to be logged in to get games.')
   humansGameID = req.body.humansGameID
   ogresGameID = req.body.ogresGameID
   return if simulatorIsTooOld req, res
   #ladderGameIDs = ['greed', 'criss-cross', 'brawlwood', 'dungeon-arena', 'gold-rush', 'sky-span']  # Let's not give any extra simulations to old ladders.
-  ladderGameIDs = ['dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove']  #, 'harrowland']  # harrowland slow for some reason
+  ladderGameIDs = ['dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove', 'harrowland']
   levelID = _.sample ladderGameIDs
-  sortLimit = 200
   unless ogresGameID and humansGameID
-    #fetch random games here
-    queryParams =
-      'levelID': levelID
-      'submitted': true
-      'team': 'humans'
-    selection = 'team totalScore transpiledCode submittedCodeLanguage teamSpells levelID creatorName creator submitDate'
-    LevelSession.count(queryParams).cache().exec (err, numberOfHumans) =>
-      if err? then return errors.serverError(res, 'Couldn\'t get the number of human games')
-      unless numberOfHumans
+    async.map [{levelID: levelID, team: 'humans'}, {levelID: levelID, team: 'ogres'}], findRandomSession, (err, sessions) ->
+      if err then return errors.serverError(res, "Couldn't get two games to simulate for #{levelID}.")
+      unless sessions.length is 2
         res.send(204, 'No games to score.')
         return res.end()
-      numberOfHumans = Math.min numberOfHumans, sortLimit
-      humanSkipCount = Math.floor(Math.random() * numberOfHumans)
-      ogreCountParams =
-        'levelID': levelID
-        'submitted': true
-        'team': 'ogres'
-      LevelSession.count(ogreCountParams).cache().exec (err, numberOfOgres) =>
-        if err? then return errors.serverError(res, 'Couldn\'t get the number of ogre games')
-        unless numberOfOgres
-          res.send(204, 'No games to score.')
-          return res.end()
-        numberOfOgres = Math.min numberOfOgres, sortLimit
-        ogresSkipCount = Math.floor(Math.random() * numberOfOgres)
-
-        query = LevelSession
-          .aggregate()
-          .match(queryParams)
-          .sort({'submitDate': -1})
-          .limit(sortLimit)
-          .project(selection)
-          .skip(humanSkipCount)
-          .limit(1)
-        query.exec (err, randomSession) =>
-          if err? then return errors.serverError(res, "Couldn't select a random session! #{err}")
-          randomSession = randomSession[0]
-          queryParams =
-            'levelID': levelID
-            'submitted': true
-            'team': 'ogres'
-          query = LevelSession
-            .aggregate()
-            .match(queryParams)
-            .sort({'submitDate': -1})
-            .limit(sortLimit)
-            .project(selection)
-            .skip(ogresSkipCount)
-            .limit(1)
-          query.exec (err, otherSession) =>
-            if err? then return errors.serverError(res, 'Couldn\'t select the other random session!')
-            otherSession = otherSession[0]
-            taskObject =
-              'messageGenerated': Date.now()
-              'sessions': []
-            for session in [randomSession, otherSession]
-              sessionInformation =
-                'sessionID': session._id
-                'team': session.team ? 'No team'
-                'transpiledCode': session.transpiledCode
-                'submittedCodeLanguage': session.submittedCodeLanguage
-                'teamSpells': session.teamSpells ? {}
-                'levelID': session.levelID
-                'creatorName': session.creatorName
-                'creator': session.creator
-                'totalScore': session.totalScore
-              taskObject.sessions.push sessionInformation
-            #console.log 'Dispatching random game between', taskObject.sessions[0].creatorName, 'and', taskObject.sessions[1].creatorName
-            sendResponseObject req, res, taskObject
+      taskObject = messageGenerated: Date.now(), sessions: (formatSessionInformation session for session in sessions)
+      #console.log 'Dispatching random game between', taskObject.sessions[0].creatorName, 'and', taskObject.sessions[1].creatorName
+      sendResponseObject req, res, taskObject
   else
     #console.log "Directly simulating #{humansGameID} vs. #{ogresGameID}."
     LevelSession.findOne(_id: humansGameID).select(selection).lean().exec (err, humanSession) =>
       if err? then return errors.serverError(res, 'Couldn\'t find the human game')
       LevelSession.findOne(_id: ogresGameID).select(selection).lean().exec (err, ogreSession) =>
         if err? then return errors.serverError(res, 'Couldn\'t find the ogre game')
-        taskObject =
-          'messageGenerated': Date.now()
-          'sessions': []
-        for session in [humanSession, ogreSession]
-          sessionInformation =
-            'sessionID': session._id
-            'team': session.team ? 'No team'
-            'transpiledCode': session.transpiledCode
-            'submittedCodeLanguage': session.submittedCodeLanguage
-            'teamSpells': session.teamSpells ? {}
-            'levelID': session.levelID
-            'creatorName': session.creatorName
-            'creator': session.creator
-            'totalScore': session.totalScore
-
-          taskObject.sessions.push sessionInformation
+        taskObject = messageGenerated: Date.now(), sessions: (formatSessionInformation session for session in [humanSession, ogreSession])
         sendResponseObject req, res, taskObject

 module.exports.recordTwoGames = (req, res) ->
@@ -399,25 +334,7 @@
 constructTaskObject = (taskMessageBody, message, callback) ->
   async.map taskMessageBody.sessions, getSessionInformation, (err, sessions) ->
     if err? then return callback err
-
-    taskObject =
-      'messageGenerated': Date.now()
-      'sessions': []
-
-    for session in sessions
-      sessionInformation =
-        'sessionID': session._id
-        'submitDate': session.submitDate
-        'team': session.team ? 'No team'
-        'transpiledCode': session.transpiledCode
-        'submittedCodeLanguage': session.submittedCodeLanguage
-        'teamSpells': session.teamSpells ? {}
-        'levelID': session.levelID
-        'creator': session.creator
-        'creatorName': session.creatorName
-        'totalScore': session.totalScore
-
-      taskObject.sessions.push sessionInformation
+    taskObject = messageGenerated: Date.now(), sessions: (formatSessionInformation session for session in sessions)
     callback null, taskObject, message

 constructTaskLogObject = (calculatorUserID, taskObject, message, callback) ->


removed duplicated method

removed duplicated method

--- src/loopback-user-client.coffee
+++ src/loopback-user-client.coffee
@@ -11,28 +11,6 @@
 @module loopback-promised
 ###
 class LoopBackUserClient extends LoopBackClient
-
-
-    ###*
-    Confirm the user's identity.
-
-    @method confirm
-    @param {String} userId
-    @param {String} token The validation token
-    @param {String} redirect URL to redirect the user to once confirmed
-    @return {Promise<Object>}
-    ###
-    confirm: (userId, token, redirect) ->
-
-        path        = '/confirm'
-        http_method = 'GET'
-
-        params =
-            uid      : userId
-            token    : token
-            redirect : redirect
-
-        @request(path, params, http_method)

     ###*


little restructuring.

little restructuring.

--- src/models/NavigationBar.coffee
+++ src/models/NavigationBar.coffee
@@ -92,9 +92,6 @@
       relativeTo = steroids.app.path
       params = {}

-      if options.overrideBackButton?
-        params.overrideBackButton = options.overrideBackButton
-
       if options.constructor.name == "String"
         params.title = options

@@ -106,6 +103,9 @@
         if not options.title?
           params.titleImagePath = relativeTo + options.titleImagePath
           params.title = ""
+
+      if options.overrideBackButton?
+        params.overrideBackButton = options.overrideBackButton

       if options.buttons?


fix bug where updating link does not work

--- src/modules/link-tooltip.coffee
+++ src/modules/link-tooltip.coffee
@@ -11,10 +11,14 @@
   @tooltipInput.value = url

 exitEditMode = ->
-  if ScribeDOM.getText(@tooltipLink) != @tooltipInput.value
+  if @savedLink? or ScribeDOM.getText(@tooltipLink) != @tooltipInput.value
     @tooltipLink.href = @tooltipInput.value
     ScribeDOM.setText(@tooltipLink, @tooltipInput.value)
-    formatLink.call(this, @tooltipInput.value)
+    if @savedLink?
+      @savedLink.href = @tooltipInput.value
+      @savedLink = null
+    else
+      formatLink.call(this, @tooltipInput.value)
   ScribeDOM.removeClass(@tooltip, 'editing')

 formatLink = (value) ->
@@ -34,6 +38,7 @@
         @tooltipLink.href = link.href
         ScribeDOM.removeClass(@tooltip, 'editing')
         showTooptip.call(this, link.getBoundingClientRect())
+        @savedLink = link
         return
       link = link.parentNode
     hideTooltip.call(this)


:lipstick:

:lipstick:

--- spec/app/project-spec.coffee
+++ spec/app/project-spec.coffee
@@ -203,4 +203,4 @@
         project.setHideIgnoredFiles(true)
         expect(project.getHideIgnoredFiles()).toBe(true)
         project.setHideIgnoredFiles(false)
-        expect(project.getHideIgnoredFiles()).toBe(false)+        expect(project.getHideIgnoredFiles()).toBe(false)


Remove previous duplicate-line specs from editor-view-spec

--- spec/editor-view-spec.coffee
+++ spec/editor-view-spec.coffee
@@ -2781,61 +2781,6 @@
           expect(buffer.lineForRow(1)).toBe '    if (items.length <= 1) return items;'
           expect(buffer.lineForRow(2)).toBe '  var sort = function(items) {'

-  describe "when editor:duplicate-lines is triggered", ->
-    describe "where there is no selection", ->
-      describe "when the cursor isn't on a folded line", ->
-        it "duplicates the current line below and moves the cursor down one row", ->
-          editor.setCursorBufferPosition([0, 5])
-          editorView.trigger 'editor:duplicate-lines'
-          expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {'
-          expect(buffer.lineForRow(1)).toBe 'var quicksort = function () {'
-          expect(editor.getCursorBufferPosition()).toEqual [1, 5]
-
-      describe "when the cursor is on a folded line", ->
-        it "duplicates the entire fold before and moves the cursor to the new fold", ->
-          editor.setCursorBufferPosition([4])
-          editor.foldCurrentRow()
-          editorView.trigger 'editor:duplicate-lines'
-          expect(editor.getCursorScreenPosition()).toEqual [5]
-          expect(editor.isFoldedAtScreenRow(4)).toBeTruthy()
-          expect(editor.isFoldedAtScreenRow(5)).toBeTruthy()
-          expect(buffer.lineForRow(8)).toBe '    while(items.length > 0) {'
-          expect(buffer.lineForRow(9)).toBe '      current = items.shift();'
-          expect(buffer.lineForRow(10)).toBe '      current < pivot ? left.push(current) : right.push(current);'
-          expect(buffer.lineForRow(11)).toBe '    }'
-
-      describe "when the cursor is on the last line and it doesn't have a trailing newline", ->
-        it "inserts a newline and the duplicated line", ->
-          editor.moveCursorToBottom()
-          editorView.trigger 'editor:duplicate-lines'
-          expect(buffer.lineForRow(12)).toBe '};'
-          expect(buffer.lineForRow(13)).toBe '};'
-          expect(buffer.lineForRow(14)).toBeUndefined()
-          expect(editor.getCursorBufferPosition()).toEqual [13, 2]
-
-      describe "when the cursor in on the last line and it is only a newline", ->
-        it "duplicates the current line below and moves the cursor down one row", ->
-          editor.moveCursorToBottom()
-          editor.insertNewline()
-          editor.moveCursorToBottom()
-          editorView.trigger 'editor:duplicate-lines'
-          expect(buffer.lineForRow(13)).toBe ''
-          expect(buffer.lineForRow(14)).toBe ''
-          expect(buffer.lineForRow(15)).toBeUndefined()
-          expect(editor.getCursorBufferPosition()).toEqual [14, 0]
-
-      describe "when the cursor is on the second to last line and the last line only a newline", ->
-        it "duplicates the current line below and moves the cursor down one row", ->
-          editor.moveCursorToBottom()
-          editor.insertNewline()
-          editor.setCursorBufferPosition([12])
-          editorView.trigger 'editor:duplicate-lines'
-          expect(buffer.lineForRow(12)).toBe '};'
-          expect(buffer.lineForRow(13)).toBe '};'
-          expect(buffer.lineForRow(14)).toBe ''
-          expect(buffer.lineForRow(15)).toBeUndefined()
-          expect(editor.getCursorBufferPosition()).toEqual [13, 0]
-
   describe "when the escape key is pressed on the editor view", ->
     it "clears multiple selections if there are any, and otherwise allows other bindings to be handled", ->
       atom.keymap.bindKeys 'name', '.editor', {'escape': 'test-event'}


:white_check_mark: Write specs for atomic tokens

--- spec/text-editor-spec.coffee
+++ spec/text-editor-spec.coffee
@@ -1635,6 +1635,19 @@
             [[7, 20], [7, 25]]
           ]

+        it "takes atomic tokens into account", ->
+          waitsForPromise ->
+            atom.project.open('sample-with-tabs-and-leading-comment.coffee', autoIndent: false).then (o) -> editor = o
+
+          runs ->
+            editor.setSelectedBufferRange([[2, 1], [2, 3]])
+            editor.addSelectionBelow()
+
+            expect(editor.getSelectedBufferRanges()).toEqual [
+              [[2, 1], [2, 3]]
+              [[3, 1], [3, 2]]
+            ]
+
       describe "when the selection is empty", ->
         it "does not skip soft-wrapped lines shorter than the current column", ->
           editor.setSoftWrapped(true)
@@ -1722,6 +1735,19 @@
             [[7, 20], [7, 25]]
             [[6, 20], [6, 25]]
           ]
+
+        it "takes atomic tokens into account", ->
+          waitsForPromise ->
+            atom.project.open('sample-with-tabs-and-leading-comment.coffee', autoIndent: false).then (o) -> editor = o
+
+          runs ->
+            editor.setSelectedBufferRange([[3, 1], [3, 2]])
+            editor.addSelectionAbove()
+
+            expect(editor.getSelectedBufferRanges()).toEqual [
+              [[3, 1], [3, 2]]
+              [[2, 1], [2, 3]]
+            ]

       describe "when the selection is empty", ->
         it "does not skip soft-wrapped lines shorter than the current column", ->


:art: Tweak descriptions for moveLine{Up,Down} specs

--- spec/text-editor-spec.coffee
+++ spec/text-editor-spec.coffee
@@ -4192,35 +4192,34 @@
       expect(editor.getSelectedBufferRange()).toEqual [[13, 0], [14, 2]]

   describe ".moveLineUp()", ->
-    it "moves line up", ->
+    it "moves the line under the cursor up", ->
       editor.setCursorBufferPosition([1, 0])
       editor.moveLineUp()
       expect(editor.getTextInBufferRange([[0, 0], [0, 30]])).toBe "  var sort = function(items) {"
       expect(editor.indentationForBufferRow(0)).toBe 1
       expect(editor.indentationForBufferRow(1)).toBe 0
-
-    it "update indentation when config editor.autoIndent is true", ->
+
+    it "updates the line's indentation when the editor.autoIndent setting is true", ->
       atom.config.set('editor.autoIndent', true)
       editor.setCursorBufferPosition([1, 0])
       editor.moveLineUp()
       expect(editor.indentationForBufferRow(0)).toBe 0
       expect(editor.indentationForBufferRow(1)).toBe 0
-
+
   describe ".moveLineDown()", ->
-    it "moves line down", ->
+    it "moves the line under the cursor down", ->
       editor.setCursorBufferPosition([0, 0])
       editor.moveLineDown()
       expect(editor.getTextInBufferRange([[1, 0], [1, 31]])).toBe "var quicksort = function () {"
       expect(editor.indentationForBufferRow(0)).toBe 1
       expect(editor.indentationForBufferRow(1)).toBe 0
-
-    it "update indentation when config editor.autoIndent is true", ->
+
+    it "updates the line's indentation when the editor.autoIndent setting is true", ->
       atom.config.set('editor.autoIndent', true)
       editor.setCursorBufferPosition([0, 0])
       editor.moveLineDown()
       expect(editor.indentationForBufferRow(0)).toBe 1
       expect(editor.indentationForBufferRow(1)).toBe 2
-

   describe ".shouldPromptToSave()", ->
     it "returns false when an edit session's buffer is in use by more than one session", ->


:art: Put subword movement specs under cursor and selection sections

--- spec/text-editor-spec.coffee
+++ spec/text-editor-spec.coffee
@@ -858,6 +858,185 @@
         editor.moveToBeginningOfNextWord()
         expect(editor.getCursorBufferPosition()).toEqual [11, 9]

+    describe ".moveToPreviousSubwordBoundary", ->
+      it "does not move the cursor when there is no previous subword boundary", ->
+        editor.setText('')
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 0])
+
+      it "stops at word and underscore boundaries", ->
+        editor.setText("sub_word \n")
+        editor.setCursorBufferPosition([0, 9])
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 8])
+
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 4])
+
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 0])
+
+        editor.setText(" word\n")
+        editor.setCursorBufferPosition([0, 3])
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 1])
+
+      it "stops at camelCase boundaries", ->
+        editor.setText(" getPreviousWord\n")
+        editor.setCursorBufferPosition([0, 16])
+
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 12])
+
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 4])
+
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 1])
+
+      it "skips consecutive non-word characters", ->
+        editor.setText("e, => \n")
+        editor.setCursorBufferPosition([0, 6])
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 3])
+
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 1])
+
+      it "skips consecutive uppercase characters", ->
+        editor.setText(" AAADF \n")
+        editor.setCursorBufferPosition([0, 7])
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 6])
+
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 1])
+
+        editor.setText("ALPhA\n")
+        editor.setCursorBufferPosition([0, 4])
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 2])
+
+      it "skips consecutive numbers", ->
+        editor.setText(" 88 \n")
+        editor.setCursorBufferPosition([0, 4])
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 3])
+
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 1])
+
+      it "works with multiple cursors", ->
+        editor.setText("curOp\ncursorOptions\n")
+        editor.setCursorBufferPosition([0, 8])
+        editor.addCursorAtBufferPosition([1, 13])
+        [cursor1, cursor2] = editor.getCursors()
+
+        editor.moveToPreviousSubwordBoundary()
+
+        expect(cursor1.getBufferPosition()).toEqual([0, 3])
+        expect(cursor2.getBufferPosition()).toEqual([1, 6])
+
+      it "works with non-English characters", ->
+        editor.setText("supåTøåst \n")
+        editor.setCursorBufferPosition([0, 9])
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 4])
+
+        editor.setText("supaÖast \n")
+        editor.setCursorBufferPosition([0, 8])
+        editor.moveToPreviousSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 4])
+
+    describe ".moveToNextSubwordBoundary", ->
+      it "does not move the cursor when there is no next subword boundary", ->
+        editor.setText('')
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 0])
+
+      it "stops at word and underscore boundaries", ->
+        editor.setText(" sub_word \n")
+        editor.setCursorBufferPosition([0, 0])
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 1])
+
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 4])
+
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 9])
+
+        editor.setText("word \n")
+        editor.setCursorBufferPosition([0, 0])
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 4])
+
+      it "stops at camelCase boundaries", ->
+        editor.setText("getPreviousWord \n")
+        editor.setCursorBufferPosition([0, 0])
+
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 3])
+
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 11])
+
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 15])
+
+      it "skips consecutive non-word characters", ->
+        editor.setText(", => \n")
+        editor.setCursorBufferPosition([0, 0])
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 1])
+
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 4])
+
+      it "skips consecutive uppercase characters", ->
+        editor.setText(" AAADF \n")
+        editor.setCursorBufferPosition([0, 0])
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 1])
+
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 6])
+
+        editor.setText("ALPhA\n")
+        editor.setCursorBufferPosition([0, 0])
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 2])
+
+      it "skips consecutive numbers", ->
+        editor.setText(" 88 \n")
+        editor.setCursorBufferPosition([0, 0])
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 1])
+
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 3])
+
+      it "works with multiple cursors", ->
+        editor.setText("curOp\ncursorOptions\n")
+        editor.setCursorBufferPosition([0, 0])
+        editor.addCursorAtBufferPosition([1, 0])
+        [cursor1, cursor2] = editor.getCursors()
+
+        editor.moveToNextSubwordBoundary()
+        expect(cursor1.getBufferPosition()).toEqual([0, 3])
+        expect(cursor2.getBufferPosition()).toEqual([1, 6])
+
+      it "works with non-English characters", ->
+        editor.setText("supåTøåst \n")
+        editor.setCursorBufferPosition([0, 0])
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 4])
+
+        editor.setText("supaÖast \n")
+        editor.setCursorBufferPosition([0, 0])
+        editor.moveToNextSubwordBoundary()
+        expect(editor.getCursorBufferPosition()).toEqual([0, 4])
+
     describe ".moveToBeginningOfNextParagraph()", ->
       it "moves the cursor before the first line of the next paragraph", ->
         editor.setCursorBufferPosition [0, 6]
@@ -1329,6 +1508,128 @@
         expect(selection3.isReversed()).toBeFalsy()
         expect(selection4.getBufferRange()).toEqual [[3, 30], [3, 31]]
         expect(selection4.isReversed()).toBeFalsy()
+
+    describe ".selectToPreviousSubwordBoundary", ->
+      it "selects subwords", ->
+        editor.setText("")
+        editor.insertText("_word\n")
+        editor.insertText(" getPreviousWord\n")
+        editor.insertText("e, => \n")
+        editor.insertText(" 88 \n")
+        editor.setCursorBufferPosition([0, 5])
+        editor.addCursorAtBufferPosition([1, 7])
+        editor.addCursorAtBufferPosition([2, 5])
+        editor.addCursorAtBufferPosition([3, 3])
+        [selection1, selection2, selection3, selection4] = editor.getSelections()
+
+        editor.selectToPreviousSubwordBoundary()
+        expect(selection1.getBufferRange()).toEqual([[0, 1], [0, 5]])
+        expect(selection1.isReversed()).toBeTruthy()
+        expect(selection2.getBufferRange()).toEqual([[1, 4], [1, 7]])
+        expect(selection2.isReversed()).toBeTruthy()
+        expect(selection3.getBufferRange()).toEqual([[2, 3], [2, 5]])
+        expect(selection3.isReversed()).toBeTruthy()
+        expect(selection4.getBufferRange()).toEqual([[3, 1], [3, 3]])
+        expect(selection4.isReversed()).toBeTruthy()
+
+    describe ".selectToNextSubwordBoundary", ->
+      it "selects subwords", ->
+        editor.setText("")
+        editor.insertText("word_\n")
+        editor.insertText("getPreviousWord\n")
+        editor.insertText("e, => \n")
+        editor.insertText(" 88 \n")
+        editor.setCursorBufferPosition([0, 1])
+        editor.addCursorAtBufferPosition([1, 7])
+        editor.addCursorAtBufferPosition([2, 2])
+        editor.addCursorAtBufferPosition([3, 1])
+        [selection1, selection2, selection3, selection4] = editor.getSelections()
+
+        editor.selectToNextSubwordBoundary()
+        expect(selection1.getBufferRange()).toEqual([[0, 1], [0, 4]])
+        expect(selection1.isReversed()).toBeFalsy()
+        expect(selection2.getBufferRange()).toEqual([[1, 7], [1, 11]])
+        expect(selection2.isReversed()).toBeFalsy()
+        expect(selection3.getBufferRange()).toEqual([[2, 2], [2, 5]])
+        expect(selection3.isReversed()).toBeFalsy()
+        expect(selection4.getBufferRange()).toEqual([[3, 1], [3, 3]])
+        expect(selection4.isReversed()).toBeFalsy()
+
+    describe ".deleteToBeginningOfSubword", ->
+      it "deletes subwords", ->
+        editor.setText("")
+        editor.insertText("_word\n")
+        editor.insertText(" getPreviousWord\n")
+        editor.insertText("e, => \n")
+        editor.insertText(" 88 \n")
+        editor.setCursorBufferPosition([0, 5])
+        editor.addCursorAtBufferPosition([1, 7])
+        editor.addCursorAtBufferPosition([2, 5])
+        editor.addCursorAtBufferPosition([3, 3])
+        [cursor1, cursor2, cursor3, cursor4] = editor.getCursors()
+
+        editor.deleteToBeginningOfSubword()
+        expect(buffer.lineForRow(0)).toBe('_')
+        expect(buffer.lineForRow(1)).toBe(' getviousWord')
+        expect(buffer.lineForRow(2)).toBe('e,  ')
+        expect(buffer.lineForRow(3)).toBe('  ')
+        expect(cursor1.getBufferPosition()).toEqual([0, 1])
+        expect(cursor2.getBufferPosition()).toEqual([1, 4])
+        expect(cursor3.getBufferPosition()).toEqual([2, 3])
+        expect(cursor4.getBufferPosition()).toEqual([3, 1])
+
+        editor.deleteToBeginningOfSubword()
+        expect(buffer.lineForRow(0)).toBe('')
+        expect(buffer.lineForRow(1)).toBe(' viousWord')
+        expect(buffer.lineForRow(2)).toBe('e ')
+        expect(buffer.lineForRow(3)).toBe(' ')
+        expect(cursor1.getBufferPosition()).toEqual([0, 0])
+        expect(cursor2.getBufferPosition()).toEqual([1, 1])
+        expect(cursor3.getBufferPosition()).toEqual([2, 1])
+        expect(cursor4.getBufferPosition()).toEqual([3, 0])
+
+        editor.deleteToBeginningOfSubword()
+        expect(buffer.lineForRow(0)).toBe('')
+        expect(buffer.lineForRow(1)).toBe('viousWord')
+        expect(buffer.lineForRow(2)).toBe('  ')
+        expect(buffer.lineForRow(3)).toBe('')
+        expect(cursor1.getBufferPosition()).toEqual([0, 0])
+        expect(cursor2.getBufferPosition()).toEqual([1, 0])
+        expect(cursor3.getBufferPosition()).toEqual([2, 0])
+        expect(cursor4.getBufferPosition()).toEqual([2, 1])
+
+    describe ".deleteToEndOfSubword", ->
+      it "deletes subwords", ->
+        editor.setText("")
+        editor.insertText("word_\n")
+        editor.insertText("getPreviousWord \n")
+        editor.insertText("e, => \n")
+        editor.insertText(" 88 \n")
+        editor.setCursorBufferPosition([0, 0])
+        editor.addCursorAtBufferPosition([1, 0])
+        editor.addCursorAtBufferPosition([2, 2])
+        editor.addCursorAtBufferPosition([3, 0])
+        [cursor1, cursor2, cursor3, cursor4] = editor.getCursors()
+
+        editor.deleteToEndOfSubword()
+        expect(buffer.lineForRow(0)).toBe('_')
+        expect(buffer.lineForRow(1)).toBe('PreviousWord ')
+        expect(buffer.lineForRow(2)).toBe('e, ')
+        expect(buffer.lineForRow(3)).toBe('88 ')
+        expect(cursor1.getBufferPosition()).toEqual([0, 0])
+        expect(cursor2.getBufferPosition()).toEqual([1, 0])
+        expect(cursor3.getBufferPosition()).toEqual([2, 2])
+        expect(cursor4.getBufferPosition()).toEqual([3, 0])
+
+        editor.deleteToEndOfSubword()
+        expect(buffer.lineForRow(0)).toBe('')
+        expect(buffer.lineForRow(1)).toBe('Word ')
+        expect(buffer.lineForRow(2)).toBe('e,')
+        expect(buffer.lineForRow(3)).toBe(' ')
+        expect(cursor1.getBufferPosition()).toEqual([0, 0])
+        expect(cursor2.getBufferPosition()).toEqual([1, 0])
+        expect(cursor3.getBufferPosition()).toEqual([2, 2])
+        expect(cursor4.getBufferPosition()).toEqual([3, 0])

     describe ".selectWordsContainingCursors()", ->
       describe "when the cursor is inside a word", ->
@@ -4522,307 +4823,6 @@

         waitsForPromise -> editor.checkoutHeadRevision()

-  describe ".moveToPreviousSubwordBoundary", ->
-    it "does not move the cursor when there is no previous subword boundary", ->
-      editor.setText('')
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 0])
-
-    it "stops at word and underscore boundaries", ->
-      editor.setText("sub_word \n")
-      editor.setCursorBufferPosition([0, 9])
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 8])
-
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 4])
-
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 0])
-
-      editor.setText(" word\n")
-      editor.setCursorBufferPosition([0, 3])
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 1])
-
-    it "stops at camelCase boundaries", ->
-      editor.setText(" getPreviousWord\n")
-      editor.setCursorBufferPosition([0, 16])
-
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 12])
-
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 4])
-
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 1])
-
-    it "skips consecutive non-word characters", ->
-      editor.setText("e, => \n")
-      editor.setCursorBufferPosition([0, 6])
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 3])
-
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 1])
-
-    it "skips consecutive uppercase characters", ->
-      editor.setText(" AAADF \n")
-      editor.setCursorBufferPosition([0, 7])
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 6])
-
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 1])
-
-      editor.setText("ALPhA\n")
-      editor.setCursorBufferPosition([0, 4])
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 2])
-
-    it "skips consecutive numbers", ->
-      editor.setText(" 88 \n")
-      editor.setCursorBufferPosition([0, 4])
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 3])
-
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 1])
-
-    it "works with multiple cursors", ->
-      editor.setText("curOp\ncursorOptions\n")
-      editor.setCursorBufferPosition([0, 8])
-      editor.addCursorAtBufferPosition([1, 13])
-      [cursor1, cursor2] = editor.getCursors()
-
-      editor.moveToPreviousSubwordBoundary()
-
-      expect(cursor1.getBufferPosition()).toEqual([0, 3])
-      expect(cursor2.getBufferPosition()).toEqual([1, 6])
-
-    it "works with non-English characters", ->
-      editor.setText("supåTøåst \n")
-      editor.setCursorBufferPosition([0, 9])
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 4])
-
-      editor.setText("supaÖast \n")
-      editor.setCursorBufferPosition([0, 8])
-      editor.moveToPreviousSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 4])
-
-  describe ".moveToNextSubwordBoundary", ->
-    it "does not move the cursor when there is no next subword boundary", ->
-      editor.setText('')
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 0])
-
-    it "stops at word and underscore boundaries", ->
-      editor.setText(" sub_word \n")
-      editor.setCursorBufferPosition([0, 0])
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 1])
-
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 4])
-
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 9])
-
-      editor.setText("word \n")
-      editor.setCursorBufferPosition([0, 0])
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 4])
-
-    it "stops at camelCase boundaries", ->
-      editor.setText("getPreviousWord \n")
-      editor.setCursorBufferPosition([0, 0])
-
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 3])
-
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 11])
-
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 15])
-
-    it "skips consecutive non-word characters", ->
-      editor.setText(", => \n")
-      editor.setCursorBufferPosition([0, 0])
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 1])
-
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 4])
-
-    it "skips consecutive uppercase characters", ->
-      editor.setText(" AAADF \n")
-      editor.setCursorBufferPosition([0, 0])
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 1])
-
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 6])
-
-      editor.setText("ALPhA\n")
-      editor.setCursorBufferPosition([0, 0])
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 2])
-
-    it "skips consecutive numbers", ->
-      editor.setText(" 88 \n")
-      editor.setCursorBufferPosition([0, 0])
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 1])
-
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 3])
-
-    it "works with multiple cursors", ->
-      editor.setText("curOp\ncursorOptions\n")
-      editor.setCursorBufferPosition([0, 0])
-      editor.addCursorAtBufferPosition([1, 0])
-      [cursor1, cursor2] = editor.getCursors()
-
-      editor.moveToNextSubwordBoundary()
-      expect(cursor1.getBufferPosition()).toEqual([0, 3])
-      expect(cursor2.getBufferPosition()).toEqual([1, 6])
-
-    it "works with non-English characters", ->
-      editor.setText("supåTøåst \n")
-      editor.setCursorBufferPosition([0, 0])
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 4])
-
-      editor.setText("supaÖast \n")
-      editor.setCursorBufferPosition([0, 0])
-      editor.moveToNextSubwordBoundary()
-      expect(editor.getCursorBufferPosition()).toEqual([0, 4])
-
-  describe ".selectToPreviousSubwordBoundary", ->
-    it "selects subwords", ->
-      editor.setText("")
-      editor.insertText("_word\n")
-      editor.insertText(" getPreviousWord\n")
-      editor.insertText("e, => \n")
-      editor.insertText(" 88 \n")
-      editor.setCursorBufferPosition([0, 5])
-      editor.addCursorAtBufferPosition([1, 7])
-      editor.addCursorAtBufferPosition([2, 5])
-      editor.addCursorAtBufferPosition([3, 3])
-      [selection1, selection2, selection3, selection4] = editor.getSelections()
-
-      editor.selectToPreviousSubwordBoundary()
-      expect(selection1.getBufferRange()).toEqual([[0, 1], [0, 5]])
-      expect(selection1.isReversed()).toBeTruthy()
-      expect(selection2.getBufferRange()).toEqual([[1, 4], [1, 7]])
-      expect(selection2.isReversed()).toBeTruthy()
-      expect(selection3.getBufferRange()).toEqual([[2, 3], [2, 5]])
-      expect(selection3.isReversed()).toBeTruthy()
-      expect(selection4.getBufferRange()).toEqual([[3, 1], [3, 3]])
-      expect(selection4.isReversed()).toBeTruthy()
-
-  describe ".selectToNextSubwordBoundary", ->
-    it "selects subwords", ->
-      editor.setText("")
-      editor.insertText("word_\n")
-      editor.insertText("getPreviousWord\n")
-      editor.insertText("e, => \n")
-      editor.insertText(" 88 \n")
-      editor.setCursorBufferPosition([0, 1])
-      editor.addCursorAtBufferPosition([1, 7])
-      editor.addCursorAtBufferPosition([2, 2])
-      editor.addCursorAtBufferPosition([3, 1])
-      [selection1, selection2, selection3, selection4] = editor.getSelections()
-
-      editor.selectToNextSubwordBoundary()
-      expect(selection1.getBufferRange()).toEqual([[0, 1], [0, 4]])
-      expect(selection1.isReversed()).toBeFalsy()
-      expect(selection2.getBufferRange()).toEqual([[1, 7], [1, 11]])
-      expect(selection2.isReversed()).toBeFalsy()
-      expect(selection3.getBufferRange()).toEqual([[2, 2], [2, 5]])
-      expect(selection3.isReversed()).toBeFalsy()
-      expect(selection4.getBufferRange()).toEqual([[3, 1], [3, 3]])
-      expect(selection4.isReversed()).toBeFalsy()
-
-  describe ".deleteToBeginningOfSubword", ->
-    it "deletes subwords", ->
-      editor.setText("")
-      editor.insertText("_word\n")
-      editor.insertText(" getPreviousWord\n")
-      editor.insertText("e, => \n")
-      editor.insertText(" 88 \n")
-      editor.setCursorBufferPosition([0, 5])
-      editor.addCursorAtBufferPosition([1, 7])
-      editor.addCursorAtBufferPosition([2, 5])
-      editor.addCursorAtBufferPosition([3, 3])
-      [cursor1, cursor2, cursor3, cursor4] = editor.getCursors()
-
-      editor.deleteToBeginningOfSubword()
-      expect(buffer.lineForRow(0)).toBe('_')
-      expect(buffer.lineForRow(1)).toBe(' getviousWord')
-      expect(buffer.lineForRow(2)).toBe('e,  ')
-      expect(buffer.lineForRow(3)).toBe('  ')
-      expect(cursor1.getBufferPosition()).toEqual([0, 1])
-      expect(cursor2.getBufferPosition()).toEqual([1, 4])
-      expect(cursor3.getBufferPosition()).toEqual([2, 3])
-      expect(cursor4.getBufferPosition()).toEqual([3, 1])
-
-      editor.deleteToBeginningOfSubword()
-      expect(buffer.lineForRow(0)).toBe('')
-      expect(buffer.lineForRow(1)).toBe(' viousWord')
-      expect(buffer.lineForRow(2)).toBe('e ')
-      expect(buffer.lineForRow(3)).toBe(' ')
-      expect(cursor1.getBufferPosition()).toEqual([0, 0])
-      expect(cursor2.getBufferPosition()).toEqual([1, 1])
-      expect(cursor3.getBufferPosition()).toEqual([2, 1])
-      expect(cursor4.getBufferPosition()).toEqual([3, 0])
-
-      editor.deleteToBeginningOfSubword()
-      expect(buffer.lineForRow(0)).toBe('')
-      expect(buffer.lineForRow(1)).toBe('viousWord')
-      expect(buffer.lineForRow(2)).toBe('  ')
-      expect(buffer.lineForRow(3)).toBe('')
-      expect(cursor1.getBufferPosition()).toEqual([0, 0])
-      expect(cursor2.getBufferPosition()).toEqual([1, 0])
-      expect(cursor3.getBufferPosition()).toEqual([2, 0])
-      expect(cursor4.getBufferPosition()).toEqual([2, 1])
-
-  describe ".deleteToEndOfSubword", ->
-    it "deletes subwords", ->
-      editor.setText("")
-      editor.insertText("word_\n")
-      editor.insertText("getPreviousWord \n")
-      editor.insertText("e, => \n")
-      editor.insertText(" 88 \n")
-      editor.setCursorBufferPosition([0, 0])
-      editor.addCursorAtBufferPosition([1, 0])
-      editor.addCursorAtBufferPosition([2, 2])
-      editor.addCursorAtBufferPosition([3, 0])
-      [cursor1, cursor2, cursor3, cursor4] = editor.getCursors()
-
-      editor.deleteToEndOfSubword()
-      expect(buffer.lineForRow(0)).toBe('_')
-      expect(buffer.lineForRow(1)).toBe('PreviousWord ')
-      expect(buffer.lineForRow(2)).toBe('e, ')
-      expect(buffer.lineForRow(3)).toBe('88 ')
-      expect(cursor1.getBufferPosition()).toEqual([0, 0])
-      expect(cursor2.getBufferPosition()).toEqual([1, 0])
-      expect(cursor3.getBufferPosition()).toEqual([2, 2])
-      expect(cursor4.getBufferPosition()).toEqual([3, 0])
-
-      editor.deleteToEndOfSubword()
-      expect(buffer.lineForRow(0)).toBe('')
-      expect(buffer.lineForRow(1)).toBe('Word ')
-      expect(buffer.lineForRow(2)).toBe('e,')
-      expect(buffer.lineForRow(3)).toBe(' ')
-      expect(cursor1.getBufferPosition()).toEqual([0, 0])
-      expect(cursor2.getBufferPosition()).toEqual([1, 0])
-      expect(cursor3.getBufferPosition()).toEqual([2, 2])
-      expect(cursor4.getBufferPosition()).toEqual([3, 0])
-
   describe 'gutters', ->
     describe 'the TextEditor constructor', ->
       it 'creates a line-number gutter', ->


Remove unused variable from text-editor-spec

--- spec/text-editor-spec.coffee
+++ spec/text-editor-spec.coffee
@@ -62,7 +62,6 @@
         atom.workspace.open('sample.less', initialLine: 5).then (o) -> editor = o

       runs ->
-        buffer = editor.buffer
         expect(editor.getLastCursor().getBufferPosition().row).toEqual 5
         expect(editor.getLastCursor().getBufferPosition().column).toEqual 0

@@ -74,7 +73,6 @@
         atom.workspace.open('sample.less', initialColumn: 8).then (o) -> editor = o

       runs ->
-        buffer = editor.buffer
         expect(editor.getLastCursor().getBufferPosition().row).toEqual 0
         expect(editor.getLastCursor().getBufferPosition().column).toEqual 8


Updated test cases

Updated test cases

--- spec/word-jumper-spec.coffee
+++ spec/word-jumper-spec.coffee
@@ -24,15 +24,15 @@
     it "moves right 13-times", ->
       atom.workspaceView.trigger 'word-jumper:move-right' for [1..13]
       pos = editor.getCursorBufferPosition()
-      expect(pos).toEqual [0,42]
+      expect(pos).toEqual [0,40]

-    it "moves right 14-times", ->
-      atom.workspaceView.trigger 'word-jumper:move-right' for [1..14]
+    it "moves right 16-times", ->
+      atom.workspaceView.trigger 'word-jumper:move-right' for [1..16]
       pos = editor.getCursorBufferPosition()
       expect(pos).toEqual [1,0]

-    it "moves right 18-times and left 3-times", ->
-      atom.workspaceView.trigger 'word-jumper:move-right' for [1..18]
+    it "moves right 20-times and left 3-times", ->
+      atom.workspaceView.trigger 'word-jumper:move-right' for [1..20]
       atom.workspaceView.trigger 'word-jumper:move-left' for [1..3]
       pos = editor.getCursorBufferPosition()
       expect(pos).toEqual [1,3]
@@ -41,7 +41,7 @@
       atom.workspaceView.trigger 'word-jumper:move-right' for [1..15]
       atom.workspaceView.trigger 'word-jumper:move-left' for [1..3]
       pos = editor.getCursorBufferPosition()
-      expect(pos).toEqual [0,40]
+      expect(pos).toEqual [0,36]

     it "selectes right 2-times", ->
       atom.workspaceView.trigger 'word-jumper:select-right' for [1..2]


Whitespace

Whitespace

--- src/TransloaditClient.coffee
+++ src/TransloaditClient.coffee
@@ -122,9 +122,9 @@

   calcSignature: (toSign) ->
     return crypto
-    .createHmac("sha1", @_authSecret)
-    .update(new Buffer(toSign, "utf-8"))
-    .digest "hex"
+      .createHmac("sha1", @_authSecret)
+      .update(new Buffer(toSign, "utf-8"))
+      .digest "hex"

   _appendForm: (req, params, fields) ->
     jsonParams = @_prepareParams params
@@ -164,7 +164,7 @@

   _getBoredInstance: (url, customBoredLogic, cb) ->
     if url == null
-      url  = @_serviceUrl() + "/instances/bored"
+      url = @_serviceUrl() + "/instances/bored"

     opts =
       url     : url
@@ -183,7 +183,6 @@
             err =
               error   : "BORED_INSTANCE_ERROR"
               message : "Could not find a bored instance. #{err.message}"
-
             return cb err

           url = "#{@_protocol}api2.#{theUrl}/instances/bored"
@@ -200,7 +199,7 @@
       cb err

   _findBoredInstanceUrl: (cb) ->
-    url = "http://infra-#{@_region}.transloadit.com.s3.amazonaws.com/"
+    url  = "http://infra-#{@_region}.transloadit.com.s3.amazonaws.com/"
     url += "cached_instances.json"

     opts =
@@ -220,7 +219,7 @@
       err = new Error "No responsive uploaders"
       return cb err

-    url = @_protocol + instances[index]
+    url  = @_protocol + instances[index]
     opts =
       url     : url
       timeout : 3000


Refactor .dialog

Refactor .dialog

--- src/bootstrap-dialogs.coffee
+++ src/bootstrap-dialogs.coffee
@@ -6,6 +6,21 @@
   $btn = $('<button type="button" class="btn">').html(text)
   $btn.addClass('btn-primary') if isPrimary
   $btn
+
+
+normalizeButtons = (buttons) ->
+  for button in buttons
+    if button instanceof Array
+      handler = button[1]
+      button = button[0]
+
+    if typeof button is 'string'
+      $btn = mkbutton(button)
+    else
+      $btn = $(button)
+
+    $btn.click(handler) if handler instanceof Function
+    $btn

 exports = Bootstrap.Dialogs =
@@ -35,26 +50,15 @@
       else
         ''

-    buttons = for button in buttons
-      if button instanceof Array
-        handler = button[1]
-        button = button[0]
-
-      if typeof button is 'string'
-        $btn = mkbutton(button)
-      else
-        $btn = $(button)
-
-      $btn.click(handler) if handler instanceof Function
-      $btn
-
     $el = $('
').html([
       $('
').html([
         $closeButton
         title
       ])
       body
-      $('
').html(buttons)
+      $('
').html(
+        normalizeButtons(buttons)
+      )
     ])

     promise = $.Deferred()


Updated formatting to deal with crossing midnight.

--- app/assets/javascripts/handlebars_date.js.coffee
+++ app/assets/javascripts/handlebars_date.js.coffee
@@ -1,4 +1,5 @@
 Handlebars.registerHelper "pretty-time-range", (start, end) ->
   format = "MM/DD"
-  format_2 = "HH:mm"
-  "#{moment(end).tz("America/Los_Angeles").format(format)} Until #{moment(end).tz("America/Los_Angeles").format(format_2)}"+  format_2 = "MM/DD HH:mm"
+  format_2 = "HH:mm" if moment(start).format("MM/DD/YYYY") == moment(end).format("MM/DD/YYYY")
+  "#{moment(start).tz("America/Los_Angeles").format(format)} Until #{moment(end).tz("America/Los_Angeles").format(format_2)}"


Better

Better

--- app/assets/javascripts/roofpig/Display.js.coffee
+++ app/assets/javascripts/roofpig/Display.js.coffee
@@ -31,14 +31,13 @@
     if @settings.flag('canvas') || not webgl_works
       @renderer = new THREE.CanvasRenderer()
     else
-      @renderer = new THREE.WebGLRenderer({ antialias: true })
+      @renderer = new THREE.WebGLRenderer(antialias: true)

     @dom_handler = new DomHandler(@id, roofpig_div, @renderer)

     @scene = new THREE.Scene()
-    @pieces3d = new Pieces3D(@scene, @settings)
     @camera = new Camera(@settings.hover, @settings.pov)
-    @world3d = { pieces: @pieces3d, camera: @camera }
+    @world3d = { pieces: new Pieces3D(@scene, @settings), camera: @camera }

     if (@settings.setup)
       setup_alg = new Alg(@settings.setup)


one line of docs :)

one line of docs :)

--- sync.coffee
+++ sync.coffee
@@ -1,3 +1,4 @@
+# A namespace for sync objects.  Same object abstraction level as DB table: posts, notes, ...
 class bucket
     constructor: (@s, @name, b_opts) ->
         @jd = @s.jd


Fix Camera bounds by adding ScriptManager

--- app/views/play/level/PlayGameDevLevelView.coffee
+++ app/views/play/level/PlayGameDevLevelView.coffee
@@ -4,6 +4,7 @@
 God = require 'lib/God'
 LevelLoader = require 'lib/LevelLoader'
 GoalManager = require 'lib/world/GoalManager'
+ScriptManager = require 'lib/scripts/ScriptManager'
 Surface = require 'lib/surface/Surface'
 ThangType = require 'models/ThangType'
 Level = require 'models/Level'
@@ -46,6 +47,9 @@
       @god.angelsShare.firstWorld = false # HACK
       me.team = TEAM
       @session.set 'team', TEAM
+      @scriptManager = new ScriptManager({
+        scripts: @world.scripts or [], view: @, @session, levelID: @level.get('slug')})
+      @scriptManager.loadFromSession() # Should we? TODO: Figure out how scripts work for game dev levels
       @supermodel.finishLoading()

     .then (supermodel) =>
@@ -63,6 +67,7 @@
       @surface.camera.setBounds(bounds)
       @surface.camera.zoomTo({x: 0, y: 0}, 0.1, 0)
       @surface.setWorld(@world)
+      @scriptManager.initializeCamera()
       @renderSelectors '#info-col'
       @spells = @session.generateSpellsObject()
       @state.set('loading', false)


Add selectpicker for language chooser.

Add selectpicker for language chooser.

--- app/assets/javascripts/nodes.coffee
+++ app/assets/javascripts/nodes.coffee
@@ -131,6 +131,8 @@
 csrfParam = $('meta[name="csrf-param"]').attr('content')
 csrfToken = $('meta[name="csrf-token"]').attr('content')

+$('.selectpicker').selectpicker();
+
 $('[data-toggle="status-submit"]').click (e) ->
   e.preventDefault()


payment: parseInt() needs a radix

payment: parseInt() needs a radix

--- node_modules_koding/koding-payment/lib/main.coffee
+++ node_modules_koding/koding-payment/lib/main.coffee
@@ -155,8 +155,8 @@
               title      : p.name[0]
               code       : p.plan_code[0]
               desc       : if typeof p.description[0] is "string" then p.description[0] else ""
-              feeMonthly : parseInt p.unit_amount_in_cents[0]['USD'][0]['_']
-              feeInitial : parseInt p.setup_fee_in_cents[0]['USD'][0]['_']
+              feeMonthly : parseInt p.unit_amount_in_cents[0]['USD'][0]['_'], 10
+              feeInitial : parseInt p.setup_fee_in_cents[0]['USD'][0]['_'], 10
       callback no, plans

 getPlanInfo = (data, callback)->
@@ -172,8 +172,8 @@
             code       : page.plan.plan_code[0]
             title      : page.plan.name[0]
             desc       : if typeof page.plan.description[0] is "string" then page.plan.description[0] else ""
-            feeMonthly : parseInt page.plan.unit_amount_in_cents[0]['USD'][0]['_']
-            feeInitial : parseInt page.plan.setup_fee_in_cents[0]['USD'][0]['_']
+            feeMonthly : parseInt page.plan.unit_amount_in_cents[0]['USD'][0]['_'], 10
+            feeInitial : parseInt page.plan.setup_fee_in_cents[0]['USD'][0]['_'], 10
           callback no, plan
         else
           callback yes, {}
@@ -206,10 +206,10 @@
              <plan_code>#{data.code}</plan_code>
              <name>#{escape data.title}</name>
              <setup_fee_in_cents>
-               <USD>#{parseInt(data.feeInitial)}</USD>
+               <USD>#{parseInt(data.feeInitial, 10)}</USD>
              </setup_fee_in_cents>
              <unit_amount_in_cents>
-               <USD>#{parseInt(data.feeMonthly)}</USD>
+               <USD>#{parseInt(data.feeMonthly, 10)}</USD>
              </unit_amount_in_cents>
              <plan_interval_length>1</plan_interval_length>
              <plan_interval_unit>months</plan_interval_unit>
@@ -223,8 +223,8 @@
         code       : res.plan.plan_code[0]
         title      : res.plan.name[0]
         desc       : if typeof res.plan.description[0] is "string" then res.plan.description[0] else ""
-        feeMonthly : parseInt res.plan.unit_amount_in_cents[0]['USD'][0]['_']
-        feeInitial : parseInt res.plan.setup_fee_in_cents[0]['USD'][0]['_']
+        feeMonthly : parseInt res.plan.unit_amount_in_cents[0]['USD'][0]['_'], 10
+        feeInitial : parseInt res.plan.setup_fee_in_cents[0]['USD'][0]['_'], 10
       callback no, plan

 deletePlan = (data, callback) ->
@@ -455,8 +455,8 @@
             trans =
               uuid      : t.uuid[0]
               status    : t.status[0]
-              amount    : parseInt t.amount_in_cents[0]['_']
-              tax       : parseInt t.tax_in_cents[0]['_']
+              amount    : parseInt t.amount_in_cents[0]['_'], 10
+              tax       : parseInt t.tax_in_cents[0]['_'], 10
               currency  : t.currency[0]
               datetime  : t.created_at[0]['_']
               refundable: t.refundable[0]['_']


more sound on project view

more sound on project view

--- project/coffee/view/components/ObjectsList.coffee
+++ project/coffee/view/components/ObjectsList.coffee
@@ -24,6 +24,7 @@
         null

     beforeChange : (event, slick, currentSlide, nextSlide) =>
+        Backbone.trigger( 'SoundController:play', 'touchable' )
         @B().objectsContentHackOrder = nextSlide
         @$el.find('video').each ->
             @.pause()


Fixing popover directive to destroy the popover on close

--- app/coffee/directives/common.coffee
+++ app/coffee/directives/common.coffee
@@ -160,7 +160,7 @@
                 state = element.data('state')

                 if state == "closing"
-                    element.popover('hide')
+                    element.popover('destroy')
                     element.data('state', 'closed')

@@ -174,11 +174,11 @@
                 scope.$apply ->
                     fn(scope, {"selectedId": id})

-                element.popover('hide')
+                element.popover('destroy')
                 next.off()

             next.on "click", cancelSelector, (event) ->
-                element.popover('hide')
+                element.popover('destroy')
                 next.off()

             if autoHide


Fix bug in doc_list.coffee when last doc is versioned

Ref #25.

--- assets/javascripts/views/sidebar/doc_list.coffee
+++ assets/javascripts/views/sidebar/doc_list.coffee
@@ -62,7 +62,7 @@
         versions = ''
         loop
           versions += @tmpl('sidebarDoc', doc, disabled: true)
-          break if docs[0].name isnt doc.name
+          break if docs[0]?.name isnt doc.name
           doc = docs.shift()
         html += @tmpl('sidebarDisabledVersionedDoc', doc, versions)
       else


Refactor core: private methods

Refactor core: private methods

--- source/quo.coffee
+++ source/quo.coffee
@@ -34,11 +34,13 @@
     else if $$.toType(selector) is "function"
       $$(document).ready selector
     else
-      dom = $$.getDOMObject(selector, children)
+      dom = _getDOMObject(selector, children)
       _Quo(dom, selector)

+  # ---------------------------------------------------------------------------
   # Static Methods
+  # ---------------------------------------------------------------------------
   $$.extend = (target) ->
     Array::slice.call(arguments, 1).forEach (source) ->
     target[key] = source[key] for key of source
@@ -49,12 +51,20 @@
     OBJECT_PROTOTYPE.toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase()

-  $$.getDOMObject = (selector, children) ->
+  # ---------------------------------------------------------------------------
+  # Private Methods
+  # ---------------------------------------------------------------------------
+  _Quo = (dom = EMPTY_ARRAY, selector = "") ->
+    dom.__proto__ = _Quo::
+    dom
+
+  _getDOMObject = (selector, children) ->
     domain = null
     type = $$.toType selector

-    if type is "array"
-      domain = _compact(selector)
+    if selector instanceof _Quo and not children
+      #@TODO: If selector it's array, fails :/
+      domain = _compact selector

     else if type is "string" and IS_HTML_FRAGMENT.test(selector)
       domain = $$.fragment(selector.trim(), RegExp.$1)
@@ -67,8 +77,8 @@
         if domain.length is 1
           domain = _query(domain[0], children)
         else
-          #@todo: BUG if selector count > 1
-          domain = $$.map(-> _query domain, children )
+          #@TODO: BUG if selector count > 1
+          domain = $$.map(-> _query domain, children)

     else if ELEMENT_TYPES.indexOf(selector.nodeType) >= 0 or selector is window
       domain = [selector]
@@ -76,17 +86,7 @@

     domain

-
-  # Private Methods
-  # ---------------------------------------------------------------------------
-  _Quo = (dom = EMPTY_ARRAY, selector = "") ->
-    dom.__proto__ = _Quo::
-    dom.selector = selector
-    dom
-
   _query = (domain, selector) ->
-    selector = selector.trim()
-
     if CLASS_SELECTOR.test(selector)
       elements = domain.getElementsByClassName selector.replace(".", "")
     else if TAG_SELECTOR.test(selector)
@@ -96,37 +96,28 @@
       unless elements then elements = []
     else
       elements = domain.querySelectorAll selector
-
     if elements.nodeType then [elements] else Array::slice.call(elements)

-  _compact = (array) ->
-    console.log array, [1..100]
-    # array = [1..10]
-    # array.filter (x) -> x > 5
-    array.filter (i) -> i?
-    # array.filter (item) -> item isnt undefined and item isnt null
+  _compact = (items) ->
+    items.filter (item) -> item if item?

-
-  # Instance Methods
-  _Quo::forEach = [].forEach
-
-  _Quo::indexOf = EMPTY_ARRAY.indexOf
-
-  _Quo::each = (callback) ->
-    @forEach (element, index) -> callback.call element, index, element
-
+  # ---------------------------------------------------------------------------
   # Exports
   # ---------------------------------------------------------------------------
   _Quo:: = $$.fn = {}

-  # Instance Methods
+  $$.fn.each = (callback) ->
+    @forEach (element, index) -> callback.call element, index, element
+
+  $$.fn.filter = (selector) ->
+    console.log "selector", selector.length
+    $$ EMPTY_ARRAY.filter.call(@, (el) ->
+      el.parentNode and _query(el.parentNode, selector).indexOf(el) >= 0)
+
   $$.fn.forEach = EMPTY_ARRAY.forEach

   $$.fn.indexOf = EMPTY_ARRAY.indexOf
-
-  $$.fn.each = (callback) ->
-    @forEach (element, index) -> callback.call element, index, element

   $$


Customize ID.

Customize ID.

--- src/redux-axios-reducers.coffee
+++ src/redux-axios-reducers.coffee
@@ -127,8 +127,10 @@

   transformData: (data) -> data

+  getItemId: (item) -> item.id
+
   update: (config={}) =>
-    id = config and config.id or config.data and config.data.id
+    id = config and @getItemId(config) or config.data and @getItemId(config.data)
     return if id then @put(config) else @post(config)

   get: (config) => @fetch(config)
@@ -161,20 +163,21 @@
     @initial.byId = {}
     @initial.data = []

-  reduceSuccess: (state, action) ->
-    singleId = action.payload.config.id or (
-      action.payload.config.data and action.payload.config.data.id)
-
-    state.data = [] if action.payload.config.method == 'get' and not singleId
-    data = action.payload.response.data
+  reduceSuccess: (state, {payload}) ->
+    singleId = @getItemId(payload.config) or (
+      payload.config.data and @getItemId(payload.config.data))
+
+    state.data = [] if payload.config.method == 'get' and not singleId
+    data = payload.response.data
     data = [data] unless Array.isArray(data)

     for item in data
-      continue unless item and item.id
-      state.byId = { state.byId..., [item.id]: item }
-      state.data = [state.data..., item.id] unless singleId
-
-    if action.payload.config.method == 'delete' and singleId
+      continue unless item and @getItemId(item)
+      id = @getItemId(item)
+      state.byId = { state.byId..., [id]: item }
+      state.data = [state.data..., id] unless singleId
+
+    if payload.config.method == 'delete' and singleId
       delete state.byId[singleId]
       state.byId = {state.byId...}
       state.data = (id for id in state.data when id != singleId)
@@ -184,8 +187,10 @@
   transformConfig: (config) ->
     config = super(config)
     id = (
-      config.id or (config.data and config.data.id) or
-      (config.params and config.params.id))
+      @getItemId(config) or
+      (config.data and @getItemId(config.data)) or
+      (config.params and @getItemId(config.params))
+    )
     config.url += "/#{id}" if id
     return config


truncate delta

truncate delta

--- src/scene/objects/entity.coffee
+++ src/scene/objects/entity.coffee
@@ -93,7 +93,7 @@
     @animation.update(timer) unless @animation.ended

     if @moving
-      @x += (@dx * timer.dt)
-      @y += (@dy * timer.dt)
+      @x += (@dx * timer.dt) | 0
+      @y += (@dy * timer.dt) | 0

     null


made geolocation a little more friendly

made geolocation a little more friendly

--- src/scripts/geolocate-ip.coffee
+++ src/scripts/geolocate-ip.coffee
@@ -24,6 +24,6 @@
       .get() (err, res, body) ->
         if res.statusCode is 200
           geolocation_json = JSON.parse(body)
-          msg.send "#{geolocation_json.ip} is from #{geolocation_json.city}, #{geolocation_json.country_name}."
+          msg.send "I'm pretty sure #{geolocation_json.ip} is from #{geolocation_json.city}, #{geolocation_json.country_name}."
         else
-          msg.send "Error: Geolocation failed for #{target_ip} using #{api_url}: #{res.statusCode}"
+          msg.send "Somethign didn't work trying to geolocate#{target_ip} using #{api_url}. :frowning:"


webgl fix bevel

webgl fix bevel

--- bokehjs/src/coffee/renderer/glyph/bokehgl.coffee
+++ bokehjs/src/coffee/renderer/glyph/bokehgl.coffee
@@ -278,13 +278,14 @@
           // Scale to map to pixel coordinates. The original algorithm from the paper
           // assumed isotropic scale. We obviously do not have this.
           vec2 abs_scale_aspect = abs(u_scale_aspect);
-          vec2 abs_scale = u_scale_length *abs_scale_aspect;
+          vec2 abs_scale = u_scale_length * abs_scale_aspect;

           // Attributes to varyings
           v_segment = a_segment * u_scale_length;
           v_length  = u_length * u_scale_length;

           // Correct angles for aspect ratio
+
           vec2 av;
           av = vec2(1.0, tan(a_angles.x)) / abs_scale_aspect;
           v_angles.x = atan(av.y, av.x);
@@ -314,7 +315,7 @@
           float v = a_texcoord.y;
           vec2 o1 = vec2( +t1.y, -t1.x);
           vec2 o2 = vec2( +t2.y, -t2.x);
-
+
           // This is a join
           // ----------------------------------------------------------------
           if( t1 != t2 ) {
@@ -429,7 +430,7 @@
           {
               v_miter.y = 1e10;
           }
-
+
           v_texcoord = vec2( u, v*w );

           // Calculate position in device coordinates. Note that we
@@ -509,13 +510,23 @@
           }
           // Bevel join
           else if ( type == 2 ) {
+              if (dx < segment.x) {
+                  vec2 x= texcoord - vec2(segment.x,0.0);
+                  d = max(d, max(abs(x.x), abs(x.y)));
+
+              } else if (dx > segment.y) {
+                  vec2 x = texcoord - vec2(segment.y,0.0);
+                  d = max(d, max(abs(x.x), abs(x.y)));
+              }
+              /*  Original code for bevel which does not work for us
               if( (dx < segment.x) ||  (dx > segment.y) )
-                  d = max(d, min(abs(miter.x),abs(miter.y)));
+                  d = max(d, min(abs(x.x),abs(x.y)));
+              */
           }
           // Miter limit
           if( (dx < segment.x) ||  (dx > segment.y) ) {
               d = max(d, min(abs(miter.x),abs(miter.y)) - miter_limit*linewidth/2.0 );
-          }
+          }
           return d;
       }


allow remove url

allow remove url

--- app/pages/admin/project-status.cjsx
+++ app/pages/admin/project-status.cjsx
@@ -82,7 +82,7 @@

   updateRedirect:  (e) ->
     _redirectUrl = this.refs.redirectUrl.getDOMNode().value
-    if _redirectUrl?.match(@props.validUrlRegex)
+    if _redirectUrl?.match(@props.validUrlRegex) || _redirectUrl == ""
       handleInputChange.call(@props.project, e)
     else
       @setState(error: @props.invalidUrl)


Bandwidth logging is now sent to a different file.

--- src/server/browser/index.coffee
+++ src/server/browser/index.coffee
@@ -41,10 +41,15 @@

         # Browsers log to logs/#{browser.id}.log
         # TODO: only use logfile if not running tests.
-        @logPath = Path.resolve(__dirname, '..', '..', '..', 'logs', "#{@id}.log")
-        @logStream = FS.createWriteStream(@logPath)
-        @logStream.write("Log opened: #{Date()}\n")
-        @logStream.write("BrowserID: #{@id}\n")
+        @consoleLogPath = Path.resolve(__dirname, '..', '..', '..', 'logs', "#{@id}.log")
+        @consoleLogStream = FS.createWriteStream(@consoleLogPath)
+
+        @bwLogPath = Path.resolve(__dirname, '..', '..', '..', 'logs', "#{@id}.bw.log")
+        @bwLogStream = FS.createWriteStream(@bwLogPath)
+
+        for stream in [@consoleLogStream, @bwLogStream]
+            stream.write("Log opened: #{Date()}\n")
+            stream.write("BrowserID: #{@id}\n")

         # Performance tracking.
         @bytesSent = 0
@@ -53,7 +58,7 @@
             msg = JSON.stringify(params)
             bytes = msg.length
             @bytesSent += bytes
-            @logStream.write("#{Date()}  -- Sent #{bytes} characters (#{msg})\n")
+            @bwLogStream.write("#{Date()}  -- Sent #{bytes} characters (#{msg})\n")

     pauseRendering : () =>
         @emit 'DOMEvent',
@@ -68,7 +73,7 @@
         msg = JSON.stringify(params)
         bytes = msg.length
         @bytesReceived += bytes
-        @logStream.write("#{Date()}  -- Recv #{bytes} characters (#{msg})\n")
+        @bwLogStream.write("#{Date()}  -- Recv #{bytes} characters (#{msg})\n")
         @pauseRendering()
         @events.processEvent(params)
         @resumeRendering()
@@ -77,10 +82,14 @@
         msg = JSON.stringify(params)
         bytes = msg.length
         @bytesReceived += bytes
-        @logStream.write("#{Date()}  -- Recv #{bytes} characters (#{msg})\n")
+        @bwLogStream.write("#{Date()}  -- Recv #{bytes} characters (#{msg})\n")
         @clientAPI.DOMUpdate(params)

     processComponentEvent : (params) ->
+        msg = JSON.stringify(params)
+        bytes = msg.length
+        @bytesReceived += bytes
+        @bwLogStream.write("#{Date()}  -- Recv #{bytes} characters (#{msg})\n")
         node = @dom.nodes.get(params.nodeID)
         if !node
             throw new Error("Invalid component nodeID: #{params.nodeID}")
@@ -142,7 +151,8 @@
                     args = Array.prototype.slice.call(arguments)
                     args.push('\n')
                     str = args.join(' ')
-                    self.logStream.write(str)
+                    self.consoleLogStream.write(str)
+                    # For connected debug clients.
                     self.emit('log', str)

         if process.env.TESTS_RUNNING


quick fix for group.link

quick fix for group.link

--- app/scripts/controllers/main.coffee
+++ app/scripts/controllers/main.coffee
@@ -2,7 +2,10 @@

 angular.module('acsApp')
     .controller 'MainCtrl', ($scope, resourceFactory) ->
-        $scope.group = resourceFactory.Group.get()
+        $scope.group = resourceFactory.Group.get(null, ->
+            # workaround, atm link is not set in graph api response
+            $scope.group.link = 'https://www.facebook.com/groups/AustrianComputerScience/'
+        )
         $scope.getUser = (id) -> resourceFactory.User.get(userid: id, fields: 'picture')

     .controller 'PostCtrl', ($scope, resourceFactory) ->


adapt test to latest data

adapt test to latest data

--- test.coffee
+++ test.coffee
@@ -103,9 +103,9 @@
 			t.strictEqual data[0].name, 'Verkehrsverbund Brandenburg-Berlin'
 			t.done()

-		'lines': (t) -> s.lines(true, 245).then (data) ->
+		'lines': (t) -> s.lines(true, 246).then (data) ->
 			t.strictEqual data.length,      1
-			t.strictEqual data[0].id,       245
+			t.strictEqual data[0].id,       246
 			t.strictEqual data[0].name,     '100'
 			t.strictEqual data[0].type,     'bus'
 			t.done()
@@ -116,8 +116,8 @@
 			t.strictEqual data[0].name,     'U Spichernstr. (Berlin)'
 			t.done()

-		'trips': (t) -> s.trips(true, 77336).then (data) ->
+		'trips': (t) -> s.trips(true, 86341).then (data) ->
 			t.strictEqual data.length,      1
-			t.strictEqual data[0].id,       77336
-			t.strictEqual data[0].lineId,   548 # U9
+			t.strictEqual data[0].id,       86341
+			t.strictEqual data[0].lineId,   551 # U9
 			t.done()


Syntax error.

Syntax error.

--- app/assets/javascripts/web/views/gaze_view.js.coffee
+++ app/assets/javascripts/web/views/gaze_view.js.coffee
@@ -6,7 +6,7 @@
   didInsertElement: ->
     view = this
     $(@scrollParent).on("scroll", (e) -> view.didScroll(view,e))
-    setTimeout(=> $(@scrollParent).trigger("scroll"), 5000)
+    setTimeout((=> $(@scrollParent).trigger("scroll")), 5000)

   didScroll: (view,e) ->
     @get('controller').send('more') if @isScrolledToBottom()


Make a separate list for the items

Make a separate list for the items

Signed-off-by: Ian Macalinao <08370e385f4778cad8fe504ec5445edd3d45bd9a@gmail.com>

--- src/item.coffee
+++ src/item.coffee
@@ -2,6 +2,7 @@
 #<< item_effects

 lol.items = {}
+lol.items.list = {}

 lol._defItem = (name, item) ->
-  lol.items[name] = item
+  lol.items.list[name] = item


make back button on review got back to perf report or calendar as appropriate

--- src/components/task-teacher-review/breadcrumbs.cjsx
+++ src/components/task-teacher-review/breadcrumbs.cjsx
@@ -1,5 +1,5 @@
 React = require 'react'
-Router = require 'react-router'
+BS = require 'react-bootstrap'
 _ = require 'underscore'

 CrumbMixin = require './crumb-mixin'
@@ -8,6 +8,9 @@

 module.exports = React.createClass
   displayName: 'Breadcrumbs'
+
+  contextTypes:
+    router: React.PropTypes.func

   mixins: [ChapterSectionMixin, CrumbMixin]

@@ -31,12 +34,9 @@

       {stepButtons}
-      <Router.Link
-        to='taskplans'
-        params={{courseId}}
-        className='btn btn-default'>
-          Back to Calendar
-      </Router.Link>
+      <BS.Button onClick={@context.router.goBack}>
+          Back
+      </BS.Button>

         {title}


:shit:

:shit:

--- src/extensions/fuzzy-finder/src/fuzzy-finder.coffee
+++ src/extensions/fuzzy-finder/src/fuzzy-finder.coffee
@@ -62,7 +62,6 @@
       @setLoading("Indexing...")
       @rootView.project.getFilePaths().done (paths) =>
         ignoredNames = config.get("fuzzy-finder.ignoredNames")
-        console.log config
         @projectPaths = paths
         if ignoredNames
           @projectPaths = @projectPaths.filter (path) ->


Removing type comments.

Removing type comments.

--- src/math.coffee
+++ src/math.coffee
@@ -21,9 +21,6 @@
 class wolf.Point

     # Create a point.
-    #
-    # @param x {Number} the x co-ordinate
-    # @param y {Number} the y co-ordinate
     constructor : (x, y) ->
         @x = x
         @y = y
@@ -34,18 +31,12 @@

     # Return true if this point is equal to the given point, false
     # otherwise.
-    #
-    # @param other {Object} the point to compare against.
-    # @return {Boolean}
     equals : (other) ->
         # FIXME: floating point math. What to do here?
         return @x == other.x and @y == other.y

     # Return the point produced by summing this point with the given
     # vector.
-    #
-    # @param vector {Object}
-    # @return {Object} the resultant point
     add : (vector) ->
         return new wolf.Vector(@x + vector.x, @y + vector.y)