DEMO: removed some commented-out code.

DEMO: removed some commented-out code.

--- demo/src/Demo.coffee
+++ demo/src/Demo.coffee
@@ -156,32 +156,10 @@
 				text: "Pick a theme!"
 			menu:
 				onClick: (ev, btn, item) =>
-					#	for k in Demo.themeNames
-					#		if not theme
-					#			theme = k
-
-					#	Demo.cssLoader.registerTheme(name: k, )
-					#		if k == window.localStorage.getItem("theme")
-					#			theme = k
-
-					#	Demo.cssLoader.loadTheme(theme)
-					#	.done =>
-
-					old_theme = Demo.cssLoader.getActiveCSS()?.theme
-
-#					if CUI.util.xor(old_theme.startsWith("ng"), item.value.startsWith("ng"))
-#						reload = true
-#					else
-#						reload = false
-
 					theme = item.value
 					Demo.cssLoader.load(theme: theme, url: CUI.getPathToScript() + "/css/cui_#{theme}.css").done( =>
 						console.debug "load theme done, setting item", item.value
 						window.localStorage.setItem("theme", item.value)
-						# FIXME: once "ng" is finished, we can remove the reload
-#						if reload
-#							CUI.toaster(text: "Reloading...", show_ms: null)
-#							document.location.reload()
 					).fail =>
 						CUI.alert
 							title: "Error"


remove stray console.log

remove stray console.log

--- tutor/src/components/performance-forecast/practice.cjsx
+++ tutor/src/components/performance-forecast/practice.cjsx
@@ -18,7 +18,6 @@
   onClick: ->
     {courseId, page_ids} = @props
     route = Router.makePathname('practiceTopics', {courseId}, query: {page_ids})
-    console.log route
     @context.router.transitionTo( route )

   isDisabled: ->


Fix unsubscription link

Fix unsubscription link

--- packages/rocketchat-mailer/server/functions/sendMail.coffee
+++ packages/rocketchat-mailer/server/functions/sendMail.coffee
@@ -40,7 +40,7 @@
 		# Meteor.users.find({ "username": /\.rocket\.team/ }).forEach (user) ->
 			email = user.emails?[0]?.address

-			html = body.replace /\[unsubscribe\]/g, Meteor.absoluteUrl(FlowRouter.path('rocket-mailer/unsubscribe/:hash', { hash: "#{user._id}:#{user.createdAt.getTime()}" }))
+			html = body.replace /\[unsubscribe\]/g, Meteor.absoluteUrl(FlowRouter.path('mailer/unsubscribe/:hash', { hash: "#{user._id}:#{user.createdAt.getTime()}" }))
 			html = html.replace /\[name\]/g, user.name
 			fname = _.strLeft user.name, ' '
 			lname = _.strRightBack user.name, ' '


Remove online status class from channels

Was unneccesarily requiring style override for channel icon (#) color.

--- packages/rocketchat-ui-sidenav/side-nav/chatRoomItem.coffee
+++ packages/rocketchat-ui-sidenav/side-nav/chatRoomItem.coffee
@@ -9,7 +9,7 @@
 			return this.unread

 	userStatus: ->
-		return 'status-' + (Session.get('user_' + this.name + '_status') or 'offline')
+		return 'status-' + (Session.get('user_' + this.name + '_status') or 'offline') if this.t is 'd'

 	name: ->
 		return this.name


better styling on gridlines with no labels radar

--- src/viz/types/radar.coffee
+++ src/viz/types/radar.coffee
@@ -111,7 +111,9 @@
     a = (angle * labelIndex(l)) - Math.PI/2
     top = a < 0 or a > Math.PI
     righty = a < Math.PI/2
-    o = offset a, maxRadius + vars.labels.padding
+    ov = maxRadius
+    ov += vars.labels.padding if vars.labels.value
+    o = offset a, ov
     x = o.x
     y = o.y
     x -= labelWidth unless righty


fixed #17 Multiple dimension Take fails on Scalar

--- src/vocabulary/take.coffee
+++ src/vocabulary/take.coffee
@@ -25,10 +25,11 @@
     # 3↑⍬              <=> 0 0 0
     # ¯2↑⍬             <=> 0 0
     # 0↑⍬              <=> ⍬
+    # 3 3↑1            <=> 3 3⍴1 0 0 0 0 0 0 0 0
     if alpha.shape.length > 1
       throw RankError()
     if omega.shape.length is 0
-      omega = new APLArray [omega.unwrap()]
+      omega = new APLArray [omega.unwrap()], (if alpha.shape.length is 0 then [1] else repeat [1], alpha.shape[0])
     a = alpha.toArray()
     if a.length > omega.shape.length
       throw RankError()


rm process exception catch

rm process exception catch

--- src/qq.coffee
+++ src/qq.coffee
@@ -14,9 +14,6 @@
         @parseVersion()
         @logger = new Log 'info'
         @events = new EventEmitter
-
-        process.on 'uncaughtException', (err)=>
-          @emit 'error', err

     # Public: The version of QQ from npm
     #


Not firing on Chrome

Not firing on Chrome

Signed-off-by: Steven Hoffman <f5bcacb8ce1651b7d1e56f2693431f20e085fdeb@valenciamgmt.com>

--- vendor/assets/javascripts/fustrate/components/file_picker.coffee
+++ vendor/assets/javascripts/fustrate/components/file_picker.coffee
@@ -3,7 +3,7 @@
     input = $ '<input type="file">'

     input
-      .on 'change', (e) ->
+      .change ->
         callback input[0].files

         input.remove()


JMachine: code tidy-up cosmetic

JMachine: code tidy-up cosmetic

--- workers/social/lib/social/models/computeproviders/machine.coffee
+++ workers/social/lib/social/models/computeproviders/machine.coffee
@@ -1,5 +1,6 @@
-{ Module }     = require 'jraphical'
-{ revive }     = require './computeutils'
+
+{ Module }  = require 'jraphical'
+{ revive }  = require './computeutils'

 module.exports = class JMachine extends Module

@@ -124,37 +125,38 @@

       { r: { group, user } } = client

-      selector =
-        $or      : [
-          { _id  : ObjectId machineId }
-          { uid  : machineId }
+      selector  =
+        $or     : [
+          { _id : ObjectId machineId }
+          { uid : machineId }
         ]
-        users    : $elemMatch: id: user.getId()
-        groups   : $elemMatch: id: group.getId()
+        users   : $elemMatch: id: user.getId()
+        groups  : $elemMatch: id: group.getId()

       JMachine.one selector, (err, machine)->
         callback err, machine

   reviveUsers: permit 'populate users',
-  success: (client, callback)->

-    JUser = require '../user'
+    success: (client, callback)->

-    accounts = []
-    queue    = []
+      JUser = require '../user'

-    (@users ? []).forEach (_user)->
-      queue.push -> JUser.one _id: _user.id, (err, user)->
-        if not err? and user
-          user.fetchOwnAccount (err, account)->
-            if not err? and account?
-              accounts.push account
+      accounts = []
+      queue    = []
+
+      (@users ? []).forEach (_user)->
+        queue.push -> JUser.one _id: _user.id, (err, user)->
+          if not err? and user
+            user.fetchOwnAccount (err, account)->
+              if not err? and account?
+                accounts.push account
+              queue.next()
+          else
             queue.next()
-        else
-          queue.next()

-    queue.push ->
-      callback null, accounts
+      queue.push ->
+        callback null, accounts

-    daisy queue
+      daisy queue


JMachine: do not remove permanent users when a normal unshare method called

--- workers/social/lib/social/models/computeproviders/machine.coffee
+++ workers/social/lib/social/models/computeproviders/machine.coffee
@@ -171,13 +171,16 @@
     return owner

-  excludeUser = (users, user)->
+  excludeUser = (options)->
+
+    { users, user, permanent } = options

     userId   = user.getId()
     newUsers = []

     for u in users
-      newUsers.push u  unless userId.equals u.id
+      if u.permanent isnt permanent or not userId.equals u.id
+        newUsers.push u

     return newUsers

@@ -188,7 +191,7 @@
     # passing False for owner
     {user, asOwner, permanent} = options

-    newUsers = excludeUser users, user
+    newUsers = excludeUser { users, user, permanent }
     newUsers.push { id: user.getId(), owner: no, approved: no, permanent }

     return newUsers
@@ -274,12 +277,14 @@
       @update $set: { users }, (err)-> callback err

-  removeUsers: (usersToRemove, callback)->
+  removeUsers: (options, callback)->
+
+    {targets, permanent} = options

     users = @users.splice 0

-    for user in usersToRemove
-      users = excludeUser users, user
+    for user in targets
+      users = excludeUser { users, user, permanent }

     @update $set: { users }, (err)-> callback err

@@ -311,7 +316,7 @@

         if asUser
         then @addUsers {targets, asOwner, permanent}, callback
-        else @removeUsers targets, callback
+        else @removeUsers {targets, permanent}, callback

       else
         callback new KodingError "Target does not support machines."
@@ -481,7 +486,7 @@
       unless isOwner user, this
         return callback new KodingError "Access denied"

-      { target, permanent } = options
+      { target, permanent, asUser } = options

       # At least one target is required
       if not target or target.length is 0
@@ -504,6 +509,13 @@
         return callback \
           new KodingError "It is not allowed to change owner state!"

+      # If it's a call for unshare then no need to check
+      # any other state for it
+      if asUser is no
+        JMachine::shareWith.call this, options, callback
+
+        return
+
       # Permanent option is only valid for paid accounts
       # if its passed then we need to check payment
       #
@@ -590,7 +602,7 @@
     if isOwner user, this
       return callback null

-    options = target: [user.username], asUser: no
+    options = target: [user.username], asUser: no, permanent: yes

     @shareWith options, (err)->
       callback err


JMachine: minor whitespace

JMachine: minor whitespace

--- workers/social/lib/social/models/computeproviders/machine.coffee
+++ workers/social/lib/social/models/computeproviders/machine.coffee
@@ -484,8 +484,8 @@
   share: secure (client, users, callback) ->

     options = target : users, asUser : yes
-
     @shareWith$ client, options, callback
+

   unshare: secure (client, users, callback)->

@@ -502,12 +502,10 @@
   setAsOwner: secure (client, users, callback) ->

     options = target : users, asUser : yes, asOwner : yes
-
     @shareWith$ client, options, callback

   unsetAsOwner: secure (client, users, callback) ->

     options = target : users, asUser : yes, asOwner : no
-
     @shareWith$ client, options, callback


fixed radioGroupValue

fixed radioGroupValue

--- Bacon.UI.coffee
+++ Bacon.UI.coffee
@@ -44,23 +44,23 @@
 Bacon.Observable::awaiting = (response) ->
   @map(true).merge(response.map(false)).toProperty(false).skipDuplicates()

-Bacon.EventStream::ajax = ->
-  @flatMapLatest Bacon.UI.ajax
+Bacon.EventStream::ajax = -> @flatMapLatest Bacon.UI.ajax

 Bacon.UI.radioGroupValue = (radioButtons, init) ->
-  init = radioButtons.val()  unless init?
-  radioButtons.asEventStream("change").map((e) ->
-    e.target.value
-  ).toProperty init
+  if init?
+    radioButtons.each (i, elem) ->
+      $(elem).attr "checked", true if elem.value is init
+  else init = radioButtons.filter(":checked").first().val()
+
+  radioButtons.asEventStream("change").map((e) -> e.target.value).toProperty init

 Bacon.UI.checkBoxValue = (checkbox, initValue) ->
-  isChecked = ->
-    !!checkbox.attr("checked")
-  checkbox.attr "checked", initValue  if initValue isnt null
+  isChecked = -> !!checkbox.attr("checked")
+  checkbox.attr "checked", initValue if initValue isnt null
   checkbox.asEventStream("change").map(isChecked).toProperty(isChecked()).skipDuplicates()

 Bacon.UI.hash = (defaultValue) ->
   getHash = ->
     (if !!document.location.hash then document.location.hash else defaultValue)
-  defaultValue = ""  if defaultValue is undefined
+  defaultValue = "" if defaultValue is undefined
   $(window).asEventStream("hashchange").map(getHash).toProperty(getHash()).skipDuplicates()


fix imageUrl if no upload

fix imageUrl if no upload

--- src/join-request.coffee
+++ src/join-request.coffee
@@ -81,7 +81,7 @@
         content:
           fallback: "New invite request from #{user.displayName}"
           pretext: "#{user.displayName} would like to join Slack!"
-          text: "#{imageUrl}" if req.files.screenshot.name?
+          text: "#{imageUrl}" if req.files.screenshot.name
           fields: [
             {title: 'Full Name'
             value: user.displayName


ignore this

ignore this

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -69,7 +69,6 @@
     @appStorage.fetchStorage (storage)=>
       if not @appStorage.getValue 'shortcuts'
         @putDefaultShortcutsToAppStorage()
-        @appStorage.fetchStorage()

     @fetchedFromFs = no


added task to open xcode project

added task to open xcode project

--- main.coffee
+++ main.coffee
@@ -196,7 +196,21 @@
       else
         message

-    process.exit 1
+
+openXcode = (name) ->
+  try
+    execSync "open iOS/iOS/#{name}.xcworkspace", stdio: 'ignore'
+  catch {message}
+    logErr \
+      if message.match /ENOENT/i
+        """
+        Cannot find #{name}.xcworkspace in iOS/iOS.
+        Run this command from your project's root directory.
+        """
+      else if message.match /EACCES/i
+        "Invalid permissions for opening #{name}.xcworkspace in iOS/iOS"
+      else
+        message

 cli.version '0.0.4'
@@ -213,5 +227,10 @@

     init name

+cli.command 'xcode'
+  .description 'Open Xcode project'
+  .action ->
+    openXcode readConfig().name
+

 cli.parse process.argv


changed launch task description

changed launch task description

--- main.coffee
+++ main.coffee
@@ -334,7 +334,7 @@

 cli.command 'launch'
-  .description 'Run project in simulator and start REPL'
+  .description 'Compile project and run in simulator'
   .action ->
     launch readConfig()


Transactions close too late

Transactions close too late

--- main.coffee
+++ main.coffee
@@ -52,25 +52,23 @@
 	else
 		console.log "Database: #{result.rows[0].version}"

+
 # Attach profiler?
 if process.env.SQLPROF is 'true'
-	dbConnection._query = dbConnection.query
-	dbConnection.query = (q, data, cb) ->
-		logged = q
-		for value, index in data
-			index++
-			while logged.indexOf("$#{index}") isnt -1
-				logged = logged.replace "$#{index}", chalk.blue(JSON.stringify(value))
+	dbConnection.on 'query', (query) ->
 		start = Date.now()
-		@_query q, data, (error, result) ->
+		query.on 'close', ->
+			logged = query.text
+			for value, index in query.values
+				index++
+				while logged.indexOf("$#{index}") isnt -1
+					logged = logged.replace "$#{index}", chalk.blue(JSON.stringify(value))
 			time = Date.now() - start
 			time = switch
 				when time < 10 then chalk.green("#{time} ms")
 				when time < 20 then chalk.yellow("#{time} ms")
 				else chalk.red("#{time} ms")
 			console.log "\n#{time}: #{logged}\n"
-			# console.log "\n#{time}: t: #{logged}\n"
-			if cb? then cb(error, result)

 # Set up Express
@@ -125,9 +123,23 @@
 		pjax: request.header('X-PJAX')?
 		moment: moment
 		plural: plural
+	# Transaction wrapper
+	request.uonline.tx = transaction(dbConnection)
+	request.uonline.tx_id = Math.random()*1000|0
+	console.log "opened tx ##{request.uonline.tx_id}"
+	endTransaction = ->
+		console.log "ending tx ##{request.uonline.tx_id}, state is: #{request.uonline.tx.state()}"
+		if request.uonline.tx.state() isnt 'closed'
+			console.log 'closing tx'
+			request.uonline.tx.commit()  # no sync() here?
+			console.log 'closed tx, state:', request.uonline.tx.state()
+		else
+			console.log 'not closing'
+	response.on 'finish', endTransaction
+	response.on 'close', endTransaction
 	# Read session data
 	user = lib.user.sessionInfoRefreshing.sync(null,
-		dbConnection, request.cookies.sessid, config.sessionExpireTime, true)
+		request.uonline.tx, request.cookies.sessid, config.sessionExpireTime, true)
 	request.uonline.user = user
 	# utility
 	writeDisplayRace = (x) ->
@@ -142,12 +154,12 @@
 		key = "#{x.race}-#{x.gender}"
 		x.displayRace = tmp[key]
 	# Read character data
-	character = lib.game.getCharacter.sync null, dbConnection, request.uonline.user.character_id
+	character = lib.game.getCharacter.sync null, request.uonline.tx, request.uonline.user.character_id
 	if character?
 		writeDisplayRace(character)
 	request.uonline.character = character
 	# Read all user's characters data
-	characters = lib.game.getCharacters.sync null, dbConnection, request.uonline.user.id
+	characters = lib.game.getCharacters.sync null, request.uonline.tx, request.uonline.user.id
 	if characters?
 		characters.forEach writeDisplayRace
 	request.uonline.characters = characters
@@ -199,13 +211,13 @@

 fetchCharacter = ((request, response) ->
-	character = lib.game.getCharacter.sync null, dbConnection, request.uonline.user.character_id
+	character = lib.game.getCharacter.sync null, request.uonline.tx, request.uonline.user.character_id
 	request.uonline.character = character
 ).asyncMiddleware()

 fetchCharacterFromURL = ((request, response) ->
-	request.uonline.fetched_character = lib.game.getCharacter.sync null, dbConnection, request.params.name
+	request.uonline.fetched_character = lib.game.getCharacter.sync null, request.uonline.tx, request.params.name
 ).asyncMiddleware()

@@ -213,7 +225,7 @@
 	id = parseInt(request.params.id, 10)
 	if isNaN(id)
 		throw new Error '404'
-	chars = lib.game.getCharacter.sync null, dbConnection, id
+	chars = lib.game.getCharacter.sync null, request.uonline.tx, id
 	if not chars?
 		throw new Error '404'
 	for i of chars
@@ -223,7 +235,7 @@

 fetchItems = ((request, response) ->
-	items = lib.game.getCharacterItems.sync null, dbConnection, request.uonline.user.character_id
+	items = lib.game.getCharacterItems.sync null, request.uonline.tx, request.uonline.user.character_id
 	request.uonline.equipment = items.filter (x) -> x.equipped
 	request.uonline.equipment.shield = request.uonline.equipment.find (x) -> x.type == 'shield'
 	request.uonline.equipment.right_hand = request.uonline.equipment.find (x) -> x.type.startsWith 'weapon'
@@ -234,19 +246,19 @@

 fetchLocation = ((request, response) ->
 	try
-		location = lib.game.getCharacterLocation.sync null, dbConnection, request.uonline.user.character_id
+		location = lib.game.getCharacterLocation.sync null, request.uonline.tx, request.uonline.user.character_id
 		#request.uonline.pic = request.uonline.picture  if request.uonline.picture?  # TODO: LOLWHAT
 	catch e
 		console.error e.stack
-		location = lib.game.getInitialLocation.sync null, dbConnection
-		lib.game.changeLocation.sync null, dbConnection, request.uonline.user.character_id, location.id
+		location = lib.game.getInitialLocation.sync null, request.uonline.tx
+		lib.game.changeLocation.sync null, request.uonline.tx, request.uonline.user.character_id, location.id
 	request.uonline.location = location
 	return
 ).asyncMiddleware()

 fetchArea = ((request, response) ->
-	area = lib.game.getCharacterArea.sync null, dbConnection, request.uonline.user.character_id
+	area = lib.game.getCharacterArea.sync null, request.uonline.tx, request.uonline.user.character_id
 	request.uonline.area = area
 	return
 ).asyncMiddleware()
@@ -254,14 +266,14 @@

 fetchUsersNearby = ((request, response) ->
 	tmpUsers = lib.game.getNearbyUsers.sync null,
-		dbConnection, request.uonline.user.id, request.uonline.character.location
+		request.uonline.tx, request.uonline.user.id, request.uonline.character.location
 	request.uonline.players_list = tmpUsers
 	return
 ).asyncMiddleware()

 fetchMonstersNearby = ((request, response) ->
-	tmpMonsters = lib.game.getNearbyMonsters.sync null, dbConnection, request.uonline.character.location
+	tmpMonsters = lib.game.getNearbyMonsters.sync null, request.uonline.tx, request.uonline.character.location
 	request.uonline.monsters_list = tmpMonsters
 	request.uonline.monsters_list.in_fight = tmpMonsters.filter((m) -> m.fight_mode)
 	request.uonline.monsters_list.not_in_fight = tmpMonsters.filter((m) -> not m.fight_mode)
@@ -270,7 +282,7 @@

 #fetchStats = ((request, response) ->
-#	chars = lib.game.getUserCharacters.sync null, dbConnection, request.uonline.userid
+#	chars = lib.game.getUserCharacters.sync null, request.uonline.tx, request.uonline.userid
 #	for i of chars
 #		request.uonline[i] = chars[i]
 #	return
@@ -278,7 +290,7 @@

 fetchStatsFromURL = ((request, response) ->
-	chars = lib.game.getUserCharacters.sync null, dbConnection, request.params.username
+	chars = lib.game.getUserCharacters.sync null, request.uonline.tx, request.params.username
 	if not chars?
 		throw new Error '404'
 	for i of chars
@@ -289,7 +301,7 @@

 fetchBattleGroups = ((request, response) ->
 	if request.uonline.character.fight_mode
-		participants = lib.game.getBattleParticipants.sync null, dbConnection, request.uonline.user.character_id
+		participants = lib.game.getBattleParticipants.sync null, request.uonline.tx, request.uonline.user.character_id
 		our_side = participants
 			.find((p) -> p.character_id is request.uonline.user.character_id)
 			.side
@@ -309,6 +321,9 @@

 app.get '/explode/', (request, response) ->
 	throw new Error 'Emulated error.'
+
+app.get '/explode_db/', (request, response) ->
+	request.uonline.tx.query.sync request.uonline.tx, 'SELECT * FROM "Emulated DB error."'

 app.get '/', (request, response) ->
@@ -331,8 +346,8 @@
 	mustNotBeAuthed,
 	setInstance('login'),
 	(request, response) ->
-		if lib.user.accessGranted.sync null, dbConnection, request.body.username, request.body.password
-			sessid = lib.user.createSession.sync null, dbConnection, request.body.username
+		if lib.user.accessGranted.sync null, request.uonline.tx, request.body.username, request.body.password
+			sessid = lib.user.createSession.sync null, request.uonline.tx, request.body.username
 			response.cookie 'sessid', sessid
 			response.redirect 303, '/'
 		else
@@ -353,7 +368,7 @@
 	(request, response) ->
 		usernameIsValid = lib.validation.usernameIsValid(request.body.username)
 		passwordIsValid = lib.validation.passwordIsValid(request.body.password)
-		userExists = lib.user.userExists.sync(null, dbConnection, request.body.username)
+		userExists = lib.user.userExists.sync(null, request.uonline.tx, request.body.username)
 		if (usernameIsValid is true) and (passwordIsValid is true) and (userExists is false)
 			result = lib.user.registerUser.sync(
 				null
@@ -409,7 +424,7 @@
 	mustBeAuthed,
 	(request, response) ->
 		lib.user.closeSession.sync null,
-			dbConnection, request.uonline.user.sessid
+			request.uonline.tx, request.uonline.user.sessid
 		response.redirect 303, '/'  # force GET

@@ -425,7 +440,7 @@
 	setInstance('new_character'),
 	(request, response) ->
 		nameIsValid = lib.validation.characterNameIsValid(request.body.character_name)
-		alreadyExists = lib.character.characterExists.sync(null, dbConnection, request.body.character_name)
+		alreadyExists = lib.character.characterExists.sync(null, request.uonline.tx, request.body.character_name)

 		if nameIsValid and not alreadyExists
 			charid = lib.character.createCharacter.sync(
@@ -462,7 +477,7 @@
 app.post '/action/go',
 	mustBeAuthed,
 	(request, response) ->
-		result = lib.game.changeLocation.sync null, dbConnection, request.uonline.user.character_id, request.body.to
+		result = lib.game.changeLocation.sync null, request.uonline.tx, request.uonline.user.character_id, request.body.to
 		if result.result != 'ok'
 			console.error "Location change failed: #{result.reason}"
 		response.redirect 303, '/game/'
@@ -471,14 +486,14 @@
 app.post '/action/attack',
 	mustBeAuthed,
 	(request, response) ->
-		lib.game.goAttack.sync null, dbConnection, request.uonline.user.character_id
+		lib.game.goAttack.sync null, request.uonline.tx, request.uonline.user.character_id
 		response.redirect 303, '/game/'

 app.post '/action/escape',
 	mustBeAuthed,
 	(request, response) ->
-		lib.game.goEscape.sync null, dbConnection, request.uonline.user.character_id
+		lib.game.goEscape.sync null, request.uonline.tx, request.uonline.user.character_id
 		response.redirect 303, '/game/'

@@ -486,7 +501,7 @@
 	mustBeAuthed,
 	(request, response) ->
 		lib.game.hitOpponent.sync(
-			null, dbConnection,
+			null, request.uonline.tx,
 			request.uonline.user.character_id,
 			request.body.id,
 			request.body.with_item_id
@@ -498,19 +513,19 @@
 	(request, response) ->
 		response.json
 			nick: request.params.nick
-			isNickBusy: lib.user.userExists.sync null, dbConnection, request.params.nick
+			isNickBusy: lib.user.userExists.sync null, request.uonline.tx, request.params.nick

 app.get '/ajax/isCharacterNameBusy/:name',
 	(request, response) ->
 		response.json
 			name: request.params.name
-			isCharacterNameBusy: lib.character.characterExists.sync null, dbConnection, request.params.name
+			isCharacterNameBusy: lib.character.characterExists.sync null, request.uonline.tx, request.params.name

 app.post '/ajax/cheatFixAll',
 	(request, response) ->
-		dbConnection.query.sync dbConnection,
+		request.uonline.tx.query.sync request.uonline.tx,
 			'UPDATE items '+
 				'SET strength = '+
 				'(SELECT strength_max FROM items_proto '+
@@ -523,7 +538,7 @@
 app.post '/action/unequip',
 	mustBeAuthed,
 	(request, response) ->
-		dbConnection.query.sync dbConnection,
+		request.uonline.tx.query.sync request.uonline.tx,
 			'UPDATE items '+
 				'SET equipped = false '+
 				'WHERE id = $1 AND owner = $2',
@@ -534,7 +549,7 @@
 app.post '/action/equip',
 	mustBeAuthed,
 	(request, response) ->
-		dbConnection.query.sync dbConnection,
+		request.uonline.tx.query.sync request.uonline.tx,
 			'UPDATE items '+
 				'SET equipped = true '+
 				'WHERE id = $1 AND owner = $2',
@@ -545,26 +560,35 @@
 app.post '/action/switchCharacter',
 	mustBeAuthed,
 	(request, response) ->
-		lib.character.switchCharacter.sync null, dbConnection, request.uonline.user.id, request.body.id
+		lib.character.switchCharacter.sync null, request.uonline.tx, request.uonline.user.id, request.body.id
 		response.redirect 303, 'back'

 app.post '/action/deleteCharacter',
 	mustBeAuthed,
 	(request, response) ->
-		lib.character.deleteCharacter.sync null, dbConnection, request.uonline.user.id, request.body.id
+		lib.character.deleteCharacter.sync null, request.uonline.tx, request.uonline.user.id, request.body.id
 		response.redirect 303, '/account/'

 app.get '/state/',
 	(request, response, next) ->
-		players = dbConnection.query.sync dbConnection,
+		players = request.uonline.tx.query.sync request.uonline.tx,
 			"SELECT *, (sess_time > NOW() - $1 * INTERVAL '1 SECOND') AS online FROM uniusers",
 			[config.sessionExpireTime]
 		request.uonline.userstate = players.rows
 		next()
 	,
 	setInstance('state'), render('state')
+
+
+app.get '/test/', (req, res, next) ->
+	console.log('test route. it exists, but 404 will appear')
+	next()
+
+app.use (request, response, next) ->
+	console.log('after route')
+	next()

 # 404 handling


:earth_asia::information_desk_person: Updated in browser at strd6.github.io/editor

--- main.coffee
+++ main.coffee
@@ -24,8 +24,10 @@
     when 144 # Note on
       state.toSet = note
       state.moveNext = 2
+
+      playNote(note, note)
     when 128 # Note off
-      ;
+      releaseNote(note)

 {width, height} = require "./pixie"

@@ -150,33 +152,32 @@

 notes = {}
 playNote = (note, id) ->
+  console.log "play!"
   freq = noteToFreq(note - 12)
+
+  console.log freq

   osc = context.createOscillator()
   osc.type = "square"
-  osc.frequency = freq
+  osc.frequency.value = freq
   osc.start()

   osc = Gainer(osc)
   osc.gain.linearRampToValueAtTime(1, context.currentTime)
   osc.connect(masterGain)

-  notes[id] = osc
+  notes[id] = [osc, osc.gain]

 releaseNote = (id) ->
-  osc = notes[id]
+  console.log "release!"
+  [osc, gain] = notes[id]
   # Wow this is nutz!
-  osc.gain.linearRampToValueAtTime(1, context.currentTime)
-  osc.gain.linearRampToValueAtTime(0.0, context.currentTime + 0.125)
+  # Need to set the value to the current value because the
+  # linearRampToValueAtTime uses the previous time to create the ramp, yolo!
+  gain.setValueAtTime(osc.gain.value, context.currentTime)
+  gain.linearRampToValueAtTime(0.0, context.currentTime + 0.125)
   delete notes[id]
-
-  console.log "release!"

   setTimeout ->
     osc.disconnect()
   , 1000
-
-playNote(69, 1)
-setTimeout ->
-  releaseNote(1)
-, 1000


Save window position on quit from tray menu.

--- main.coffee
+++ main.coffee
@@ -78,14 +78,23 @@
        label: 'Clear log and quit',
        click: ->
           clear_log?()
+          localStorage.myoulog_win_position = JSON.stringify ewin.getPosition()
           ewin.close()
+
     },
     {
        label: 'Quit',
        click: ->
-          ewin.close()
+           localStorage.myoulog_win_position = JSON.stringify ewin.getPosition()
+           ewin.close()
+
     }
 ]
+
+win_position = localStorage.myoulog_win_position
+if win_position
+    win_position = JSON.parse win_position
+    ewin.setPosition win_position[0], win_position[1]

 window.trayMenu = Menu.buildFromTemplate trayMenuTemplate
 if tray?


no more zabooma

no more zabooma

--- salvus/page/syncdoc.coffee
+++ salvus/page/syncdoc.coffee
@@ -911,7 +911,6 @@

 class SynchronizedHistory extends AbstractSynchronizedDoc
     constructor: (@editor, opts) ->
-        console.log("zabooma")
         @opts = defaults opts,
             project_id : required
             filename   : required


Feeble attempts at dealing with midnight

--- meteor/schedules/schedules.coffee
+++ meteor/schedules/schedules.coffee
@@ -3,12 +3,24 @@
   timeOffset or= 60
   getDateScheduleForTiploc(now, tiploc, timeOffset)

-@getDateScheduleForTiploc = (date, tiploc, timeOffset) ->
+getOffsetDayOfWeek = (m) ->
   # Days runs starts with monday at index 0, javascript starts with sunday at
   # index 0. We need to shift it along
-  m = moment(date)
-  startTime = parseInt(m.format('HHmm'))
-  endTime = parseInt(m.add('minutes', timeOffset).format('HHmm'))
+  (m.day() + 6) % 7
+
+@getDateScheduleForTiploc = (date, tiploc, timeOffset) ->
+  # FIXME: Retrieve schedules for trains that were schedules the previous day
+  # but finish after midnight, i.e., day of week is the day before and the last
+  # arrival time is after midnight.
+  startDate = moment(date)
+  endDate = startDate.clone().add('minutes', timeOffset)
+  startTime = parseInt(startDate.format('HHmm'))
+  endTime = parseInt(endDate.format('HHmm'))
+
+  startDayOfWeek = getOffsetDayOfWeek(startDate)
+  endDayOfWeek = getOffsetDayOfWeek(endDate)
+
+  dayQuery = {}
   # Special after midnight case
   if endTime < startTime
     timeQuery =
@@ -20,16 +32,23 @@
         time:
           $lte: endTime
       ]
+    startDayQuery = {}
+    startDayQuery["JsonScheduleV1.schedule_days_runs.#{startDayOfWeek}"] = true
+    endDayQuery = {}
+    endDayQuery["JsonScheduleV1.schedule_days_runs.#{endDayOfWeek}"] = true
+    dayQuery =
+      $or: [
+        startDayQuery
+      ,
+        endDayQuery
+      ]
   else
     timeQuery =
       tiploc_code: tiploc
       time:
         $gte: startTime
         $lte: endTime
-
-  day = (date.getDay() + 6) % 7
-  dayQuery = {}
-  dayQuery["JsonScheduleV1.schedule_days_runs.#{day}"] = true
+    dayQuery["JsonScheduleV1.schedule_days_runs.#{startDayOfWeek}"] = true
   Schedules.find
     $and: [
       'JsonScheduleV1.schedule_segment.schedule_location':
@@ -68,7 +87,9 @@
     train.terminated = terminated == 'true'
   train

-@getArrivalAndDepartureTimes = (currentTiploc, scheduleCursor) ->
+@getArrivalAndDepartureTimes = (currentTiploc, scheduleCursor, date) ->
+  date or= new Date()
+  date = moment(date)
   schedules = []
   scheduleCursor.forEach (rawSchedule) ->
     jsonScheduleV1 = rawSchedule.JsonScheduleV1
@@ -97,10 +118,18 @@
         schedule.platform = loc.platform
         break
     schedules.push schedule
+  nowTime = parseInt(date.format('HHmm'))
+  # FIXME: Ensure this works for sorting times as we approach midnight.
   sortedSchedules = _.sortBy schedules, (schedule) ->
     if schedule.arrival?
-      return schedule.arrival
-    schedule.departure
+      time = schedule.arrival
+    else if schedule.departure?
+      time = schedule.departure
+    else
+      return -1
+    if time < nowTime
+      time += 2359
+    time
   return sortedSchedules


Add tests for 'I'

Add tests for 'I'

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -397,6 +397,25 @@
         expect(editorView).toHaveClass 'insert-mode'
         expect(editor.getCursorScreenPosition()).toEqual [0, 2]

+  describe "the I keybinding", ->
+    beforeEach ->
+      editor.getBuffer().setText("11\n  22\n")
+
+    describe "at the end of a line", ->
+      it "switches to insert mode at the beginning of the line", ->
+        editor.setCursorScreenPosition([0,2])
+        keydown('I', shift: true)
+
+        expect(editorView).toHaveClass 'insert-mode'
+        expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+      it "switches to insert mode after leading whitespace", ->
+        editor.setCursorScreenPosition([1,4])
+        keydown('I', shift: true)
+
+        expect(editorView).toHaveClass 'insert-mode'
+        expect(editor.getCursorScreenPosition()).toEqual [1, 2]
+
   describe "the J keybinding", ->
     beforeEach ->
       editor.getBuffer().setText("012\n    456\n")


:white_check_mark: Added specs for #533

:white_check_mark: Added specs for #533

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -2170,6 +2170,51 @@
       treeView.width(1000)
       treeView.find('.tree-view-resize-handle').trigger 'dblclick'
       expect(treeView.width()).toBeLessThan 1000
+
+  describe "when other panels are added", ->
+    beforeEach ->
+      jasmine.attachToDOM(workspaceElement)
+
+    it "should resize normally", ->
+      expect(treeView).toBeVisible()
+      expect(atom.workspace.getLeftPanels().length).toBe(1)
+
+      treeView.width(100)
+
+      expect(treeView.width()).toBe(100)
+
+      panel = document.createElement('div')
+      panel.style.width = '100px'
+      atom.workspace.addLeftPanel({item: panel, priority: 10})
+
+      expect(atom.workspace.getLeftPanels().length).toBe(2)
+      expect(treeView.width()).toBe(100)
+
+      treeView.resizeTreeView({pageX: 250, which: 1})
+
+      expect(treeView.width()).toBe(150)
+
+    it "should resize normally on the right side", ->
+      atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side')
+      expect(treeView).toMatchSelector('[data-show-on-right-side="true"]')
+
+      expect(treeView).toBeVisible()
+      expect(atom.workspace.getRightPanels().length).toBe(1)
+
+      treeView.width(100)
+
+      expect(treeView.width()).toBe(100)
+
+      panel = document.createElement('div')
+      panel.style.width = '100px'
+      atom.workspace.addRightPanel({item: panel, priority: 10})
+
+      expect(atom.workspace.getRightPanels().length).toBe(2)
+      expect(treeView.width()).toBe(100)
+
+      treeView.resizeTreeView({pageX: $(document.body).width() - 250, which: 1})
+
+      expect(treeView.width()).toBe(150)

   describe "selecting items", ->
     [dirView, fileView1, fileView2, fileView3, treeView, rootDirPath, dirPath, filePath1, filePath2, filePath3] = []


Fixing bug on facebook login

Fixing bug on facebook login

--- src/server/Application.coffee
+++ src/server/Application.coffee
@@ -72,7 +72,7 @@

 	onLoginRequest: (session, request, response) =>
 		data = request.getData();
-		if (data.method? and data.method == "facebook")
+		if (data? and data.method? and data.method == "facebook")
 			user = new User();
 			promise = user.loadFromFacebook(data.token);
 			promise.then () =>


Update marker-index.coffee

Update marker-index.coffee

Changed the BRANCHING_FACTOR  to  BRANCHING_THRESHOLD

--- src/marker-index.coffee
+++ src/marker-index.coffee
@@ -1,6 +1,7 @@
 Point = require "./point"

-BRANCHING_FACTOR = 3
+# Max number of children allowed in a Node
+BRANCHING_THRESHOLD = 3

 class Node
   constructor: (@children) ->
@@ -33,8 +34,8 @@

       childStart = childEnd

-    if @children.length > BRANCHING_FACTOR
-      splitIndex = Math.ceil(@children.length / BRANCHING_FACTOR)
+    if @children.length > BRANCHING_THRESHOLD
+      splitIndex = Math.ceil(@children.length / BRANCHING_THRESHOLD)
       [new Node(@children.slice(0, splitIndex)), new Node(@children.slice(splitIndex))]

   findContaining: (start, end) ->


Added support for target in defaultClickHandler

--- core/app/backbone/contrib/default_click_handler.coffee
+++ core/app/backbone/contrib/default_click_handler.coffee
@@ -2,18 +2,24 @@

 # Add a default clickhandler so we can use hrefs
 defaultClickHandler = (e) ->
-  url = $(e.target).closest("a").attr("href")
+  if Backbone.History.started # Capitalization in Backbone.[H]istory is intentional
+    $link = $(e.target).closest("a")
+    url = $link.attr("href")

-  if e.metaKey or e.ctrlKey or e.altKey
-    window.open url, "_blank"
-  else if not Backbone.History.started # Capitalization in Backbone.[H]istory is intentional
-    window.open url
+    target = $link.attr("target")
+    if e.metaKey or e.ctrlKey or e.altKey
+      target = "_blank"
+
+    if target?
+      window.open url, target
+    else
+      navigateTo url, $link.attr("target")
+
+    false # prevent default
   else
-    navigateTo url
-  e.preventDefault()
-  false
+    true

-navigateTo = (url) ->
+navigateTo = (url, target='_self') ->
   if "/" + Backbone.history.fragment is url
     # Allow reloads by clicking links without polluting the history
     Backbone.history.fragment = null
@@ -25,7 +31,7 @@
       Backbone.history.fragment = null # Force creating a state in the history
       Backbone.history.navigate url, false
     else
-      window.open url
+      window.open url, target
       window.focus()

 # HACK: this is needed because internal events did not seem to work


Topic Page: fix pagination problem on activity search

--- client/Social/Activity/AppController.coffee
+++ client/Social/Activity/AppController.coffee
@@ -71,18 +71,8 @@
   fetchCurrentGroup:(callback)-> callback @currentGroupSlug

   search:(text)->
-    {JNewStatusUpdate} = KD.remote.api
-    options =
-      limit : 20
-      skip  : 0
-
-    JNewStatusUpdate.search text, options, (err, activities) =>
-      KD.notify_ err if err
-      @resetAll()
-      @clearPopulateActivityBindings()
-      @listController.listActivities activities
-
-
+    @setWarning text, yes
+    @populateActivity searchText:text

   bindLazyLoad:->
@@ -145,7 +135,11 @@
     groupsController = KD.getSingleton 'groupsController'
     {isReady}        = groupsController
     currentGroup     = groupsController.getCurrentGroup()
-    {filterByTag,to} = options
+    {
+      filterByTag
+      to
+      searchText
+    } = options

     setFeedData = (messages) =>

@@ -173,6 +167,7 @@
         facets     : @getActivityFilter()
         withExempt : no
         slug       : filterByTag
+        searchText : searchText

       options.withExempt = \
         KD.getSingleton("activityController").flags.showExempt?
@@ -186,6 +181,17 @@
         @resetAll()
         @clearPopulateActivityBindings()
         @_wasFilterByTag = filterByTag
+
+      if not to and (searchText or @_wasSearchText)
+        @resetAll()
+        @clearPopulateActivityBindings()
+        @_wasSearchText = searchText
+
+      if searchText? or (@_wasSearchText and to)
+        options.searchText ?= @_wasSearchText
+        @once "searchFeedFetched_#{eventSuffix}", setFeedData
+        @searchActivities options
+        @setWarning searchText, no

       if filterByTag? or (@_wasFilterByTag and to)

@@ -211,6 +217,15 @@

     if isReady then fetch()
     else groupsController.once 'GroupChanged', fetch
+
+  searchActivities:(options = {})->
+    options.to = @lastTo
+    {JNewStatusUpdate} = KD.remote.api
+    eventSuffix = "#{@getFeedFilter()}_#{@getActivityFilter()}"
+    JNewStatusUpdate.search options, (err, activities) =>
+      if err then @emit "activitiesCouldntBeFetched", err
+      else @emit "searchFeedFetched_#{eventSuffix}", activities
+

   fetchTopicActivities:(options = {})->
     options.to = @lastTo


ide: removed unused variable

ide: removed unused variable

--- client/ide/lib/views/tabview/ideview.coffee
+++ client/ide/lib/views/tabview/ideview.coffee
@@ -88,7 +88,7 @@

       { tabHandle, view } = pane
       { options }  = view
-      { machine, paneType } = options
+      { paneType } = options

       switch paneType
@@ -106,7 +106,7 @@

             ideApp.writeSnapshot()

-            @renameEditor paneView, machine, newTitle
+            @renameEditor paneView, newTitle

         when 'terminal'
@@ -836,7 +836,7 @@
       showErrorNotification err

-  renameEditor: (paneView, machine, newTitle) ->
+  renameEditor: (paneView, newTitle) ->

     editorHandle = @tabView.getHandleByPane paneView


Specify utf8 encoding to fs.readFileSync()

--- src/stdlib/fs-utils.coffee
+++ src/stdlib/fs-utils.coffee
@@ -108,7 +108,7 @@

   # Open, read, and close a file, returning the file's contents.
   read: (filePath) ->
-    String fs.readFileSync(filePath)
+    fs.readFileSync(filePath, 'utf8')

   # Open, write, flush, and close a file, writing the given content.
   writeSync: (filePath, content) ->


Remove console.log line

Remove console.log line

--- services/web/public/coffee/main/project-list/project-list.coffee
+++ services/web/public/coffee/main/project-list/project-list.coffee
@@ -213,7 +213,6 @@

 			modalInstance.result.then(
 				(tag) ->
-					console.log "Created tag", tag
 					$scope.tags.push tag
 					$scope.addSelectedProjectsToTag(tag)
 			)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/timed_events.coffee
+++ src/timed_events.coffee
@@ -1,5 +1,5 @@
 ###*
-The TimedEvents module allows arbitrary code to be exe
+The TimedEvents module allows arbitrary code to be executed e

 player = Core


Turned exception into warning so lanejs users can adapt

--- src/stateful_widget.js.coffee
+++ src/stateful_widget.js.coffee
@@ -52,7 +52,8 @@
     constructor: ( selector ) ->
       @_uid ?= @_generateUID()
       @$el = $( selector )
-      throw new Error "Unable to select DOM element with selector '#{selector}'" unless @$el.length
+      if console?.warn? and @$el.length is 0
+        console.warn "No DOM elements found with selector '#{selector}'"
       @el = @$el[0]
       @initState @_states[0] if @_states?[0]?
       @_bindEvents()


Pass child test

Pass child test

--- src/test/test_world.coffee
+++ src/test/test_world.coffee
@@ -39,6 +39,7 @@
   test 'world has children', (t) ->
     world.add_child("Anjali")
     result = world.map_child (child) -> "#{child.get('value')} Prabhakar"
-    #t.equal result[0], "Anjali Prabhakar", "add/map child"
+    t.equal result[0], "Anjali Prabhakar", "add/map child"
+    t.fail() #TODO: Test inheritance
     t.end()


Fix latexmk version checking

Fix latexmk version checking

--- server-ce/Gruntfile.coffee
+++ server-ce/Gruntfile.coffee
@@ -161,7 +161,7 @@

 		checkLatexmk: (callback = (error) ->) ->
 			grunt.log.write "Checking latexmk is installed... "
-			exec "latexmk -version", (error, stdout, stderr) ->
+			exec "latexmk --version", (error, stdout, stderr) ->
 				if error? and error.message.match("command not found")
 					grunt.log.error "FAIL."
 					grunt.log.errorlns """
@@ -179,7 +179,7 @@
 						grunt.log.error "Unknown latexmk version"
 					else
 						version = m[1]
-						if semver.gt(version + ".0", "4.39.0")
+						if semver.gte(version + ".0", "4.39.0")
 							grunt.log.writeln "OK."
 							grunt.log.writeln "Running latexmk version #{version}"
 						else


convert the dropdown tests to mocha (complient) + chai

--- modules/dropdown/test/spec.coffee
+++ modules/dropdown/test/spec.coffee
@@ -7,6 +7,7 @@
   id = '#button'

   fixture = undefined
+  clock = undefined

   getWindowMeasurement = (horizontal, scroll) ->
     if scroll then 0
@@ -61,14 +62,13 @@
     hx.loop = hx_loop = (f) ->
       g = -> hx_loop_update(f, g)
       hx_loop_update(f, g)
-    jasmine.clock().install()
     baseTime = new Date(2013, 0, 1)
-    jasmine.clock().mockDate(baseTime)
+    clock = sinon.useFakeTimers(baseTime.getTime())

   afterAll ->
     fixture.remove()
     hx.loop = savedHxLoop
-    jasmine.clock().uninstall()
+    clock.restore()
     hx.select('body')
       .style('padding', '')
       .style('margin', '')
@@ -88,177 +88,177 @@

   it 'should throw an error when passing in the wrong thing for dropdownContent', ->
-    spyOn(console, 'error')
+    chai.spy.on(console, 'error')
     dd = new hx.Dropdown(id, hx.detached('div'))
     dd.show()
-    expect(console.error).toHaveBeenCalledWith('dropdown: dropdownContent is not a valid type ' + id)
+    console.error.should.have.been.called.with('dropdown: dropdownContent is not a valid type ' + id)

   it 'should create a dropdown object with the correct default options', ->
     dd = new hx.Dropdown(id, content)

-    expect(dd._.selection).toEqual(button)
-    expect(getSpacing(dd)).toEqual(0)
-    expect(dd.options.matchWidth).toEqual(true)
-    expect(dd._.alignments).toEqual('lblt'.split(''))
-    expect(dd._.dropdown).not.toBeDefined()
-    expect(dd._.visible).toEqual(false)
-    expect(dd.options.ddClass).toEqual('')
-    expect(dd.options.mode).toEqual('click')
+    dd._.selection.should.eql(button)
+    getSpacing(dd).should.equal(0)
+    dd.options.matchWidth.should.equal(true)
+    dd._.alignments.should.eql('lblt'.split(''))
+    should.not.exist(dd._.dropdown)
+    dd._.visible.should.equal(false)
+    dd.options.ddClass.should.equal('')
+    dd.options.mode.should.equal('click')

   it 'should set the mode correctly for click', ->
     dd = new hx.Dropdown(id, content, {mode: 'click'})

-    expect(dd._.selection.node().__hx__.eventEmitter.has('click')).toEqual(true)
-    expect(dd._.selection.node().__hx__.eventEmitter.has('mouseover')).toEqual(false)
-    expect(dd._.selection.node().__hx__.eventEmitter.has('mouseout')).toEqual(false)
+    dd._.selection.node().__hx__.eventEmitter.has('click').should.equal(true)
+    dd._.selection.node().__hx__.eventEmitter.has('mouseover').should.equal(false)
+    dd._.selection.node().__hx__.eventEmitter.has('mouseout').should.equal(false)

     dd = new hx.Dropdown(id, content, {mode: 'hover'})
-    expect(dd._.selection.node().__hx__.eventEmitter.has('click')).toEqual(true)
-    expect(dd._.selection.node().__hx__.eventEmitter.has('mouseover')).toEqual(true)
-    expect(dd._.selection.node().__hx__.eventEmitter.has('mouseout')).toEqual(true)
+    dd._.selection.node().__hx__.eventEmitter.has('click').should.equal(true)
+    dd._.selection.node().__hx__.eventEmitter.has('mouseover').should.equal(true)
+    dd._.selection.node().__hx__.eventEmitter.has('mouseout').should.equal(true)

   it 'should set the alignment correctly', ->
     dd = new hx.Dropdown(id, content, {align: 'rbrb'})
-    expect(dd._.alignments).toEqual('rbrb'.split(''))
+    dd._.alignments.should.eql('rbrb'.split(''))

   it 'should use the right alignment option when a named align value is used', ->
     dd = new hx.Dropdown(id, content, {align: 'up'})
-    expect(dd._.alignments).toEqual('ltlb'.split(''))
+    dd._.alignments.should.eql('ltlb'.split(''))

     dd = new hx.Dropdown(id, content, {align: 'down'})
-    expect(dd._.alignments).toEqual('lblt'.split(''))
+    dd._.alignments.should.eql('lblt'.split(''))

     dd = new hx.Dropdown(id, content, {align: 'left'})
-    expect(dd._.alignments).toEqual('ltrt'.split(''))
+    dd._.alignments.should.eql('ltrt'.split(''))

     dd = new hx.Dropdown(id, content, {align: 'right'})
-    expect(dd._.alignments).toEqual('rtlt'.split(''))
+    dd._.alignments.should.eql('rtlt'.split(''))

   it 'should set the spacing correctly', ->
     dd = new hx.Dropdown(id, content, {spacing: 10} )
-    expect(getSpacing(dd)).toEqual(10)
+    getSpacing(dd).should.equal(10)

   # it 'should use the spacing correctly', ->
   #   dd = new hx.Dropdown(id, content, {spacing: 10})
   #   dd.show()
   #   buttonBox = button.box()
-  #   jasmine.clock().tick(301)
+  #   clock.tick(301)
   #   console.log(hx.select('body').node())
   #   ddBox = dd._.dropdown.box()
-  #   expect(ddBox.left).toEqual(buttonBox.left)
-  #   expect(ddBox.top).toEqual(buttonBox.top + buttonBox.height + getSpacing(dd))
+  #   ddBox.left.should.equal(buttonBox.left)
+  #   ddBox.top.should.equal(buttonBox.top + buttonBox.height + getSpacing(dd))

   it 'should set the matchWidth property correctly', ->
     dd = new hx.Dropdown(id, content, {matchWidth: false} )
-    expect(dd.options.matchWidth).toEqual(false)
+    dd.options.matchWidth.should.equal(false)

     dd = new hx.Dropdown(id, content, {matchWidth: true} )
-    expect(dd.options.matchWidth).toEqual(true)
+    dd.options.matchWidth.should.equal(true)

   it 'should set the ddClass correctly', ->
     dd = new hx.Dropdown(id, content, { ddClass: 'bob' })
-    expect(dd.options.ddClass).toEqual('bob')
+    dd.options.ddClass.should.equal('bob')

   it 'should call toggle the selector is clicked in click mode', ->
     dd = new hx.Dropdown(id, content)
-    spyOn(dd, 'toggle')
+    chai.spy.on(dd, 'toggle')
     dd._.selection.node().__hx__.eventEmitter.emit('click')
-    expect(dd.toggle).toHaveBeenCalled()
+    dd.toggle.should.have.been.called()

   it 'should call show/hide on mouseover/mouseout in hover mode', ->
     dd = new hx.Dropdown(id, content, {mode: 'hover'})
-    spyOn(dd, 'show')
-    spyOn(dd, 'hide')
-    spyOn(dd, 'toggle')
+    chai.spy.on(dd, 'show')
+    chai.spy.on(dd, 'hide')
+    chai.spy.on(dd, 'toggle')

     dd._.selection.node().__hx__.eventEmitter.emit('mouseover')
-    expect(dd.show).toHaveBeenCalled()
+    dd.show.should.have.been.called()

     dd._.selection.node().__hx__.eventEmitter.emit('mouseout')
-    expect(dd.hide).toHaveBeenCalled()
+    dd.hide.should.have.been.called()

     dd._.selection.node().__hx__.eventEmitter.emit('click')
-    expect(dd.toggle).toHaveBeenCalled()
+    dd.toggle.should.have.been.called()

   it 'should correctly detect if the dropdown is open', ->
     dd = new hx.Dropdown(id, content)
-    expect(dd.isOpen()).toEqual(false)
-    dd.show()
-    expect(dd.isOpen()).toEqual(true)
+    dd.isOpen().should.equal(false)
+    dd.show()
+    dd.isOpen().should.equal(true)
     dd.hide()
-    expect(dd.isOpen()).toEqual(false)
+    dd.isOpen().should.equal(false)
     dd.toggle()
-    expect(dd.isOpen()).toEqual(true)
+    dd.isOpen().should.equal(true)
     dd.toggle()
-    expect(dd.isOpen()).toEqual(false)
+    dd.isOpen().should.equal(false)

   it 'should exist on the page when opened and set the visible property to true', ->
     dd = new hx.Dropdown(id, content)
-    expect(dd._.visible).toEqual(false)
-    expect(hx.select('.hx-dropdown').empty()).toEqual(true)
+    dd._.visible.should.equal(false)
+    hx.select('.hx-dropdown').empty().should.equal(true)

     dd._.selection.node().__hx__.eventEmitter.emit('click')

-    expect(dd._.visible).toEqual(true)
-    expect(hx.select('.hx-dropdown').empty()).toEqual(false)
-    expect(hx.select('.hx-dropdown').html()).toEqual(content)
+    dd._.visible.should.equal(true)
+    hx.select('.hx-dropdown').empty().should.equal(false)
+    hx.select('.hx-dropdown').html().should.equal(content)

   it 'should not do anything if show is called and the dropdown is already open', ->
     dd = new hx.Dropdown(id, content)
-    expect(dd._.visible).toEqual(false)
-    expect(hx.select('.hx-dropdown').empty()).toEqual(true)
-
-    dd.show()
-    expect(dd._.visible).toEqual(true)
-    expect(hx.select('.hx-dropdown').empty()).toEqual(false)
-    expect(hx.select('.hx-dropdown').html()).toEqual(content)
-
-    dd.show()
-    expect(dd._.visible).toEqual(true)
-    expect(hx.select('.hx-dropdown').empty()).toEqual(false)
-    expect(hx.select('.hx-dropdown').html()).toEqual(content)
+    dd._.visible.should.equal(false)
+    hx.select('.hx-dropdown').empty().should.equal(true)
+
+    dd.show()
+    dd._.visible.should.equal(true)
+    hx.select('.hx-dropdown').empty().should.equal(false)
+    hx.select('.hx-dropdown').html().should.equal(content)
+
+    dd.show()
+    dd._.visible.should.equal(true)
+    hx.select('.hx-dropdown').empty().should.equal(false)
+    hx.select('.hx-dropdown').html().should.equal(content)

   it 'should not do anything if hide is called and the dropdown is already closed', ->
     dd = new hx.Dropdown(id, content)
-    spyOn(dd._.clickDetector, 'off')
-    expect(dd._.visible).toEqual(false)
-    expect(hx.select('.hx-dropdown').empty()).toEqual(true)
+    chai.spy.on(dd._.clickDetector, 'off')
+    dd._.visible.should.equal(false)
+    hx.select('.hx-dropdown').empty().should.equal(true)

     dd.hide()
-    expect(dd._.visible).toEqual(false)
-    expect(hx.select('.hx-dropdown').empty()).toEqual(true)
-    expect(dd._.clickDetector.off).not.toHaveBeenCalled()
+    dd._.visible.should.equal(false)
+    hx.select('.hx-dropdown').empty().should.equal(true)
+    dd._.clickDetector.off.should.have.not.been.called()

   it 'should call the clean up the click detector', ->
     dd = new hx.Dropdown(id, content)
-    spyOn(dd._.clickDetector, 'cleanUp')
+    chai.spy.on(dd._.clickDetector, 'cleanUp')
     dd.cleanUp()
-    expect(dd._.clickDetector.cleanUp).toHaveBeenCalled()
+    dd._.clickDetector.cleanUp.should.have.been.called()

   it 'should call hide when an element other than the button is clicked', ->
     dd = new hx.Dropdown(id, content)
-    spyOn(dd, 'hide')
+    chai.spy.on(dd, 'hide')
     dd.show()
     document.__hx__.eventEmitter.emit('pointerdown', { event: {target: fixture.node()}})
     document.__hx__.eventEmitter.emit('pointerup', { event: {target: fixture.node()}})
-    expect(dd.hide).toHaveBeenCalled()
+    dd.hide.should.have.been.called()

   # it 'should detect parent z-index and set the index to be 1 greater', ->
   #   fixture.style('z-index', 100)
   #   dd = new hx.Dropdown(id, content)
   #   dd.show()
-  #   expect(dd._.dropdown.style('z-index')).toEqual('101')
+  #   dd._.dropdown.style('z-index').should.equal('101')

   it 'should detect parent position and match it correctly', ->
     fixture.style('position', 'fixed')

     dd = new hx.Dropdown(id, content)
     dd.show()
-    expect(dd._.dropdown.style('position')).toEqual('fixed')
+    dd._.dropdown.style('position').should.equal('fixed')

   it 'should render correctly using a function as content', ->
     populate = (elem) ->
@@ -268,34 +268,34 @@

     dd.show()
     # uses fixture bg as hex gets converted to different things by different browsers
-    expect(dd._.dropdown.select('.bob').text()).toEqual('Dave')
+    dd._.dropdown.select('.bob').text().should.equal('Dave')

   it 'should class the dropdown with the supplied dd class', ->
     dd = new hx.Dropdown(id, content, {ddClass: 'bob'})
     dd.show()
-    expect(dd._.dropdown.classed('bob')).toEqual(true)
+    dd._.dropdown.classed('bob').should.equal(true)

   it 'should show and hide correctly', ->
     dd = new hx.Dropdown(id, content)

-    expect(dd._.visible).toEqual(false)
-    expect(hx.select('.hx-dropdown').empty()).toEqual(true)
-
-    dd.show()
-    expect(dd._.visible).toEqual(true)
-    expect(hx.select('.hx-dropdown').empty()).toEqual(false)
+    dd._.visible.should.equal(false)
+    hx.select('.hx-dropdown').empty().should.equal(true)
+
+    dd.show()
+    dd._.visible.should.equal(true)
+    hx.select('.hx-dropdown').empty().should.equal(false)

     dd.hide()
-    expect(dd._.visible).toEqual(false)
-    expect(hx.select('.hx-dropdown').empty()).toEqual(true)
-
-  it 'should set the overflow style when the useScroll option is specified', ->
-    dd = new hx.Dropdown(id, content)
-    dd.useScroll = true
-    dd.show()
-    jasmine.clock().tick(300)
-    expect(dd._.dropdown.style('overflow-y')).toEqual('auto')
+    dd._.visible.should.equal(false)
+    hx.select('.hx-dropdown').empty().should.equal(true)
+
+  it 'should set the overflow style when the useScroll (private) option is specified', ->
+    dd = new hx.Dropdown(id, content)
+    dd._.useScroll = true
+    dd.show()
+    clock.tick(300)
+    dd._.dropdown.style('overflow-y').should.equal('auto')

   it 'shouldnt try to match the width of the parent if matchWidth is false', ->
     button.text('Wider button for testing')
@@ -304,8 +304,8 @@
     dd = new hx.Dropdown(id, content, {matchWidth: false })

     dd.show()
-    expect(dd._.dropdown.style('min-width')).toEqual('0px')
-    jasmine.clock().tick(301)
+    dd._.dropdown.style('min-width').should.equal('0px')
+    clock.tick(301)

   it 'should try to match the width of the parent if matchWidth is true', ->
     button.text('Wider button for testing')
@@ -313,8 +313,8 @@
     dd = new hx.Dropdown(id, content, {matchWidt: true })

     dd.show()
-    expect(dd._.dropdown.style('min-width')).toEqual(button.style('width'))
-    jasmine.clock().tick(301)
+    dd._.dropdown.style('min-width').should.equal(button.style('width'))
+    clock.tick(301)

   # it 'should detect the maxHeight properly', ->
   #   hx.select('head').append('style').attr('id','style').attr('type', 'text/css').text("""
@@ -329,12 +329,12 @@
   #   dd.show()
   #   setTimeout ->
   #     ddBox = roundAll dd._.dropdown.box()
-  #     expect(dd._.dropdown.style('max-height')).toEqual('5px')
-  #     expect(ddBox.left).toEqual(buttonBox.left)
-  #     expect(ddBox.height).toEqual(5)
+  #     dd._.dropdown.style('max-height').should.equal('5px')
+  #     ddBox.left.should.equal(buttonBox.left)
+  #     ddBox.height.should.equal(5)
   #     hx.select('#style').remove()
   #   , 300
-  #   jasmine.clock().tick(301)
+  #   clock.tick(301)

   it 'should shift the dropdown down if shifting it up has moved it off the top of the screen', ->
     dd = new hx.Dropdown(id, content, {align: 'up'})
@@ -345,10 +345,10 @@
     dd.show()
     setTimeout ->
       ddBox = roundAll dd._.dropdown.box()
-      expect(ddBox.top).toEqual(buttonBox.top + buttonBox.height + getSpacing(dd))
-      expect(ddBox.left).toEqual(buttonBox.left)
+      ddBox.top.should.equal(buttonBox.top + buttonBox.height + getSpacing(dd))
+      ddBox.left.should.equal(buttonBox.left)
     , 300
-    jasmine.clock().tick(301)
+    clock.tick(301)

   describe 'align', ->
@@ -364,68 +364,68 @@
       tests = [
           align: null
           check: ->
-            expect(ddBox.left).toEqual(buttonBox.left)
-            expect(ddBox.top).toEqual(buttonBox.bottom + getSpacing(dd))
+            ddBox.left.should.equal(buttonBox.left)
+            ddBox.top.should.equal(buttonBox.bottom + getSpacing(dd))
         ,
           align: 'up'
           check: ->
-            expect(ddBox.left).toEqual(buttonBox.left)
-            expect(ddBox.top).toEqual(buttonBox.top - ddBox.height - getSpacing(dd))
+            ddBox.left.should.equal(buttonBox.left)
+            ddBox.top.should.equal(buttonBox.top - ddBox.height - getSpacing(dd))
         ,
           align: 'down'
           check: ->
-            expect(ddBox.left).toEqual(buttonBox.left)
-            expect(ddBox.top).toEqual(buttonBox.bottom + getSpacing(dd))
+            ddBox.left.should.equal(buttonBox.left)
+            ddBox.top.should.equal(buttonBox.bottom + getSpacing(dd))
         ,
           align: 'left'
           check: ->
-            expect(ddBox.right).toEqual(buttonBox.left - getSpacing(dd))
-            expect(ddBox.top).toEqual(buttonBox.top)
+            ddBox.right.should.equal(buttonBox.left - getSpacing(dd))
+            ddBox.top.should.equal(buttonBox.top)
         ,
           align: 'right'
           check: ->
-            expect(ddBox.left).toEqual(buttonBox.right + getSpacing(dd))
-            expect(ddBox.top).toEqual(buttonBox.top)
+            ddBox.left.should.equal(buttonBox.right + getSpacing(dd))
+            ddBox.top.should.equal(buttonBox.top)
         ,
           align: 'lbrb'
           check: ->
-            expect(ddBox.right).toEqual(buttonBox.left - getSpacing(dd))
-            expect(ddBox.bottom).toEqual(buttonBox.bottom)
+            ddBox.right.should.equal(buttonBox.left - getSpacing(dd))
+            ddBox.bottom.should.equal(buttonBox.bottom)
         ,
           align: 'lbrt'
           check: ->
-            expect(ddBox.right).toEqual(buttonBox.left - getSpacing(dd))
-            expect(ddBox.top).toEqual(buttonBox.top + buttonBox.height + getSpacing(dd))
+            ddBox.right.should.equal(buttonBox.left - getSpacing(dd))
+            ddBox.top.should.equal(buttonBox.top + buttonBox.height + getSpacing(dd))
         ,
           align: 'ltrb'
           check: ->
-            expect(ddBox.right).toEqual(buttonBox.left - getSpacing(dd))
-            expect(ddBox.bottom).toEqual(buttonBox.top - getSpacing(dd))
+            ddBox.right.should.equal(buttonBox.left - getSpacing(dd))
+            ddBox.bottom.should.equal(buttonBox.top - getSpacing(dd))
         ,
           align: 'rblt'
           check: ->
-            expect(ddBox.left).toEqual(buttonBox.right + getSpacing(dd))
-            expect(ddBox.top).toEqual(buttonBox.bottom + getSpacing(dd))
+            ddBox.left.should.equal(buttonBox.right + getSpacing(dd))
+            ddBox.top.should.equal(buttonBox.bottom + getSpacing(dd))
         ,
           align: 'rblb'
           check: ->
-            expect(ddBox.left).toEqual(buttonBox.right + getSpacing(dd))
-            expect(ddBox.bottom).toEqual(buttonBox.bottom)
+            ddBox.left.should.equal(buttonBox.right + getSpacing(dd))
+            ddBox.bottom.should.equal(buttonBox.bottom)
         ,
           align: 'rbrt'
           check: ->
-            expect(ddBox.right).toEqual(buttonBox.right)
-            expect(ddBox.top).toEqual(buttonBox.bottom + getSpacing(dd))
+            ddBox.right.should.equal(buttonBox.right)
+            ddBox.top.should.equal(buttonBox.bottom + getSpacing(dd))
         ,
           align: 'rtlb'
           check: ->
-            expect(ddBox.left).toEqual(buttonBox.right + getSpacing(dd))
-            expect(ddBox.top).toEqual(buttonBox.top - ddBox.height - getSpacing(dd))
+            ddBox.left.should.equal(buttonBox.right + getSpacing(dd))
+            ddBox.top.should.equal(buttonBox.top - ddBox.height - getSpacing(dd))
         ,
           align: 'rtrb'
           check: ->
-            expect(ddBox.right).toEqual(buttonBox.right)
-            expect(ddBox.bottom).toEqual(buttonBox.top - getSpacing(dd))
+            ddBox.right.should.equal(buttonBox.right)
+            ddBox.bottom.should.equal(buttonBox.top - getSpacing(dd))
       ]

       t = (test, index) ->
@@ -438,7 +438,7 @@
             ddBox = roundAll dd._.dropdown.box()
             test.check()
           , 300
-          jasmine.clock().tick(301)
+          clock.tick(301)

         if index < tests.length
           t(tests[index], index + 1)
@@ -453,19 +453,19 @@
       tests = [
         pos: 'left'
         check: ->
-          expect(ddBox.left).toEqual(0)
+          ddBox.left.should.equal(0)
       ,
         pos: 'top'
         check: ->
-          expect(ddBox.top).toEqual(0)
+          ddBox.top.should.equal(0)
       ,
         pos: 'right'
         check: ->
-          expect(ddBox.left).toEqual(window.innerWidth - ddBox.width - scrollbarWidth)
+          ddBox.left.should.equal(window.innerWidth - ddBox.width - scrollbarWidth)
       ,
         pos: 'bottom'
         check: ->
-          expect(ddBox.top).toEqual(window.innerHeight - ddBox.height)
+          ddBox.top.should.equal(window.innerHeight - ddBox.height)
       ]

       t = (test, index) ->
@@ -485,7 +485,7 @@
             ddBox = roundAll dd._.dropdown.box()
             test.check()
           , 300
-          jasmine.clock().tick(301)
+          clock.tick(301)

         if index < tests.length
           t(tests[index], index + 1)
@@ -496,28 +496,28 @@
       tests = [
         align: 'rbrb'
         check: ->
-          expect(ddBox.top).toEqual(buttonBox.bottom + getSpacing(dd))
-          expect(ddBox.right).toEqual(buttonBox.right)
+          ddBox.top.should.equal(buttonBox.bottom + getSpacing(dd))
+          ddBox.right.should.equal(buttonBox.right)
       ,
         align: 'lblb'
         check: ->
-          expect(ddBox.top).toEqual(buttonBox.bottom + getSpacing(dd))
-          expect(ddBox.left).toEqual(buttonBox.left)
+          ddBox.top.should.equal(buttonBox.bottom + getSpacing(dd))
+          ddBox.left.should.equal(buttonBox.left)
       ,
         align: 'rtrt'
         check: ->
-          expect(ddBox.bottom).toEqual(buttonBox.top - getSpacing(dd))
-          expect(ddBox.right).toEqual(buttonBox.right)
+          ddBox.bottom.should.equal(buttonBox.top - getSpacing(dd))
+          ddBox.right.should.equal(buttonBox.right)
       ,
         align: 'ltlt'
         check: ->
-          expect(ddBox.bottom).toEqual(buttonBox.top - getSpacing(dd))
-          expect(ddBox.left).toEqual(buttonBox.left)
+          ddBox.bottom.should.equal(buttonBox.top - getSpacing(dd))
+          ddBox.left.should.equal(buttonBox.left)
       ,
         align: 'cover'
         check: ->
-          expect(ddBox.top).toEqual(buttonBox.bottom + getSpacing(dd))
-          expect(ddBox.left).toEqual(buttonBox.left)
+          ddBox.top.should.equal(buttonBox.bottom + getSpacing(dd))
+          ddBox.left.should.equal(buttonBox.left)
       ]

       t = (test, index) ->
@@ -531,7 +531,7 @@
             ddBox = roundAll dd._.dropdown.box()
             test.check()
           , 300
-          jasmine.clock().tick(301)
+          clock.tick(301)

         if index < tests.length
           t(tests[index], index + 1)


split auth function to allow middlewares_connectauth_chain

--- src/presentationLayer/auth/index.coffee
+++ src/presentationLayer/auth/index.coffee
@@ -7,8 +7,28 @@
 	init: () ->
 		return
 	registerWs: () ->
+		if not env.middlewares.connectauth
+			env.middlewares.connectauth = {}
+		if not env.middlewares.connectauth.all
+			env.middlewares.connectauth.all = []
+		# Chain of middlewares that are applied to each Auth endpoints
+		createMiddlewareChain = () ->
+			(req, res, next) ->
+				chain = []
+				i = 0
+				for k, middleware of env.middlewares.connectauth.all
+					do (middleware) ->
+						chain.push (callback) ->
+							middleware req, res, callback
+				if chain.length == 0
+					return next()
+				async.waterfall chain, () ->
+					next()
+
+		middlewares_connectauth_chain = createMiddlewareChain()
+
 		# oauth: refresh token
-		env.server.post env.config.base + '/auth/refresh_token/:provider', (req, res, next) ->
+		env.server.post env.config.base + '/auth/refresh_token/:provider', middlewares_connectauth_chain, (req, res, next) ->
 			e = new env.utilities.check.Error
 			e.check req.body, key: env.utilities.check.format.key, secret: env.utilities.check.format.key, token:'string'
 			e.check req.params, provider:'string'
@@ -28,7 +48,7 @@
 						oa.refresh req.body.token, keyset, env.send(res,next)

 		# iframe injection for IE
-		env.server.get env.config.base + '/auth/iframe', (req, res, next) ->
+		env.server.get env.config.base + '/auth/iframe', middlewares_connectauth_chain, (req, res, next) ->
 			res.setHeader 'Content-Type', 'text/html'
 			res.setHeader 'p3p', 'CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"'
 			e = new env.utilities.check.Error
@@ -68,7 +88,7 @@
 			next()

 		# oauth: get access token from server
-		env.server.post env.config.base + '/auth/access_token', (req, res, next) ->
+		env.server.post env.config.base + '/auth/access_token', middlewares_connectauth_chain, (req, res, next) ->
 			e = new env.utilities.check.Error
 			e.check req.body, code: env.utilities.check.format.key, key: env.utilities.check.format.key, secret: env.utilities.check.format.key

@@ -135,9 +155,7 @@
 			next()

-
-		# oauth: handle callbacks
-		env.server.get env.config.base + '/auth', (req, res, next) ->
+		auth_middleware = (req, res, next) ->
 			res.setHeader 'Content-Type', 'text/html'
 			getState = (callback) ->
 				return callback null, req.params.state if req.params.state
@@ -154,48 +172,58 @@
 				env.data.states.get stateid, (err, state) ->
 					return next err if err
 					return next new env.utilities.check.Error 'state', 'invalid or expired' if not state
-					callback = clientCallback state:state.options.state, provider:state.provider, redirect_uri:state.redirect_uri, origin:state.origin, redirect_type:state.redirect_type, req, res, next
-					return callback new env.utilities.check.Error 'state', 'code already sent, please use /access_token' if state.step != "0"
-					async.parallel [
-							(cb) -> env.data.providers.getExtended state.provider, cb
-							(cb) -> env.data.apps.getKeyset state.key, state.provider, cb
-					], (err, r) =>
+					req.stateid = stateid
+					req.state = state
+					next()
+
+		# oauth: handle callbacks
+		env.server.get env.config.base + '/auth', auth_middleware, middlewares_connectauth_chain, (req, res, next) ->
+			stateid = req.stateid
+			state = req.state
+			delete req.stateid
+			delete req.state
+			callback = clientCallback state:state.options.state, provider:state.provider, redirect_uri:state.redirect_uri, origin:state.origin, redirect_type:state.redirect_type, req, res, next
+			return callback new env.utilities.check.Error 'state', 'code already sent, please use /access_token' if state.step != "0"
+			async.parallel [
+					(cb) -> env.data.providers.getExtended state.provider, cb
+					(cb) -> env.data.apps.getKeyset state.key, state.provider, cb
+			], (err, r) =>
+				return callback err if err
+				provider = r[0]
+				parameters = r[1].parameters
+				response_type = r[1].response_type
+				oa = new env.utilities.oauth[state.oauthv](provider, parameters)
+				oa.access_token state, req, (e, r) ->
+					status = if e then 'error' else 'success'
+					env.callhook 'connect.auth', req, res, (err) ->
 						return callback err if err
-						provider = r[0]
-						parameters = r[1].parameters
-						response_type = r[1].response_type
-						oa = new env.utilities.oauth[state.oauthv](provider, parameters)
-						oa.access_token state, req, (e, r) ->
-							status = if e then 'error' else 'success'
-							env.callhook 'connect.auth', req, res, (err) ->
-								return callback err if err
-								env.events.emit 'connect.callback', req:req, origin:state.origin, key:state.key, provider:state.provider, parameters:state.options?.parameters, status:status
-								return callback e if e
-
-								env.callhook 'connect.backend', results:r, key:state.key, provider:state.provider, status:status, (e) ->
-									return callback e if e
-
-
-									# If not client side mode, store the tokens
-									if state.options.client_type != 'client'
-										env.data.states.set stateid, token:JSON.stringify(r), step:1, (->)
-
-									# Always delete refresh token from front-end response
-									delete r.refresh_token
-
-									# If server_side only, remove everything and put the code
-									if response_type == 'code'
-										r = {}
-
-									# If a state was given by client, give him only the code
-									if state.options.client_type != 'client'
-										r.code = stateid
-
-									# Remove the state from db for client_side mode
-									if state.options.client_type == 'client'
-										env.data.states.del stateid, (->)
-
-									callback null, r, response_type
+						env.events.emit 'connect.callback', req:req, origin:state.origin, key:state.key, provider:state.provider, parameters:state.options?.parameters, status:status
+						return callback e if e
+
+						env.callhook 'connect.backend', results:r, key:state.key, provider:state.provider, status:status, (e) ->
+							return callback e if e
+
+
+							# If not client side mode, store the tokens
+							if state.options.client_type != 'client'
+								env.data.states.set stateid, token:JSON.stringify(r), step:1, (->)
+
+							# Always delete refresh token from front-end response
+							delete r.refresh_token
+
+							# If server_side only, remove everything and put the code
+							if response_type == 'code'
+								r = {}
+
+							# If a state was given by client, give him only the code
+							if state.options.client_type != 'client'
+								r.code = stateid
+
+							# Remove the state from db for client_side mode
+							if state.options.client_type == 'client'
+								env.data.states.del stateid, (->)
+
+							callback null, r, response_type

 		# oauth: popup or redirection to provider's authorization url
 		env.server.get env.config.base + '/auth/:provider', (req, res, next) ->


Use question marks for errors with pos not in source map

--- lib/hint.coffee
+++ lib/hint.coffee
@@ -51,8 +51,10 @@
       .compact()
       # Convert errors to use coffee source locations instead of js locations
       .map (error) ->
-        [line, col] = sourceMap.sourceLocation [error.line - 1, error.character - 1]
-        _.extend error, line: line + 1, character: col + 1
+        try [line, col] = sourceMap.sourceLocation [error.line - 1, error.character - 1]
+        _.extend error,
+          line: if line? then line + 1 else '?'
+          character: if col? then col + 1 else '?'
       # Get rid of errors that don't apply to coffee very well
       .filter (error) ->
         not _.any errorsToSkip, (to_skip) -> error.reason.indexOf(to_skip) >= 0


Ignore error about missing parens after constructor

This error is covered by coffeelint, and there's no option to configure it with
jshint, so we'll just ignore it.

--- lib/hint.coffee
+++ lib/hint.coffee
@@ -17,6 +17,7 @@
   "Confusing use of '!'."
   "Wrap the /regexp/ literal in parens to disambiguate the slash operator."
   "Creating global 'for' variable. Should be 'for (var"
+  "Missing '()' invoking a constructor." # covered by coffeelint rule non_empty_constructor_needs_parens
 ]

 # If log is true, prints out results after processing each file


removed two tap event

removed two tap event

--- src/quo.gestures.coffee
+++ src/quo.gestures.coffee
@@ -121,8 +121,6 @@
                     drag_direction = _swipeDirection(FIRST_TOUCH[0].x, CURRENT_TOUCH[0].x, FIRST_TOUCH[0].y, CURRENT_TOUCH[0].y)
                     _trigger "drag" + drag_direction
                     anyevent = true
-            if not anyevent
-                _trigger "twoTap"
             _cleanGesture()
         EVENT = undefined


Correct typo and improved language

Correct typo and improved language

--- lib/init.coffee
+++ lib/init.coffee
@@ -63,8 +63,8 @@
       type: 'string'
       default: 'external'
       enum: ['external', 'internal']
-      description: 'Select between internal Pylama (with Virtualenv detection
-      and other cool things or external stable Pylama (do not forget to
+      description: 'Switch between internal Pylama (with Virtualenv detection
+      and other cool things) or external stable Pylama (do not forget to
       specify executable path).'

   activate: ->


remove preview on MouseOver feature

remove preview on MouseOver feature

--- lib/init.coffee
+++ lib/init.coffee
@@ -96,35 +96,8 @@
         d.destroy()

-handleEntityMouseOver = (entity, e) ->
-        if !editor
-            return
-        target = e.target
-        ed = null
-        if target
-            line = target.getAttribute('data-line') || 0
-            col = ~~target.getAttribute('data-column') || 0
-            colEnd = ~~target.getAttribute('data-column-end') || col
-            lineEnd = target.getAttribute('data-line-end') || line
-        else if entity
-            line = entity.loc.start.line
-            col = entity.loc.start.column
-            colEnd = entity.loc.end.column
-            lineEnd = entity.loc.end.line
-
-        if line || target.getAttribute('data-path') #&& editor
-            fileToPreview = target.getAttribute('data-path') || currentFile
-            previewEditor = createTextEditor(fileToPreview, ~~line)
-            if entity # show in main editor
-                addLabelDecoration(editor, line, col, lineEnd, colEnd, decorations)
-            else
-                addLabelDecoration(previewEditor, line, col, lineEnd, colEnd, decorations)
-                edEl = previewEditor.element
-                wrapper = document.getElementById('lupa-editor-wrapper')
-                wrapper.innerHTML = ''
-                wrapper.appendChild(edEl)
-
-el.addEventListener('mouseover', handleEntityMouseOver.bind(this, null))
+
+

 currentFile = ''


Expect relative main path

Expect relative main path

--- src/packages/package-generator/spec/package-generator-spec.coffee
+++ src/packages/package-generator/spec/package-generator-spec.coffee
@@ -78,7 +78,7 @@
       packageGeneratorView.trigger "core:confirm"

       lines = fsUtils.read("#{packagePath}/package.cson").split("\n")
-      expect(lines[0]).toBe "'main': 'lib\/#{packageName}'"
+      expect(lines[0]).toBe "'main': './lib\/#{packageName}'"

       lines = fsUtils.read("#{packagePath}/lib/#{packageName}.coffee").split("\n")
       expect(lines[0]).toBe "SweetPackageDudeView = require './sweet-package-dude-view'"


Adjust test cases because deprecation of comments endpoint.

--- src/spec/integration/testhelpers.coffee
+++ src/spec/integration/testhelpers.coffee
@@ -31,7 +31,6 @@
     { type: { name: 'reference', referenceTypeId: 'shipping-method' }, name: "attr-ref-shipping-method-#{lowerConstraint}", label: { en: "Attribute REFERENCE-SHIPPING-METHOD #{constraint}" }, attributeConstraint: constraint, isRequired: false, isSearchable: false }
     { type: { name: 'reference', referenceTypeId: 'category' }, name: "attr-ref-category-#{lowerConstraint}", label: { en: "Attribute REFERENCE-CATEGORY #{constraint}" }, attributeConstraint: constraint, isRequired: false, isSearchable: false }
     { type: { name: 'reference', referenceTypeId: 'review' }, name: "attr-ref-review-#{lowerConstraint}", label: { en: "Attribute REFERENCE-REVIEW #{constraint}" }, attributeConstraint: constraint, isRequired: false, isSearchable: false }
-    { type: { name: 'reference', referenceTypeId: 'comment' }, name: "attr-ref-comment-#{lowerConstraint}", label: { en: "Attribute REFERENCE-COMMENT #{constraint}" }, attributeConstraint: constraint, isRequired: false, isSearchable: false }
     { type: { name: 'reference', referenceTypeId: 'key-value-document' }, name: "attr-ref-key-value-#{lowerConstraint}", label: { en: "Attribute REFERENCE-KEY-VALUE-DOCUMENT #{constraint}" }, attributeConstraint: constraint, isRequired: false, isSearchable: false }
     { type: { name: 'set', elementType: { name: 'text' } }, name: "attr-set-text-#{lowerConstraint}", label: { en: "Attribute SET-TEXT #{constraint}" }, attributeConstraint: constraint, isRequired: false, isSearchable: false, inputHint: 'SingleLine' }
     { type: { name: 'set', elementType: { name: 'ltext' } }, name: "attr-set-ltext-#{lowerConstraint}", label: { en: "Attribute SET-LTEXT #{constraint}" }, attributeConstraint: constraint, isRequired: false, isSearchable: false, inputHint: 'SingleLine' }
@@ -51,7 +50,6 @@
     { type: { name: 'set', elementType: { name: 'reference', referenceTypeId: 'shipping-method' } }, name: "attr-set-ref-shipping-method-#{lowerConstraint}", label: { en: "Attribute SET-REFERENCE-SHIPPING-METHOD #{constraint}" }, attributeConstraint: constraint, isRequired: false, isSearchable: false }
     { type: { name: 'set', elementType: { name: 'reference', referenceTypeId: 'category' } }, name: "attr-set-ref-category-#{lowerConstraint}", label: { en: "Attribute SET-REFERENCE-CATEGORY #{constraint}" }, attributeConstraint: constraint, isRequired: false, isSearchable: false }
     { type: { name: 'set', elementType: { name: 'reference', referenceTypeId: 'review' } }, name: "attr-set-ref-review-#{lowerConstraint}", label: { en: "Attribute SET-REFERENCE-REVIEW #{constraint}" }, attributeConstraint: constraint, isRequired: false, isSearchable: false }
-    { type: { name: 'set', elementType: { name: 'reference', referenceTypeId: 'comment' } }, name: "attr-set-ref-comment-#{lowerConstraint}", label: { en: "Attribute SET-REFERENCE-COMMENT #{constraint}" }, attributeConstraint: constraint, isRequired: false, isSearchable: false }
     { type: { name: 'set', elementType: { name: 'reference', referenceTypeId: 'key-value-document' } }, name: "attr-set-ref-key-value-#{lowerConstraint}", label: { en: "Attribute SET-REFERENCE-KEY-VALUE-DOCUMENT #{constraint}" }, attributeConstraint: constraint, isRequired: false, isSearchable: false }
   ]


Cleaned up the JIRA script to remove the functions we are not going to support

--- src/scripts/jira.coffee
+++ src/scripts/jira.coffee
@@ -18,15 +18,10 @@
 #   <Project Key>-<Issue ID> - Displays information about the JIRA ticket (if it exists)
 #   hubot show watchers for <Issue Key> - Shows watchers for the given JIRA issue
 #   hubot show comments for <Issue Key> - Shows the comments for the given JIRA issue
-
 #   hubot show open issues for <Issue Key> - Shows the open issues for the given JQL
 #   e.g. hubot show open issues for project = "The Cornered Badgers" AND fixVersion = "13.21"
-
 #   hubot search for <JQL> - Search JIRA with JQL
-#   hubot save filter <JQL> as <name> - Save JIRA JQL query as filter in the brain
-#   hubot use filter <name> - Use a JIRA filter from the brain
-#   hubot show filter(s) - Show all JIRA filters
-#   hubot show filter <name> - Show a specific JIRA filter
+#   e.g. hubot search for project = "The Cornered Badgers" AND component = "Consumer Web"
 #
 # Author:
 #   codec
@@ -232,39 +227,4 @@
           msg.send text
         recentissues.add msg.message.user.room+ticket

-  robot.respond /save filter (.*) as (.*)/i, (msg) ->
-    filter = filters.get msg.match[2]
-
-    if filter
-      filters.delete filter.name
-      msg.reply "Updated filter #{filter.name} for you"
-
-    filter = new IssueFilter msg.match[2], msg.match[1]
-    filters.add filter
-
-  robot.respond /delete filter (.*)/i, (msg) ->
-    filters.delete msg.match[1]
-
-  robot.respond /(use )?filter (.*)/i, (msg) ->
-    name    = msg.match[2]
-    filter  = filters.get name
-
-    if not filter
-      msg.reply "Sorry, could not find filter #{name}"
-      return
-
-    search msg, filter.jql, (text) ->
-      msg.reply text
-
-  robot.respond /(show )?filter(s)? ?(.*)?/i, (msg) ->
-    if filters.all().length == 0
-      msg.reply "Sorry, I don't remember any filters."
-      return
-
-    if msg.match[3] == undefined
-      msg.reply "I remember #{filters.all().length} filters"
-      filters.all().forEach (filter) ->
-        msg.reply "#{filter.name}: #{filter.jql}"
-    else
-      filter = filters.get msg.match[3]
-      msg.reply "#{filter.name}: #{filter.jql}"
+


remove _ argument from addOne func. Not sure why it was there

--- src/scripts/main.coffee
+++ src/scripts/main.coffee
@@ -125,6 +125,7 @@
   SUBVIEWS: []

   events:
+    'click .js-undo-delete': 'undoDelete'
     'click .js-save-form': 'saveForm'
     'click .fb-tabs a': 'showTab'
     'click .fb-add-field-types a': 'addField'
@@ -156,6 +157,7 @@
       _.pathAssign(newSubmit, Formbuilder.options.mappings.DESCRIPTION, 'Submit')
       @collection.push(newSubmit)
     @initAutosave()
+    @initUndoStack()

   initAutosave: ->
     @formSaved = true
@@ -169,6 +171,10 @@
     if Formbuilder.options.WARN_IF_UNSAVED
       $(window).bind 'beforeunload', =>
         if @formSaved then undefined else Formbuilder.options.dict.UNSAVED_CHANGES
+
+  initUndoStack: ->
+    @undoDeleteButton = @$el.find('.js-undo-delete')
+    @undoDeleteButton.attr('disabled', true).text(Formbuilder.options.dict.NOTHING_TO_UNDO)

   reset: ->
     @$responseFields.html('')
@@ -209,7 +215,7 @@
     if target == '#editField' && !@editView && (first_model = @collection.models[0])
       @createAndShowEditView(first_model)

-  addOne: (responseField, _, options) ->
+  addOne: (responseField, options) ->
     view = new ViewFieldView
       model: responseField
       parentView: @
@@ -366,6 +372,8 @@

         @updatingBatch = undefined

+  undoDelete: (e) ->
+    #move the old form element back to where it should go

 class Formbuilder
   @helpers:
@@ -415,6 +423,8 @@
       ALL_CHANGES_SAVED: 'All changes saved'
       SAVE_FORM: 'Save form'
       UNSAVED_CHANGES: 'You have unsaved changes. If you leave this page, you will lose those changes!'
+      NOTHING_TO_UNDO: 'Nothing to undo'
+      UNDO_DELETE: 'Undo deletion of #{lastElName}'

   @fields: {}
   @inputFields: {}


Remove tests for App-baseclass

Remove tests for App-baseclass

--- src/tests/spec/public/app._.spec.coffee
+++ src/tests/spec/public/app._.spec.coffee
@@ -1,3 +0,0 @@
-# describe 'app._.spec.coffee', ->
-# 	it 'should run', ->
-# 		expect(2).toEqual(2)


troubleshooting scope on subject request

--- app/assets/javascripts/lib/fetch-subjects-mixin.cjsx
+++ app/assets/javascripts/lib/fetch-subjects-mixin.cjsx
@@ -110,6 +110,7 @@
         workflow_id: workflow_id
         limit: limit
         random: true
+        scope: "active"

       request.then (subjects) =>
         subject = @orderSubjectsByY(subjects)


OX-601 use plane.hasChanged, undo forcing rerendering

--- app/assets/javascripts/oxalis/view/plane_view.coffee
+++ app/assets/javascripts/oxalis/view/plane_view.coffee
@@ -81,9 +81,6 @@
     # Dont forget to handle window resizing!
     $(window).resize( => @.resize() )

-    # regularily force update
-    setInterval(( => @renderFunction(true) ), 500)
-
     # refresh the scene once a bucket is loaded
     # FIXME: probably not the most elgant thing to do
     # FIXME: notifies all planes when any bucket is loaded
@@ -99,7 +96,7 @@

   # This is the main render function.
   # All 3D meshes and the trianglesplane are rendered here.
-  renderFunction : ( force = false ) ->
+  renderFunction : ->

     TWEEN.update()

@@ -108,7 +105,11 @@
     # working and keeps your lap cool
     # ATTENTION: this limits the FPS to 30 FPS (depending on the keypress update frequence)

-    if @flycam.hasChanged or @flycam.hasNewTextures() or force
+    modelChanged = false
+    for plane in @model.binary.planes
+      modelChanged |= plane.hasChanged()
+
+    if @flycam.hasChanged or @flycam.hasNewTextures() or modelChanged

       @trigger "render"


Prevent creating autocomplete dropdown is user is not logged in

--- app/assets/javascripts/search_autocomplete.js.coffee
+++ app/assets/javascripts/search_autocomplete.js.coffee
@@ -32,7 +32,8 @@

     @saveOriginalState()

-    @createAutocomplete()
+    # Only when user is logged in
+    @createAutocomplete() if gon.current_user_id

     @searchInput.addClass('disabled')

@@ -123,6 +124,9 @@
     @clearInput.on 'click', @onRemoveLocationClick

   enableAutocomplete: ->
+    # No need to enable anything if user is not logged in
+    return if !gon.current_user_id
+
     dropdownMenu = @dropdown.find('.dropdown-menu')
     _this = @
     @loadingSuggestions = false


:art: Update from PR feedback

:art: Update from PR feedback

--- lib/main.coffee
+++ lib/main.coffee
@@ -2,10 +2,9 @@
 {find} = helpers = require 'atom-linter'
 path = require 'path'
 globule = require 'globule'
-ChildProcess = require 'child_process'
+{spawnSync} = require 'child_process'
 {getPath} = require 'consistent-path'
-env = Object.assign({}, process.env)
-
+env = Object.assign({}, process.env, {PATH: getPath()})
 prefixPath = null

 module.exports =
@@ -65,11 +64,7 @@
     if @globalPath is '' and prefixPath is null
       npmCommand = if process.platform is 'win32' then 'npm.cmd' else 'npm'
       try
-        # Atom on OSX doesn't inherit a users PATH unless opened from the command line
-        if process.platform isnt 'win32'
-        then env = Object.assign({}, process.env, {PATH: getPath()})
-
-        prefixPath = ChildProcess.spawnSync(npmCommand, [
+        prefixPath = spawnSync(npmCommand, [
           'get'
           'prefix'
         ], {env}).output[1].toString().trim()
@@ -98,17 +93,18 @@
           if error.message is 'prefix' then atom.notifications.addError """
             **Error getting $PATH - linter-sass-lint**\n

-            You've enabled using global sass-lint without specifying a prefix so we tried.
-            Unfortunately we were unable to execute `npm get prefix` for you\n
-            Please make sure Atom is getting $PATH correctly or set it directly in linter-sass-lint settings
+            You've enabled using global sass-lint without specifying a prefix so we tried to.
+            Unfortunately we were unable to execute `npm get prefix` for you..\n
+            Please make sure Atom is getting $PATH correctly or set it directly in the `linter-sass-lint` settings.
           """, {dismissable: true}
           return []

           atom.notifications.addWarning """
             **Sass-lint package missing**

-            The sass-lint package cannot be found, please check sass-lint package path option of this package. \n
-            If you leave this option empty the sass-lint package included with linter-sass-lint will be used.
+            The sass-lint package cannot be found, please check sass-lint is installed globally. \n
+            You can always use the sass-lint pacakage included with linter-sass-lint by disabling the
+            `Use global sass-lint installation` option
           """, {dismissable: true}
           return []


:new: Consume the linter-ui service

:new: Consume the linter-ui service

--- lib/main.coffee
+++ lib/main.coffee
@@ -144,6 +144,16 @@
       for linter in linters
         @instance.deleteLinter(linter)

+  consumeUI: (UIs) ->
+    unless UIs instanceof Array
+      UIs = [ UIs ]
+    for UI in UIs
+      @instance.addUI(UI)
+
+    new Disposable =>
+      for UI in UIs
+        @instance.deleteUI(UI)
+
   consumeStatusBar: (statusBar) ->
     @instance.views.attachBottom(statusBar)


page reload

page reload

--- lib/main.coffee
+++ lib/main.coffee
@@ -30,6 +30,7 @@
       lang = event.currentTarget.id
       document.cookie = "lang=" + lang + ";expires=Sat, 01 Jan 2050 00:00:00 GMT"
       T9n.language = lang
+      window.location.reload()

   lang = getCookie("lang")
   unless lang


Add view-installed-{packages|themes} commands

--- lib/main.coffee
+++ lib/main.coffee
@@ -34,8 +34,9 @@
       'settings-view:show-keybindings': -> atom.workspace.open("#{configUri}/keybindings")
       'settings-view:change-themes': -> atom.workspace.open("#{configUri}/themes")
       'settings-view:install-packages-and-themes': -> atom.workspace.open("#{configUri}/install")
+      'settings-view:view-installed-themes': -> atom.workspace.open("#{configUri}/themes")
       'settings-view:uninstall-themes': -> atom.workspace.open("#{configUri}/themes")
-      'settings-view:view-packages': -> atom.workspace.open("#{configUri}/packages")
+      'settings-view:view-installed-packages': -> atom.workspace.open("#{configUri}/packages")
       'settings-view:uninstall-packages': -> atom.workspace.open("#{configUri}/packages")
       'settings-view:check-for-package-updates': -> atom.workspace.open("#{configUri}/updates")

@@ -76,7 +77,7 @@
       buttons: [{
         text: 'View Deprecated Packages',
         onDidClick: ->
-          atom.commands.dispatch(atom.views.getView(atom.workspace), 'settings-view:view-packages')
+          atom.commands.dispatch(atom.views.getView(atom.workspace), 'settings-view:view-installed-packages')
           notification.dismiss()
       }]
     localStorage.setItem('hasSeenDeprecatedNotification', true)


set objective command

set objective command

--- lib/main.coffee
+++ lib/main.coffee
@@ -31,8 +31,11 @@
       # Providers
       'narrow:lines': => @narrow('lines')
       'narrow:fold': => @narrow('fold')
+
       'narrow:symbols': => @narrow('symbols')
       'narrow:symbols-by-current-word': => @narrow('symbols', query: @getCurrentWord())
+      'narrow:project-symbols': => @narrow('project-symbols')
+      'narrow:project-symbols-by-current-word': => @narrow('project-symbols', query: @getCurrentWord())

       'narrow:git-diff': => @narrow('git-diff')
       'narrow:git-diff-all': => @narrow('git-diff-all')


Correct send selection command

Correct send selection command

--- lib/psci.coffee
+++ lib/psci.coffee
@@ -77,7 +77,7 @@

   sendSelectionCommand: =>
     editor = atom.workspace.getActiveTextEditor()
-    @sendLineCommand editor.getSelectedText()
+    @sendText editor.getSelectedText()
     editor.moveDown 1
     editor.moveToBeginningOfLine()


Fixing cleanup code

Fixing cleanup code

--- test/build.test.coffee
+++ test/build.test.coffee
@@ -22,9 +22,9 @@
     catch e

   afterEach (done)->
-#    if(existsSync(tempTestDirectory))
-#      wrench.rmdirRecursive(tempTestDirectory, done)
-#    else
+    if(existsSync(tempTestDirectory))
+      wrench.rmdirRecursive(tempTestDirectory, done)
+    else
       done()


make point and proposal request in parallel

Former-commit-id: 113c324f655b23dfbd96ff3acbb99f6b1f61c0e0 [formerly 3903c7f30a1e891a04a9c40395861a752a745dc7] [formerly a374b0467112a3ee19508938cd84a1a20e121f6b [formerly a374b0467112a3ee19508938cd84a1a20e121f6b [formerly d48ea7ceb75bbd0d77ff7d8c604e62d2906547cc]]]
Former-commit-id: ca999ed4c75d2139f1c2562facce3c1253481b99 [formerly 1aaef8ba51701f3ea751ab7338e3abeb1084ad95]
Former-commit-id: 8c26583cb79aa37ace60e8fad8b9ee8a5fa55800
Former-commit-id: b3ff0f1d046618d9d9af52495644be115d9d8c14

--- app/assets/javascripts/backbone/apps/franklin/franklin_app.coffee
+++ app/assets/javascripts/backbone/apps/franklin/franklin_app.coffee
@@ -84,10 +84,12 @@

     PointDetails: (long_id, point_id) ->
       proposal = App.request 'proposal:get', long_id, true
+      point = App.request 'point:get', parseInt(point_id), true
+
       region = App.request "default:region"
       # @_loading [proposal]

-      App.execute 'when:fetched', proposal, =>
+      App.execute 'when:fetched', [proposal, point], =>
         if !(region.currentView instanceof Franklin.Proposal.PositionLayout ||
              region.currentView instanceof Franklin.Proposal.AggregateLayout)
           @franklin_controller.close() if @franklin_controller
@@ -98,21 +100,17 @@
           region.controlled_by = @franklin_controller

-        point = App.request 'point:get', parseInt(point_id), true
-        # @_loading [point]
+        @franklin_controller.trigger 'point:show_details', point

-        App.execute 'when:fetched', point, =>
-          @franklin_controller.trigger 'point:show_details', point
+        crumbs = [
+          ['homepage', '/'],
+          ["#{proposal.long_id}", Routes.new_position_proposal_path(long_id)]
+          ["#{ if point.isPro() then 'Pro' else 'Con'} point", Routes.proposal_point_path(long_id, point_id)] ]

-          crumbs = [
-            ['homepage', '/'],
-            ["#{proposal.long_id}", Routes.new_position_proposal_path(long_id)]
-            ["#{ if point.isPro() then 'Pro' else 'Con'} point", Routes.proposal_point_path(long_id, point_id)] ]
+        if region.currentView instanceof Franklin.Proposal.AggregateLayout
+          crumbs.splice crumbs.length - 1, 0, ['results', Routes.proposal_path(long_id)]

-          if region.currentView instanceof Franklin.Proposal.AggregateLayout
-            crumbs.splice crumbs.length - 1, 0, ['results', Routes.proposal_path(long_id)]
-
-          App.vent.trigger 'route:completed', crumbs
+        App.vent.trigger 'route:completed', crumbs

     StaticPosition: (long_id, user_id) ->
       proposal = App.request 'proposal:get', long_id, true


Fixed gsub, should be replace

Fixed gsub, should be replace

--- app/assets/javascripts/sprangular/interceptors/railsAssets.coffee
+++ app/assets/javascripts/sprangular/interceptors/railsAssets.coffee
@@ -4,7 +4,7 @@

   $provide.factory 'railsAssetsInterceptor', ->
     request: (config) ->
-      url = config.url.gsub(/^\/assets\//, '')
+      url = config.url.replace(/^\/assets\//, '')

       if assetUrl = Env.templates[url]
         config.url = assetUrl


Try to fix path capitalization handling on Windows

--- lib/util.coffee
+++ lib/util.coffee
@@ -1,4 +1,5 @@
 {Range, Point, Directory} = require 'atom'
+{delimiter} = require 'path'

 module.exports = Util =
   debug: (message) ->
@@ -11,14 +12,14 @@
     dir ? atom.project.getDirectories()[0] ? new Directory

   getProcessOptions: (rootPath) ->
-    sep = if process.platform=='win32' then ';' else ':'
     env = {}
     for k,v of process.env
       env[k] = v
     apd = atom.config.get('haskell-ghc-mod.additionalPathDirectories')
-    apd.unshift "#{rootPath}/.cabal-sandbox/bin"
+          .concat process.env.PATH.split delimiter
     if rootPath
-      env.PATH = "#{apd.join(sep)}#{sep}#{env.PATH}"
+      apd.unshift "#{rootPath}/.cabal-sandbox/bin"
+    env.PATH = "#{apd.join(delimiter)}"
     Util.debug "PATH = #{env.PATH}"
     options =
       cwd: rootPath


Lowercase slugified names

Lowercase slugified names

--- lib/util.coffee
+++ lib/util.coffee
@@ -29,7 +29,7 @@
       callback err.message

   slugify: (name) ->
-    return slugify name
+    return slugify name, {lower: true}

   capitalize: (name) ->
     return capitalize.words name


Refactor lockstep-cron

Refactor lockstep-cron

--- scripts/lockstep-cron.coffee
+++ scripts/lockstep-cron.coffee
@@ -1,56 +1,24 @@
 CronJob = require('cron').CronJob

 module.exports = (robot) ->
+  # General Cron Tasks
   jobList = [
     { time: '30 10 * * 1-5', msg: '@channel Tea Time!' }
-    { time: '00 17 * * 5', msg: '@channel Beer & Pokdeng Time!' }
+    { time: '00 18 * * 1-5', msg: '@here Hey, turn off air filter, NOW!!!' }
   ]

-  # for job in jobList
-  #   cronJob = new CronJob job.time, =>
-  #     robot.messageRoom '#general', job.msg
-  #   , null, false, 'Asia/Bangkok'
-  #
-  #   cronJob.start()
+  jobList.forEach (job) ->
+    cronJob = new CronJob job.time, =>
+      robot.messageRoom '#general', job.msg
+    , null, false, 'Asia/Bangkok'

-  cronJob = new CronJob '30 10 * * 1-5', =>
-    robot.messageRoom '#general', '@channel Tea Time!'
-  , null, false, 'Asia/Bangkok'
+    cronJob.start()

-  cronJob.start()
+  # Farang Days
+  farangs = ['mac', 'kirati_tripler', 'kantaphon_tripler', 'ekkapob', 'nut']
+  farangs.forEach (farang, index) ->
+    cronJob = new CronJob "00 11 * * #{index + 1}", =>
+      robot.messageRoom '#general', "@channel @#{farang} is Farang! I bet you anything he will not understand Thai anymore!"
+    , null, false, 'Asia/Bangkok'

-  cronJob = new CronJob '00 18 * * 1-5', =>
-    robot.messageRoom '#general', '@here Hey, turn off air filter, NOW!!!'
-  , null, false, 'Asia/Bangkok'
-
-  cronJob.start()
-
-  cronJob = new CronJob '00 11 * * 1', =>
-    robot.messageRoom '#general', '@channel @mac is Farang! I bet you anything he will not understand Thai anymore!'
-  , null, false, 'Asia/Bangkok'
-
-  cronJob.start()
-
-  cronJob = new CronJob '00 11 * * 2', =>
-    robot.messageRoom '#general', '@channel @kirati_tripler is Farang! I bet you anything he will not understand Thai anymore!'
-  , null, false, 'Asia/Bangkok'
-
-  cronJob.start()
-
-  cronJob = new CronJob '00 11 * * 3', =>
-    robot.messageRoom '#general', '@channel @kantaphon_tripler is Farang! I bet you anything he will not understand Thai anymore!'
-  , null, false, 'Asia/Bangkok'
-
-  cronJob.start()
-
-  cronJob = new CronJob '00 11 * * 4', =>
-    robot.messageRoom '#general', '@channel @ekkapob is Farang! I bet you anything he will not understand Thai anymore!'
-  , null, false, 'Asia/Bangkok'
-
-  cronJob.start()
-
-  cronJob = new CronJob '00 11 * * 5', =>
-    robot.messageRoom '#general', '@channel @nut is Farang! I bet you anything he will not understand Thai anymore!'
-  , null, false, 'Asia/Bangkok'
-
-  cronJob.start()
+    cronJob.start()