Просмотр исходного кода

Merge branch 'master' into pr-contact-form-suggestions

Paulo Reis 10 лет назад
Родитель
Сommit
72d498c6c8
21 измененных файлов с 5744 добавлено и 318 удалено
  1. 14 2
      services/web/app/coffee/Features/Authentication/AuthenticationManager.coffee
  2. 4 0
      services/web/app/coffee/Features/Email/EmailBuilder.coffee
  3. 5 5
      services/web/app/coffee/Features/Notifications/NotificationsHandler.coffee
  4. 236 34
      services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
  5. 0 88
      services/web/app/coffee/Features/Wiki/WikiController.coffee
  6. 0 8
      services/web/app/coffee/router.coffee
  7. 1 0
      services/web/app/views/project/editor.jade
  8. 7 3
      services/web/app/views/project/editor/pdf.jade
  9. 0 68
      services/web/app/views/wiki/page.jade
  10. 1 0
      services/web/config/settings.defaults.coffee
  11. 4602 0
      services/web/npm-shrinkwrap.json
  12. 1 0
      services/web/package.json
  13. 4 2
      services/web/public/coffee/ide/editor/directives/aceEditor.coffee
  14. 18 18
      services/web/public/coffee/ide/human-readable-logs/HumanReadableLogsRules.coffee
  15. 11 5
      services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
  16. 3 1
      services/web/public/coffee/ide/wordcount/controllers/WordCountModalController.coffee
  17. 7 0
      services/web/public/coffee/main/new-subscription.coffee
  18. 24 0
      services/web/public/stylesheets/app/editor/pdf.less
  19. 8 3
      services/web/public/stylesheets/app/wiki.less
  20. 32 1
      services/web/test/UnitTests/coffee/Authentication/AuthenticationManagerTests.coffee
  21. 766 80
      services/web/test/UnitTests/coffee/Subscription/RecurlyWrapperTests.coffee

+ 14 - 2
services/web/app/coffee/Features/Authentication/AuthenticationManager.coffee

@@ -1,8 +1,11 @@
+Settings = require "settings-sharelatex"
 User = require("../../models/User").User
 {db, ObjectId} = require("../../infrastructure/mongojs")
 crypto = require 'crypto'
 bcrypt = require 'bcrypt'
 
+BCRYPT_ROUNDS = Settings?.security?.bcryptRounds or 12
+
 module.exports = AuthenticationManager =
 	authenticate: (query, password, callback = (error, user) ->) ->
 		# Using Mongoose for legacy reasons here. The returned User instance
@@ -15,7 +18,9 @@ module.exports = AuthenticationManager =
 					bcrypt.compare password, user.hashedPassword, (error, match) ->
 						return callback(error) if error?
 						if match
-							callback null, user
+							AuthenticationManager.checkRounds user, user.hashedPassword, password, (err) ->
+								return callback(err) if err?
+								callback null, user
 						else
 							callback null, null
 				else
@@ -24,7 +29,7 @@ module.exports = AuthenticationManager =
 				callback null, null
 
 	setUserPassword: (user_id, password, callback = (error) ->) ->
-		bcrypt.genSalt 7, (error, salt) ->
+		bcrypt.genSalt BCRYPT_ROUNDS, (error, salt) ->
 			return callback(error) if error?
 			bcrypt.hash password, salt, (error, hash) ->
 				return callback(error) if error?
@@ -35,3 +40,10 @@ module.exports = AuthenticationManager =
 					$unset: password: true
 				}, callback)
 
+	checkRounds: (user, hashedPassword, password, callback = (error) ->) ->
+		# check current number of rounds and rehash if necessary
+		currentRounds = bcrypt.getRounds hashedPassword
+		if currentRounds < BCRYPT_ROUNDS
+			AuthenticationManager.setUserPassword user._id, password, callback
+		else
+			callback()

+ 4 - 0
services/web/app/coffee/Features/Email/EmailBuilder.coffee

@@ -3,6 +3,8 @@ PersonalEmailLayout = require("./Layouts/PersonalEmailLayout")
 NotificationEmailLayout = require("./Layouts/NotificationEmailLayout")
 settings = require("settings-sharelatex")
 
+
+
 templates = {}
 
 templates.registered =	
@@ -114,6 +116,8 @@ module.exports =
 		template = templates[templateName]
 		opts.siteUrl = settings.siteUrl
 		opts.body = template.compiledTemplate(opts)
+		if settings.email?.templates?.customFooter?
+			opts.body += settings.email?.templates?.customFooter
 		return {
 			subject : template.subject(opts)
 			html: template.layout(opts)

+ 5 - 5
services/web/app/coffee/Features/Notifications/NotificationsHandler.coffee

@@ -6,7 +6,7 @@ oneSecond = 1000
 
 makeRequest = (opts, callback)->
 	if !settings.apis.notifications?.url?
-		return callback()
+		return callback(null, statusCode:200)
 	else
 		request(opts, callback)
 
@@ -18,7 +18,7 @@ module.exports =
 			json: true
 			timeout: oneSecond
 			method: "GET"
-		request opts, (err, res, unreadNotifications)->
+		makeRequest opts, (err, res, unreadNotifications)->
 			statusCode =  if res? then res.statusCode else 500
 			if err? or statusCode != 200
 				e = new Error("something went wrong getting notifications, #{err}, #{statusCode}")
@@ -40,7 +40,7 @@ module.exports =
 				templateKey:templateKey
 			}
 		logger.log opts:opts, "creating notification for user"
-		request opts, callback
+		makeRequest opts, callback
 
 	markAsReadWithKey: (user_id, key, callback)->
 		opts = 
@@ -51,7 +51,7 @@ module.exports =
 				key:key
 			}
 		logger.log user_id:user_id, key:key, "sending mark notification as read with key to notifications api"
-		request opts, callback
+		makeRequest opts, callback
 	
 
 	markAsRead: (user_id, notification_id, callback)->
@@ -60,4 +60,4 @@ module.exports =
 			uri: "#{settings.apis.notifications?.url}/user/#{user_id}/notification/#{notification_id}"
 			timeout:oneSecond
 		logger.log user_id:user_id, notification_id:notification_id, "sending mark notification as read to notifications api"
-		request opts, callback
+		makeRequest opts, callback

+ 236 - 34
services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee

@@ -4,11 +4,201 @@ request = require 'request'
 Settings = require "settings-sharelatex"
 xml2js = require "xml2js"
 logger = require("logger-sharelatex")
+Async = require('async')
 
 module.exports = RecurlyWrapper =
 	apiUrl : "https://api.recurly.com/v2"
 
-	createSubscription: (user, subscriptionDetails, recurly_token_id, callback)->
+	_addressToXml: (address) ->
+		allowedKeys = ['address1', 'address2', 'city', 'country', 'state', 'zip', 'postal_code']
+		resultString = "<billing_info>\n"
+		for k, v of address
+			if k == 'postal_code'
+				k = 'zip'
+			if v and (k in allowedKeys)
+				resultString += "<#{k}#{if k == 'address2' then ' nil="nil"' else ''}>#{v || ''}</#{k}>\n"
+		resultString += "</billing_info>\n"
+		return resultString
+
+	_paypal:
+		checkAccountExists: (cache, next) ->
+			user = cache.user
+			recurly_token_id = cache.recurly_token_id
+			subscriptionDetails = cache.subscriptionDetails
+			logger.log {user_id: user._id, recurly_token_id}, "checking if recurly account exists for user"
+			RecurlyWrapper.apiRequest({
+				url:    "accounts/#{user._id}"
+				method: "GET"
+			}, (error, response, responseBody) ->
+				if error
+					if response.statusCode == 404  # actually not an error in this case, just no existing account
+						cache.userExists = false
+						return next(null, cache)
+					logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while checking account"
+					return next(error)
+				logger.log {user_id: user._id, recurly_token_id}, "user appears to exist in recurly"
+				RecurlyWrapper._parseAccountXml responseBody, (err, account) ->
+					if err
+						logger.error {err, user_id: user._id, recurly_token_id}, "error parsing account"
+						return next(err)
+					cache.userExists = true
+					cache.account = account
+					return next(null, cache)
+			)
+		createAccount: (cache, next) ->
+			user = cache.user
+			recurly_token_id = cache.recurly_token_id
+			subscriptionDetails = cache.subscriptionDetails
+			address = subscriptionDetails.address
+			if !address
+				return next(new Error('no address in subscriptionDetails at createAccount stage'))
+			if cache.userExists
+				logger.log {user_id: user._id, recurly_token_id}, "user already exists in recurly"
+				return next(null, cache)
+			logger.log {user_id: user._id, recurly_token_id}, "creating user in recurly"
+			requestBody = """
+			<account>
+				<account_code>#{user._id}</account_code>
+				<email>#{user.email}</email>
+				<first_name>#{user.first_name}</first_name>
+				<last_name>#{user.last_name}</last_name>
+				<address>
+					<address1>#{address.address1}</address1>
+					<address2>#{address.address2}</address2>
+					<city>#{address.city || ''}</city>
+					<state>#{address.state || ''}</state>
+					<zip>#{address.zip || ''}</zip>
+					<country>#{address.country}</country>
+				</address>
+			</account>
+			"""
+			RecurlyWrapper.apiRequest({
+				url    : "accounts"
+				method : "POST"
+				body   : requestBody
+			}, (error, response, responseBody) =>
+				if error
+					logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while creating account"
+					return next(error)
+				RecurlyWrapper._parseAccountXml responseBody, (err, account) ->
+					if err
+						logger.error {err, user_id: user._id, recurly_token_id}, "error creating account"
+						return next(err)
+					cache.account = account
+					return next(null, cache)
+			)
+		createBillingInfo: (cache, next) ->
+			user = cache.user
+			recurly_token_id = cache.recurly_token_id
+			subscriptionDetails = cache.subscriptionDetails
+			logger.log {user_id: user._id, recurly_token_id}, "creating billing info in recurly"
+			accountCode = cache?.account?.account_code
+			if !accountCode
+				return next(new Error('no account code at createBillingInfo stage'))
+			requestBody = """
+			<billing_info>
+				<token_id>#{recurly_token_id}</token_id>
+			</billing_info>
+			"""
+			RecurlyWrapper.apiRequest({
+				url: "accounts/#{accountCode}/billing_info"
+				method: "POST"
+				body: requestBody
+			}, (error, response, responseBody) =>
+				if error
+					logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while creating billing info"
+					return next(error)
+				RecurlyWrapper._parseBillingInfoXml responseBody, (err, billingInfo) ->
+					if err
+						logger.error {err, user_id: user._id, accountCode, recurly_token_id}, "error creating billing info"
+						return next(err)
+					cache.billingInfo = billingInfo
+					return next(null, cache)
+			)
+
+		setAddress: (cache, next) ->
+			user = cache.user
+			recurly_token_id = cache.recurly_token_id
+			subscriptionDetails = cache.subscriptionDetails
+			logger.log {user_id: user._id, recurly_token_id}, "setting billing address in recurly"
+			accountCode = cache?.account?.account_code
+			if !accountCode
+				return next(new Error('no account code at setAddress stage'))
+			address = subscriptionDetails.address
+			if !address
+				return next(new Error('no address in subscriptionDetails at setAddress stage'))
+			requestBody = RecurlyWrapper._addressToXml(address)
+			RecurlyWrapper.apiRequest({
+				url: "accounts/#{accountCode}/billing_info"
+				method: "PUT"
+				body: requestBody
+			}, (error, response, responseBody) =>
+				if error
+					logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while setting address"
+					return next(error)
+				RecurlyWrapper._parseBillingInfoXml responseBody, (err, billingInfo) ->
+					if err
+						logger.error {err, user_id: user._id, recurly_token_id}, "error updating billing info"
+						return next(err)
+					cache.billingInfo = billingInfo
+					return next(null, cache)
+			)
+		createSubscription: (cache, next) ->
+			user = cache.user
+			recurly_token_id = cache.recurly_token_id
+			subscriptionDetails = cache.subscriptionDetails
+			logger.log {user_id: user._id, recurly_token_id}, "creating subscription in recurly"
+			requestBody = """
+			<subscription>
+				<plan_code>#{subscriptionDetails.plan_code}</plan_code>
+				<currency>#{subscriptionDetails.currencyCode}</currency>
+				<coupon_code>#{subscriptionDetails.coupon_code}</coupon_code>
+				<account>
+					<account_code>#{user._id}</account_code>
+				</account>
+			</subscription>
+			"""  # TODO: check account details and billing
+			RecurlyWrapper.apiRequest({
+				url    : "subscriptions"
+				method : "POST"
+				body   : requestBody
+			}, (error, response, responseBody) =>
+				if error
+					logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while creating subscription"
+					return next(error)
+				RecurlyWrapper._parseSubscriptionXml responseBody, (err, subscription) ->
+					if err
+						logger.error {err, user_id: user._id, recurly_token_id}, "error creating subscription"
+						return next(err)
+					cache.subscription = subscription
+					return next(null, cache)
+			)
+
+	_createPaypalSubscription: (user, subscriptionDetails, recurly_token_id, callback) ->
+		logger.log {user_id: user._id, recurly_token_id}, "starting process of creating paypal subscription"
+		# We use `async.waterfall` to run each of these actions in sequence
+		# passing a `cache` object along the way. The cache is initialized
+		# with required data, and `async.apply` to pass the cache to the first function
+		cache = {user, recurly_token_id, subscriptionDetails}
+		Async.waterfall([
+			Async.apply(RecurlyWrapper._paypal.checkAccountExists, cache),
+			RecurlyWrapper._paypal.createAccount,
+			RecurlyWrapper._paypal.createBillingInfo,
+			RecurlyWrapper._paypal.setAddress,
+			RecurlyWrapper._paypal.createSubscription,
+		], (err, result) ->
+			if err
+				logger.error {err, user_id: user._id, recurly_token_id}, "error in paypal subscription creation process"
+				return callback(err)
+			if !result.subscription
+				err = new Error('no subscription object in result')
+				logger.error {err, user_id: user._id, recurly_token_id}, "error in paypal subscription creation process"
+				return callback(err)
+			logger.log {user_id: user._id, recurly_token_id}, "done creating paypal subscription for user"
+			callback(null, result.subscription)
+		)
+
+	_createCreditCardSubscription: (user, subscriptionDetails, recurly_token_id, callback) ->
 		requestBody = """
 		          <subscription>
 		            <plan_code>#{subscriptionDetails.plan_code}</plan_code>
@@ -25,17 +215,23 @@ module.exports = RecurlyWrapper =
 		            </account>
 		          </subscription>
 		          """
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url    : "subscriptions"
 			method : "POST"
 			body   : requestBody
 		}, (error, response, responseBody) =>
 			return callback(error) if error?
-			@_parseSubscriptionXml responseBody, callback
-		)		
+			RecurlyWrapper._parseSubscriptionXml responseBody, callback
+		)
+
+	createSubscription: (user, subscriptionDetails, recurly_token_id, callback)->
+		isPaypal = subscriptionDetails.isPaypal
+		logger.log {user_id: user._id, isPaypal, recurly_token_id}, "setting up subscription in recurly"
+		fn = if isPaypal then RecurlyWrapper._createPaypalSubscription else RecurlyWrapper._createCreditCardSubscription
+		fn user, subscriptionDetails, recurly_token_id, callback
 
 	apiRequest : (options, callback) ->
-		options.url = @apiUrl + "/" + options.url
+		options.url = RecurlyWrapper.apiUrl + "/" + options.url
 		options.headers =
 			"Authorization" : "Basic " + new Buffer(Settings.apis.recurly.apiKey).toString("base64")
 			"Accept"        : "application/xml"
@@ -60,7 +256,7 @@ module.exports = RecurlyWrapper =
 						newAttributes[key] = value
 				else
 					newAttributes[newKey] = value
-					
+
 			return newAttributes
 
 		crypto.randomBytes 32, (error, buffer) ->
@@ -74,14 +270,14 @@ module.exports = RecurlyWrapper =
 			signature = "#{signed}|#{unsignedQuery}"
 
 			callback null, signature
-	
+
 
 	getSubscriptions: (accountId, callback)->
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: "accounts/#{accountId}/subscriptions"
 		}, (error, response, body) =>
 			return callback(error) if error?
-			@_parseXml body, callback
+			RecurlyWrapper._parseXml body, callback
 		)
 
 
@@ -94,11 +290,11 @@ module.exports = RecurlyWrapper =
 		else
 			url = "subscriptions/#{subscriptionId}"
 
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: url
 		}, (error, response, body) =>
 			return callback(error) if error?
-			@_parseSubscriptionXml body, (error, recurlySubscription) =>
+			RecurlyWrapper._parseSubscriptionXml body, (error, recurlySubscription) =>
 				return callback(error) if error?
 				if options.includeAccount
 					if recurlySubscription.account? and recurlySubscription.account.url?
@@ -106,7 +302,7 @@ module.exports = RecurlyWrapper =
 					else
 						return callback "I don't understand the response from Recurly"
 
-					@getAccount accountId, (error, account) ->
+					RecurlyWrapper.getAccount accountId, (error, account) ->
 						return callback(error) if error?
 						recurlySubscription.account = account
 						callback null, recurlySubscription
@@ -124,9 +320,9 @@ module.exports = RecurlyWrapper =
 					per_page:200
 			if cursor?
 				opts.qs.cursor = cursor
-			@apiRequest opts, (error, response, body) =>
+			RecurlyWrapper.apiRequest opts, (error, response, body) =>
 				return callback(error) if error?
-				@_parseXml body, (err, data)->
+				RecurlyWrapper._parseXml body, (err, data)->
 					if err?
 						logger.err err:err, "could not get accoutns"
 						callback(err)
@@ -142,19 +338,19 @@ module.exports = RecurlyWrapper =
 
 
 	getAccount: (accountId, callback) ->
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: "accounts/#{accountId}"
 		}, (error, response, body) =>
 			return callback(error) if error?
-			@_parseAccountXml body, callback
+			RecurlyWrapper._parseAccountXml body, callback
 		)
 
 	getBillingInfo: (accountId, callback)->
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: "accounts/#{accountId}/billing_info"
 		}, (error, response, body) =>
 			return callback(error) if error?
-			@_parseXml body, callback
+			RecurlyWrapper._parseXml body, callback
 		)
 
 
@@ -166,13 +362,13 @@ module.exports = RecurlyWrapper =
 		            <timeframe>#{options.timeframe}</timeframe>
 		          </subscription>
 		          """
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url    : "subscriptions/#{subscriptionId}"
 			method : "put"
 			body   : requestBody
 		}, (error, response, responseBody) =>
 			return callback(error) if error?
-			@_parseSubscriptionXml responseBody, callback
+			RecurlyWrapper._parseSubscriptionXml responseBody, callback
 		)
 
 	createFixedAmmountCoupon: (coupon_code, name, currencyCode, discount_in_cents, plan_code, callback)->
@@ -191,7 +387,7 @@ module.exports = RecurlyWrapper =
 			</coupon>
 		"""
 		logger.log coupon_code:coupon_code, requestBody:requestBody, "creating coupon"
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url    : "coupons"
 			method : "post"
 			body   : requestBody
@@ -203,16 +399,16 @@ module.exports = RecurlyWrapper =
 
 
 	lookupCoupon: (coupon_code, callback)->
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: "coupons/#{coupon_code}"
 		}, (error, response, body) =>
 			return callback(error) if error?
-			@_parseXml body, callback
+			RecurlyWrapper._parseXml body, callback
 		)
 
 	cancelSubscription: (subscriptionId, callback) ->
 		logger.log subscriptionId:subscriptionId, "telling recurly to cancel subscription"
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: "subscriptions/#{subscriptionId}/cancel",
 			method: "put"
 		}, (error, response, body) ->
@@ -221,13 +417,13 @@ module.exports = RecurlyWrapper =
 
 	reactivateSubscription: (subscriptionId, callback) ->
 		logger.log subscriptionId:subscriptionId, "telling recurly to reactivating subscription"
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: "subscriptions/#{subscriptionId}/reactivate",
 			method: "put"
 		}, (error, response, body) ->
 			callback(error)
 		)
-		
+
 
 	redeemCoupon: (account_code, coupon_code, callback)->
 		requestBody = """
@@ -237,7 +433,7 @@ module.exports = RecurlyWrapper =
 			</redemption>
 		"""
 		logger.log account_code:account_code, coupon_code:coupon_code, requestBody:requestBody, "redeeming coupon for user"
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url    : "coupons/#{coupon_code}/redeem"
 			method : "post"
 			body   : requestBody
@@ -251,7 +447,7 @@ module.exports = RecurlyWrapper =
 		next_renewal_date = new Date()
 		next_renewal_date.setDate(next_renewal_date.getDate() + daysUntilExpire)
 		logger.log subscriptionId:subscriptionId, daysUntilExpire:daysUntilExpire, "Exending Free trial for user"
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url    : "/subscriptions/#{subscriptionId}/postpone?next_renewal_date=#{next_renewal_date}&bulk=false"
 			method : "put"
 		}, (error, response, responseBody) =>
@@ -261,7 +457,7 @@ module.exports = RecurlyWrapper =
 		)
 
 	_parseSubscriptionXml: (xml, callback) ->
-		@_parseXml xml, (error, data) ->
+		RecurlyWrapper._parseXml xml, (error, data) ->
 			return callback(error) if error?
 			if data? and data.subscription?
 				recurlySubscription = data.subscription
@@ -270,7 +466,7 @@ module.exports = RecurlyWrapper =
 			callback null, recurlySubscription
 
 	_parseAccountXml: (xml, callback) ->
-		@_parseXml xml, (error, data) ->
+		RecurlyWrapper._parseXml xml, (error, data) ->
 			return callback(error) if error?
 			if data? and data.account?
 				account = data.account
@@ -278,6 +474,15 @@ module.exports = RecurlyWrapper =
 				return callback "I don't understand the response from Recurly"
 			callback null, account
 
+	_parseBillingInfoXml: (xml, callback) ->
+		RecurlyWrapper._parseXml xml, (error, data) ->
+			return callback(error) if error?
+			if data? and data.billing_info?
+				billingInfo = data.billing_info
+			else
+				return callback "I don't understand the response from Recurly"
+			callback null, billingInfo
+
 	_parseXml: (xml, callback) ->
 		convertDataTypes = (data) ->
 			if data? and data["$"]?
@@ -299,7 +504,7 @@ module.exports = RecurlyWrapper =
 						else
 							array.push(convertDataTypes(value))
 					data = array
-			
+
 			if data instanceof Array
 				data = (convertDataTypes(entry) for entry in data)
 			else if typeof data == "object"
@@ -315,6 +520,3 @@ module.exports = RecurlyWrapper =
 			return callback(error) if error?
 			result = convertDataTypes(data)
 			callback null, result
-
-			
-

+ 0 - 88
services/web/app/coffee/Features/Wiki/WikiController.coffee

@@ -1,88 +0,0 @@
-request = require("request")
-settings = require("settings-sharelatex")
-logger = require("logger-sharelatex")
-ErrorController = require "../Errors/ErrorController"
-_ = require("underscore")
-AuthenticationController = require("../Authentication/AuthenticationController")
-async = require("async")
-other_lngs = ["es"]
-
-module.exports = WikiController = 
-
-
-	_checkIfLoginIsNeeded: (req, res, next)->
-		if settings.apis.wiki.requireLogin
-			AuthenticationController.requireLogin()(req, res, next)
-		else
-			next()
-
-	getPage: (req, res, next) ->
-		WikiController._checkIfLoginIsNeeded req, res, ->
-			
-			page = req.url.replace(/^\/learn/, "").replace(/^\//, "")
-			if page == ""
-				page = "Main_Page"
-
-			logger.log page: page, "getting page from wiki"
-			if _.include(other_lngs, req.lng)
-				lngPage = "#{page}_#{req.lng}"
-			else
-				lngPage = page
-			jobs =
-				contents: (cb)-> 
-					WikiController._getPageContent "Contents", cb
-				pageData: (cb)->
-					WikiController._getPageContent lngPage, cb
-			async.parallel jobs, (error, results)->
-				return next(error) if error?
-				{pageData, contents} = results
-				if pageData.content?.length > 280
-					if _.include(other_lngs, req.lng)
-						pageData.title = pageData.title.slice(0, pageData.title.length - (req.lng.length+1) )
-
-					if pageData.title?.toLowerCase()?.indexOf("kb") == 0
-						pageData.title = pageData.title.slice(3)
-
-					if pageData.title?.toLowerCase()?.indexOf("errors") == 0
-						pageData.title = pageData.title.slice(7)
-						
-					WikiController._renderPage(pageData, contents, res)
-				else
-					WikiController._getPageContent page, (error, pageData) ->
-						return next(error) if error?
-						WikiController._renderPage(pageData, contents, res)
-
-
-					
-
-	_getPageContent: (page, callback = (error, data = { content: "", title: "" }) ->) ->
-		request {
-			url: "#{settings.apis.wiki.url}/learn-scripts/api.php"
-			qs: {
-				page: decodeURI(page)
-				action: "parse"
-				format: "json"
-			}
-		}, (err, response, data)->
-			return callback(err) if err?
-			try
-				data = JSON.parse(data)
-			catch err
-				logger.err err:err, data:data, "error parsing data from wiki"
-			result = 
-				content: data?.parse?.text?['*']
-				title: data?.parse?.title
-			callback null, result
-
-
-	_renderPage: (page, contents, res)->
-		if page.title == "Main Page"
-			title = "Documentation"
-		else
-			title = page.title
-			
-		res.render "wiki/page", {
-			page: page
-			contents: contents
-			title: title
-		}

+ 0 - 8
services/web/app/coffee/router.coffee

@@ -30,7 +30,6 @@ PasswordResetRouter = require("./Features/PasswordReset/PasswordResetRouter")
 StaticPagesRouter = require("./Features/StaticPages/StaticPagesRouter")
 ChatController = require("./Features/Chat/ChatController")
 BlogController = require("./Features/Blog/BlogController")
-WikiController = require("./Features/Wiki/WikiController")
 Modules = require "./infrastructure/Modules"
 RateLimiterMiddlewear = require('./Features/Security/RateLimiterMiddlewear')
 RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
@@ -204,13 +203,6 @@ module.exports = class Router
 		webRouter.get  "/project/:Project_id/messages", AuthorizationMiddlewear.ensureUserCanReadProject, ChatController.getMessages
 		webRouter.post "/project/:Project_id/messages", AuthorizationMiddlewear.ensureUserCanReadProject, ChatController.sendMessage
 
-		webRouter.get  /learn(\/.*)?/, RateLimiterMiddlewear.rateLimit({
-			endpointName: "wiki"
-			params: []
-			maxRequests: 60
-			timeInterval: 60
-		}), WikiController.getPage
-
 		webRouter.post "/project/:Project_id/references/index", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.index
 		webRouter.post "/project/:Project_id/references/indexAll", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.indexAll
 

+ 1 - 0
services/web/app/views/project/editor.jade

@@ -97,6 +97,7 @@ block content
 		window.csrfToken = "!{csrfToken}";
 		window.anonymous = #{anonymous};
 		window.maxDocLength = #{maxDocLength};
+		window.wikiEnabled = #{!!(settings.apis.wiki && settings.apis.wiki.url)};
 		window.requirejs = {
 			"paths" : {
 				"mathjax": "/js/libs/mathjax/MathJax.js?config=TeX-AMS_HTML",

+ 7 - 3
services/web/app/views/project/editor/pdf.jade

@@ -103,6 +103,8 @@ div.full-size.pdf(ng-controller="PdfController")
 				ng-init="feedbackSent = false;"
 			)
 				span.line-no
+					i.fa.fa-link(aria-hidden="true")
+					| &nbsp;
 					span(ng-show="entry.file") {{ entry.file }}
 					span(ng-show="entry.line") , line {{ entry.line }}
 				p.entry-message(ng-show="entry.message") {{ entry.message }}
@@ -112,9 +114,11 @@ div.full-size.pdf(ng-controller="PdfController")
 				)
 					figure.card-hint-icon-container
 						i.fa.fa-lightbulb-o(aria-hidden="true")
-					p.card-hint-text(ng-show="entry.humanReadableHint", ng-bind-html="entry.humanReadableHint")
-					.card-hint-actions
-						.card-hint-ext-link
+					p.card-hint-text(
+						ng-show="entry.humanReadableHint",
+						ng-bind-html="wikiEnabled ? entry.humanReadableHint : stripHTMLFromString(entry.humanReadableHint)")
+					.card-hint-actions.clearfix
+						.card-hint-ext-link(ng-if="wikiEnabled")
 							a(ng-href="{{ entry.extraInfoURL }}", target="_blank")
 								i.fa.fa-external-link
 								|&nbsp;#{translate("log_hint_extra_info")}

+ 0 - 68
services/web/app/views/wiki/page.jade

@@ -1,68 +0,0 @@
-extends ../layout
-
-block content
-	.content.content-alt(ng-cloak)
-		.container.wiki
-			.row.template-page-header
-				.col-md-8(ng-cloak)
-
-			.row
-				.col-xs-3.contents(ng-non-bindable)
-					| !{contents.content}
-
-				.col-xs-9.page
-					- if(typeof(settings.algolia) != "undefined" && typeof(settings.algolia.indexes) != "undefined" && typeof(settings.algolia.indexes.wiki) != "undefined")
-						span(ng-controller="SearchWikiController")
-							.row
-								form.project-search.form-horizontal.col-md-9(role="form")
-									.form-group.has-feedback.has-feedback-left.col-md-12
-										input.form-control.col-md-12(type='text', ng-model='searchQueryText', ng-keyup='search()', placeholder="Search help library....")
-										i.fa.fa-search.form-control-feedback-left
-										i.fa.fa-times.form-control-feedback(
-											ng-click="clearSearchText()",
-											style="cursor: pointer;",
-											ng-show="searchQueryText.length > 0"
-										)
-								.col-md-3.text-right
-									a.btn.btn-primary(ng-click="showMissingTemplateModal()") #{translate("suggest_new_doc")}
-
-							.row
-								.col-md-12(ng-cloak)
-									a(ng-href='{{hit.url}}',ng-repeat='hit in hits').search-result.card.card-thin
-										span(ng-bind-html='hit.name')
-										div.search-result-content(ng-show="hit.content != ''", ng-bind-html='hit.content')
-
-					.card.row-spaced(ng-non-bindable)
-						.page-header
-							h1 #{title}
-							
-						| !{page.content}
-
-
-
-
-	script(type="text/ng-template", id="missingWikiPageModal")
-		.modal-header
-			button.close(
-				type="button"
-				data-dismiss="modal"
-				ng-click="close()"
-			) &times;
-			h3 #{translate("suggest_new_doc")}
-		.modal-body.contact-us-modal
-			span(ng-show="sent == false")
-				label.desc
-					| #{translate("email")} (#{translate("optional")})
-				.form-group
-					input.field.text.medium.span8.form-control(ng-model="form.email",  ng-init="form.email = '#{getUserEmail()}'", type='email', spellcheck='false', value='', maxlength='255', tabindex='2')
-				label.desc
-					| #{translate("suggestion")}
-				.form-group
-					textarea.field.text.medium.span8.form-control(ng-model="form.message",type='text', value='', maxlength='255', tabindex='4', onkeyup='')
-			span(ng-show="sent")
-				p #{translate("request_sent_thank_you")}
-		.modal-footer
-			button.btn.btn-default(ng-click="close()") 
-				span #{translate("dismiss")}
-			button.btn-success.btn(type='submit', ng-disabled="sending", ng-click="contactUs()") #{translate("contact_us")}
-

+ 1 - 0
services/web/config/settings.defaults.coffee

@@ -137,6 +137,7 @@ module.exports = settings =
 	# --------
 	security:
 		sessionSecret: sessionSecret
+		bcryptRounds: 12 # number of rounds used to hash user passwords (raised to power 2)
 
 	httpAuthUsers: httpAuthUsers
 

+ 4602 - 0
services/web/npm-shrinkwrap.json

@@ -0,0 +1,4602 @@
+{
+  "name": "web-sharelatex",
+  "version": "0.1.4",
+  "dependencies": {
+    "archiver": {
+      "version": "0.9.0",
+      "from": "archiver@0.9.0",
+      "resolved": "https://registry.npmjs.org/archiver/-/archiver-0.9.0.tgz",
+      "dependencies": {
+        "buffer-crc32": {
+          "version": "0.2.5",
+          "from": "buffer-crc32@>=0.2.1 <0.3.0",
+          "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.5.tgz"
+        },
+        "readable-stream": {
+          "version": "1.0.34",
+          "from": "readable-stream@>=1.0.24 <1.1.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+          "dependencies": {
+            "core-util-is": {
+              "version": "1.0.2",
+              "from": "core-util-is@>=1.0.0 <1.1.0",
+              "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+            },
+            "isarray": {
+              "version": "0.0.1",
+              "from": "isarray@0.0.1",
+              "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+            },
+            "string_decoder": {
+              "version": "0.10.31",
+              "from": "string_decoder@>=0.10.0 <0.11.0",
+              "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+            },
+            "inherits": {
+              "version": "2.0.1",
+              "from": "inherits@>=2.0.1 <2.1.0",
+              "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+            }
+          }
+        },
+        "tar-stream": {
+          "version": "0.3.3",
+          "from": "tar-stream@>=0.3.0 <0.4.0",
+          "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-0.3.3.tgz",
+          "dependencies": {
+            "bl": {
+              "version": "0.6.0",
+              "from": "bl@>=0.6.0 <0.7.0",
+              "resolved": "https://registry.npmjs.org/bl/-/bl-0.6.0.tgz"
+            },
+            "end-of-stream": {
+              "version": "0.1.5",
+              "from": "end-of-stream@>=0.1.3 <0.2.0",
+              "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz",
+              "dependencies": {
+                "once": {
+                  "version": "1.3.3",
+                  "from": "once@>=1.3.0 <1.4.0",
+                  "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
+                  "dependencies": {
+                    "wrappy": {
+                      "version": "1.0.2",
+                      "from": "wrappy@>=1.0.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "zip-stream": {
+          "version": "0.3.7",
+          "from": "zip-stream@>=0.3.0 <0.4.0",
+          "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-0.3.7.tgz",
+          "dependencies": {
+            "crc32-stream": {
+              "version": "0.2.0",
+              "from": "crc32-stream@>=0.2.0 <0.3.0",
+              "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-0.2.0.tgz"
+            },
+            "debug": {
+              "version": "1.0.4",
+              "from": "debug@>=1.0.2 <1.1.0",
+              "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz",
+              "dependencies": {
+                "ms": {
+                  "version": "0.6.2",
+                  "from": "ms@0.6.2",
+                  "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
+                }
+              }
+            },
+            "deflate-crc32-stream": {
+              "version": "0.1.2",
+              "from": "deflate-crc32-stream@>=0.1.0 <0.2.0",
+              "resolved": "https://registry.npmjs.org/deflate-crc32-stream/-/deflate-crc32-stream-0.1.2.tgz"
+            }
+          }
+        },
+        "lazystream": {
+          "version": "0.1.0",
+          "from": "lazystream@>=0.1.0 <0.2.0",
+          "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-0.1.0.tgz"
+        },
+        "file-utils": {
+          "version": "0.1.5",
+          "from": "file-utils@>=0.1.5 <0.2.0",
+          "resolved": "https://registry.npmjs.org/file-utils/-/file-utils-0.1.5.tgz",
+          "dependencies": {
+            "lodash": {
+              "version": "2.1.0",
+              "from": "lodash@>=2.1.0 <2.2.0",
+              "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.1.0.tgz"
+            },
+            "iconv-lite": {
+              "version": "0.2.11",
+              "from": "iconv-lite@>=0.2.11 <0.3.0",
+              "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz"
+            },
+            "glob": {
+              "version": "3.2.11",
+              "from": "glob@>=3.2.6 <3.3.0",
+              "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+              "dependencies": {
+                "inherits": {
+                  "version": "2.0.1",
+                  "from": "inherits@>=2.0.0 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                },
+                "minimatch": {
+                  "version": "0.3.0",
+                  "from": "minimatch@>=0.3.0 <0.4.0",
+                  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+                  "dependencies": {
+                    "lru-cache": {
+                      "version": "2.7.3",
+                      "from": "lru-cache@>=2.0.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz"
+                    },
+                    "sigmund": {
+                      "version": "1.0.1",
+                      "from": "sigmund@>=1.0.0 <1.1.0",
+                      "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+                    }
+                  }
+                }
+              }
+            },
+            "minimatch": {
+              "version": "0.2.14",
+              "from": "minimatch@>=0.2.12 <0.3.0",
+              "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+              "dependencies": {
+                "lru-cache": {
+                  "version": "2.7.3",
+                  "from": "lru-cache@>=2.0.0 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz"
+                },
+                "sigmund": {
+                  "version": "1.0.1",
+                  "from": "sigmund@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+                }
+              }
+            },
+            "findup-sync": {
+              "version": "0.1.3",
+              "from": "findup-sync@>=0.1.2 <0.2.0",
+              "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
+              "dependencies": {
+                "lodash": {
+                  "version": "2.4.2",
+                  "from": "lodash@>=2.4.1 <2.5.0",
+                  "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+                }
+              }
+            },
+            "isbinaryfile": {
+              "version": "0.1.9",
+              "from": "isbinaryfile@>=0.1.9 <0.2.0",
+              "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-0.1.9.tgz"
+            }
+          }
+        },
+        "lodash": {
+          "version": "2.4.2",
+          "from": "lodash@>=2.4.0 <2.5.0",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+        }
+      }
+    },
+    "async": {
+      "version": "0.6.2",
+      "from": "async@0.6.2",
+      "resolved": "https://registry.npmjs.org/async/-/async-0.6.2.tgz"
+    },
+    "base64-stream": {
+      "version": "0.1.3",
+      "from": "base64-stream@>=0.1.2 <0.2.0",
+      "resolved": "https://registry.npmjs.org/base64-stream/-/base64-stream-0.1.3.tgz",
+      "dependencies": {
+        "readable-stream": {
+          "version": "2.1.4",
+          "from": "readable-stream@>=2.0.2 <3.0.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.4.tgz",
+          "dependencies": {
+            "buffer-shims": {
+              "version": "1.0.0",
+              "from": "buffer-shims@>=1.0.0 <2.0.0",
+              "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz"
+            },
+            "core-util-is": {
+              "version": "1.0.2",
+              "from": "core-util-is@>=1.0.0 <1.1.0",
+              "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+            },
+            "inherits": {
+              "version": "2.0.1",
+              "from": "inherits@>=2.0.1 <2.1.0",
+              "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+            },
+            "isarray": {
+              "version": "1.0.0",
+              "from": "isarray@>=1.0.0 <1.1.0",
+              "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
+            },
+            "process-nextick-args": {
+              "version": "1.0.7",
+              "from": "process-nextick-args@>=1.0.6 <1.1.0",
+              "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
+            },
+            "string_decoder": {
+              "version": "0.10.31",
+              "from": "string_decoder@>=0.10.0 <0.11.0",
+              "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+            },
+            "util-deprecate": {
+              "version": "1.0.2",
+              "from": "util-deprecate@>=1.0.1 <1.1.0",
+              "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
+            }
+          }
+        }
+      }
+    },
+    "basic-auth-connect": {
+      "version": "1.0.0",
+      "from": "basic-auth-connect@>=1.0.0 <2.0.0",
+      "resolved": "https://registry.npmjs.org/basic-auth-connect/-/basic-auth-connect-1.0.0.tgz"
+    },
+    "bcrypt": {
+      "version": "0.8.3",
+      "from": "bcrypt@0.8.3",
+      "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-0.8.3.tgz",
+      "dependencies": {
+        "bindings": {
+          "version": "1.2.1",
+          "from": "bindings@1.2.1",
+          "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
+        },
+        "nan": {
+          "version": "1.8.4",
+          "from": "nan@1.8.4",
+          "resolved": "https://registry.npmjs.org/nan/-/nan-1.8.4.tgz"
+        }
+      }
+    },
+    "body-parser": {
+      "version": "1.15.2",
+      "from": "body-parser@>=1.13.1 <2.0.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.15.2.tgz",
+      "dependencies": {
+        "bytes": {
+          "version": "2.4.0",
+          "from": "bytes@2.4.0",
+          "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz"
+        },
+        "content-type": {
+          "version": "1.0.2",
+          "from": "content-type@>=1.0.1 <1.1.0",
+          "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz"
+        },
+        "debug": {
+          "version": "2.2.0",
+          "from": "debug@>=2.2.0 <2.3.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+          "dependencies": {
+            "ms": {
+              "version": "0.7.1",
+              "from": "ms@0.7.1",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+            }
+          }
+        },
+        "depd": {
+          "version": "1.1.0",
+          "from": "depd@>=1.1.0 <1.2.0",
+          "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz"
+        },
+        "http-errors": {
+          "version": "1.5.0",
+          "from": "http-errors@>=1.5.0 <1.6.0",
+          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.0.tgz",
+          "dependencies": {
+            "inherits": {
+              "version": "2.0.1",
+              "from": "inherits@2.0.1",
+              "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+            },
+            "setprototypeof": {
+              "version": "1.0.1",
+              "from": "setprototypeof@1.0.1",
+              "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.1.tgz"
+            },
+            "statuses": {
+              "version": "1.3.0",
+              "from": "statuses@>=1.3.0 <2.0.0",
+              "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.0.tgz"
+            }
+          }
+        },
+        "iconv-lite": {
+          "version": "0.4.13",
+          "from": "iconv-lite@>=0.4.13 <0.5.0",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz"
+        },
+        "on-finished": {
+          "version": "2.3.0",
+          "from": "on-finished@>=2.3.0 <2.4.0",
+          "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+          "dependencies": {
+            "ee-first": {
+              "version": "1.1.1",
+              "from": "ee-first@1.1.1",
+              "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
+            }
+          }
+        },
+        "qs": {
+          "version": "6.2.0",
+          "from": "qs@6.2.0",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.0.tgz"
+        },
+        "raw-body": {
+          "version": "2.1.7",
+          "from": "raw-body@>=2.1.7 <2.2.0",
+          "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz",
+          "dependencies": {
+            "unpipe": {
+              "version": "1.0.0",
+              "from": "unpipe@1.0.0",
+              "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
+            }
+          }
+        },
+        "type-is": {
+          "version": "1.6.13",
+          "from": "type-is@>=1.6.13 <1.7.0",
+          "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.13.tgz",
+          "dependencies": {
+            "media-typer": {
+              "version": "0.3.0",
+              "from": "media-typer@0.3.0",
+              "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
+            },
+            "mime-types": {
+              "version": "2.1.11",
+              "from": "mime-types@>=2.1.11 <2.2.0",
+              "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz",
+              "dependencies": {
+                "mime-db": {
+                  "version": "1.23.0",
+                  "from": "mime-db@>=1.23.0 <1.24.0",
+                  "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "bufferedstream": {
+      "version": "1.6.0",
+      "from": "bufferedstream@1.6.0",
+      "resolved": "https://registry.npmjs.org/bufferedstream/-/bufferedstream-1.6.0.tgz"
+    },
+    "connect-redis": {
+      "version": "2.3.0",
+      "from": "connect-redis@2.3.0",
+      "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-2.3.0.tgz",
+      "dependencies": {
+        "debug": {
+          "version": "1.0.4",
+          "from": "debug@>=1.0.4 <2.0.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz",
+          "dependencies": {
+            "ms": {
+              "version": "0.6.2",
+              "from": "ms@0.6.2",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
+            }
+          }
+        },
+        "redis": {
+          "version": "0.12.1",
+          "from": "redis@>=0.12.1 <0.13.0",
+          "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz"
+        }
+      }
+    },
+    "contentful": {
+      "version": "3.4.1",
+      "from": "contentful@>=3.3.14 <4.0.0",
+      "resolved": "https://registry.npmjs.org/contentful/-/contentful-3.4.1.tgz",
+      "dependencies": {
+        "babel-runtime": {
+          "version": "6.9.2",
+          "from": "babel-runtime@>=6.3.19 <7.0.0",
+          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.9.2.tgz",
+          "dependencies": {
+            "core-js": {
+              "version": "2.4.0",
+              "from": "core-js@>=2.4.0 <3.0.0",
+              "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.0.tgz"
+            },
+            "regenerator-runtime": {
+              "version": "0.9.5",
+              "from": "regenerator-runtime@>=0.9.5 <0.10.0",
+              "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.9.5.tgz"
+            }
+          }
+        },
+        "contentful-sdk-core": {
+          "version": "2.3.0",
+          "from": "contentful-sdk-core@>=2.3.0 <3.0.0",
+          "resolved": "https://registry.npmjs.org/contentful-sdk-core/-/contentful-sdk-core-2.3.0.tgz",
+          "dependencies": {
+            "babel-plugin-rewire": {
+              "version": "1.0.0-rc-4",
+              "from": "babel-plugin-rewire@>=1.0.0-rc-3 <2.0.0",
+              "resolved": "https://registry.npmjs.org/babel-plugin-rewire/-/babel-plugin-rewire-1.0.0-rc-4.tgz"
+            },
+            "follow-redirects": {
+              "version": "0.0.7",
+              "from": "follow-redirects@0.0.7",
+              "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.7.tgz",
+              "dependencies": {
+                "debug": {
+                  "version": "2.2.0",
+                  "from": "debug@>=2.2.0 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+                  "dependencies": {
+                    "ms": {
+                      "version": "0.7.1",
+                      "from": "ms@0.7.1",
+                      "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+                    }
+                  }
+                },
+                "stream-consume": {
+                  "version": "0.1.0",
+                  "from": "stream-consume@>=0.1.0 <0.2.0",
+                  "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz"
+                }
+              }
+            },
+            "qs": {
+              "version": "6.2.0",
+              "from": "qs@>=6.1.0 <7.0.0",
+              "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.0.tgz"
+            },
+            "babel-core": {
+              "version": "6.10.4",
+              "from": "babel-core@>=6.0.0 <7.0.0",
+              "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.10.4.tgz",
+              "dependencies": {
+                "babel-code-frame": {
+                  "version": "6.11.0",
+                  "from": "babel-code-frame@>=6.8.0 <7.0.0",
+                  "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.11.0.tgz",
+                  "dependencies": {
+                    "chalk": {
+                      "version": "1.1.3",
+                      "from": "chalk@>=1.1.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+                      "dependencies": {
+                        "ansi-styles": {
+                          "version": "2.2.1",
+                          "from": "ansi-styles@>=2.2.1 <3.0.0",
+                          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz"
+                        },
+                        "escape-string-regexp": {
+                          "version": "1.0.5",
+                          "from": "escape-string-regexp@>=1.0.2 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
+                        },
+                        "has-ansi": {
+                          "version": "2.0.0",
+                          "from": "has-ansi@>=2.0.0 <3.0.0",
+                          "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+                          "dependencies": {
+                            "ansi-regex": {
+                              "version": "2.0.0",
+                              "from": "ansi-regex@>=2.0.0 <3.0.0",
+                              "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
+                            }
+                          }
+                        },
+                        "strip-ansi": {
+                          "version": "3.0.1",
+                          "from": "strip-ansi@>=3.0.0 <4.0.0",
+                          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+                          "dependencies": {
+                            "ansi-regex": {
+                              "version": "2.0.0",
+                              "from": "ansi-regex@>=2.0.0 <3.0.0",
+                              "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
+                            }
+                          }
+                        },
+                        "supports-color": {
+                          "version": "2.0.0",
+                          "from": "supports-color@>=2.0.0 <3.0.0",
+                          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz"
+                        }
+                      }
+                    },
+                    "esutils": {
+                      "version": "2.0.2",
+                      "from": "esutils@>=2.0.2 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz"
+                    },
+                    "js-tokens": {
+                      "version": "2.0.0",
+                      "from": "js-tokens@>=2.0.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-2.0.0.tgz"
+                    }
+                  }
+                },
+                "babel-generator": {
+                  "version": "6.11.0",
+                  "from": "babel-generator@>=6.9.0 <7.0.0",
+                  "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.11.0.tgz",
+                  "dependencies": {
+                    "detect-indent": {
+                      "version": "3.0.1",
+                      "from": "detect-indent@>=3.0.1 <4.0.0",
+                      "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-3.0.1.tgz",
+                      "dependencies": {
+                        "get-stdin": {
+                          "version": "4.0.1",
+                          "from": "get-stdin@>=4.0.1 <5.0.0",
+                          "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz"
+                        },
+                        "minimist": {
+                          "version": "1.2.0",
+                          "from": "minimist@>=1.1.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz"
+                        },
+                        "repeating": {
+                          "version": "1.1.3",
+                          "from": "repeating@>=1.1.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz",
+                          "dependencies": {
+                            "is-finite": {
+                              "version": "1.0.1",
+                              "from": "is-finite@>=1.0.0 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz",
+                              "dependencies": {
+                                "number-is-nan": {
+                                  "version": "1.0.0",
+                                  "from": "number-is-nan@>=1.0.0 <2.0.0",
+                                  "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz"
+                                }
+                              }
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                },
+                "babel-helpers": {
+                  "version": "6.8.0",
+                  "from": "babel-helpers@>=6.8.0 <7.0.0",
+                  "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.8.0.tgz"
+                },
+                "babel-messages": {
+                  "version": "6.8.0",
+                  "from": "babel-messages@>=6.8.0 <7.0.0",
+                  "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.8.0.tgz"
+                },
+                "babel-register": {
+                  "version": "6.9.0",
+                  "from": "babel-register@>=6.9.0 <7.0.0",
+                  "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.9.0.tgz",
+                  "dependencies": {
+                    "core-js": {
+                      "version": "2.4.0",
+                      "from": "core-js@>=2.4.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.0.tgz"
+                    },
+                    "home-or-tmp": {
+                      "version": "1.0.0",
+                      "from": "home-or-tmp@>=1.0.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-1.0.0.tgz",
+                      "dependencies": {
+                        "os-tmpdir": {
+                          "version": "1.0.1",
+                          "from": "os-tmpdir@>=1.0.1 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.1.tgz"
+                        },
+                        "user-home": {
+                          "version": "1.1.1",
+                          "from": "user-home@>=1.1.1 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz"
+                        }
+                      }
+                    },
+                    "mkdirp": {
+                      "version": "0.5.1",
+                      "from": "mkdirp@>=0.5.1 <0.6.0",
+                      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+                      "dependencies": {
+                        "minimist": {
+                          "version": "0.0.8",
+                          "from": "minimist@0.0.8",
+                          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
+                        }
+                      }
+                    },
+                    "source-map-support": {
+                      "version": "0.2.10",
+                      "from": "source-map-support@>=0.2.10 <0.3.0",
+                      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.2.10.tgz",
+                      "dependencies": {
+                        "source-map": {
+                          "version": "0.1.32",
+                          "from": "source-map@0.1.32",
+                          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz",
+                          "dependencies": {
+                            "amdefine": {
+                              "version": "1.0.0",
+                              "from": "amdefine@>=0.0.4",
+                              "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz"
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                },
+                "babel-traverse": {
+                  "version": "6.10.4",
+                  "from": "babel-traverse@>=6.10.4 <7.0.0",
+                  "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.10.4.tgz",
+                  "dependencies": {
+                    "globals": {
+                      "version": "8.18.0",
+                      "from": "globals@>=8.3.0 <9.0.0",
+                      "resolved": "https://registry.npmjs.org/globals/-/globals-8.18.0.tgz"
+                    },
+                    "invariant": {
+                      "version": "2.2.1",
+                      "from": "invariant@>=2.2.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.1.tgz",
+                      "dependencies": {
+                        "loose-envify": {
+                          "version": "1.2.0",
+                          "from": "loose-envify@>=1.0.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.2.0.tgz",
+                          "dependencies": {
+                            "js-tokens": {
+                              "version": "1.0.3",
+                              "from": "js-tokens@>=1.0.1 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-1.0.3.tgz"
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                },
+                "babylon": {
+                  "version": "6.8.2",
+                  "from": "babylon@>=6.7.0 <7.0.0",
+                  "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.8.2.tgz"
+                },
+                "convert-source-map": {
+                  "version": "1.2.0",
+                  "from": "convert-source-map@>=1.1.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.2.0.tgz"
+                },
+                "debug": {
+                  "version": "2.2.0",
+                  "from": "debug@>=2.1.1 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+                  "dependencies": {
+                    "ms": {
+                      "version": "0.7.1",
+                      "from": "ms@0.7.1",
+                      "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+                    }
+                  }
+                },
+                "json5": {
+                  "version": "0.4.0",
+                  "from": "json5@>=0.4.0 <0.5.0",
+                  "resolved": "https://registry.npmjs.org/json5/-/json5-0.4.0.tgz"
+                },
+                "minimatch": {
+                  "version": "3.0.2",
+                  "from": "minimatch@>=3.0.2 <4.0.0",
+                  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz",
+                  "dependencies": {
+                    "brace-expansion": {
+                      "version": "1.1.5",
+                      "from": "brace-expansion@>=1.0.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.5.tgz",
+                      "dependencies": {
+                        "balanced-match": {
+                          "version": "0.4.1",
+                          "from": "balanced-match@>=0.4.1 <0.5.0",
+                          "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz"
+                        },
+                        "concat-map": {
+                          "version": "0.0.1",
+                          "from": "concat-map@0.0.1",
+                          "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+                        }
+                      }
+                    }
+                  }
+                },
+                "path-exists": {
+                  "version": "1.0.0",
+                  "from": "path-exists@>=1.0.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz"
+                },
+                "path-is-absolute": {
+                  "version": "1.0.0",
+                  "from": "path-is-absolute@>=1.0.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz"
+                },
+                "private": {
+                  "version": "0.1.6",
+                  "from": "private@>=0.1.6 <0.2.0",
+                  "resolved": "https://registry.npmjs.org/private/-/private-0.1.6.tgz"
+                },
+                "shebang-regex": {
+                  "version": "1.0.0",
+                  "from": "shebang-regex@>=1.0.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz"
+                },
+                "slash": {
+                  "version": "1.0.0",
+                  "from": "slash@>=1.0.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz"
+                },
+                "source-map": {
+                  "version": "0.5.6",
+                  "from": "source-map@>=0.5.0 <0.6.0",
+                  "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz"
+                }
+              }
+            },
+            "babel-template": {
+              "version": "6.9.0",
+              "from": "babel-template@>=6.2.0 <7.0.0",
+              "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.9.0.tgz",
+              "dependencies": {
+                "babylon": {
+                  "version": "6.8.2",
+                  "from": "babylon@>=6.7.0 <7.0.0",
+                  "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.8.2.tgz"
+                },
+                "babel-traverse": {
+                  "version": "6.10.4",
+                  "from": "babel-traverse@>=6.9.0 <7.0.0",
+                  "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.10.4.tgz",
+                  "dependencies": {
+                    "babel-code-frame": {
+                      "version": "6.11.0",
+                      "from": "babel-code-frame@>=6.8.0 <7.0.0",
+                      "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.11.0.tgz",
+                      "dependencies": {
+                        "chalk": {
+                          "version": "1.1.3",
+                          "from": "chalk@>=1.1.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+                          "dependencies": {
+                            "ansi-styles": {
+                              "version": "2.2.1",
+                              "from": "ansi-styles@>=2.2.1 <3.0.0",
+                              "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz"
+                            },
+                            "escape-string-regexp": {
+                              "version": "1.0.5",
+                              "from": "escape-string-regexp@>=1.0.2 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
+                            },
+                            "has-ansi": {
+                              "version": "2.0.0",
+                              "from": "has-ansi@>=2.0.0 <3.0.0",
+                              "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+                              "dependencies": {
+                                "ansi-regex": {
+                                  "version": "2.0.0",
+                                  "from": "ansi-regex@>=2.0.0 <3.0.0",
+                                  "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
+                                }
+                              }
+                            },
+                            "strip-ansi": {
+                              "version": "3.0.1",
+                              "from": "strip-ansi@>=3.0.0 <4.0.0",
+                              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+                              "dependencies": {
+                                "ansi-regex": {
+                                  "version": "2.0.0",
+                                  "from": "ansi-regex@>=2.0.0 <3.0.0",
+                                  "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
+                                }
+                              }
+                            },
+                            "supports-color": {
+                              "version": "2.0.0",
+                              "from": "supports-color@>=2.0.0 <3.0.0",
+                              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz"
+                            }
+                          }
+                        },
+                        "esutils": {
+                          "version": "2.0.2",
+                          "from": "esutils@>=2.0.2 <3.0.0",
+                          "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz"
+                        },
+                        "js-tokens": {
+                          "version": "2.0.0",
+                          "from": "js-tokens@>=2.0.0 <3.0.0",
+                          "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-2.0.0.tgz"
+                        }
+                      }
+                    },
+                    "babel-messages": {
+                      "version": "6.8.0",
+                      "from": "babel-messages@>=6.8.0 <7.0.0",
+                      "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.8.0.tgz"
+                    },
+                    "debug": {
+                      "version": "2.2.0",
+                      "from": "debug@>=2.2.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+                      "dependencies": {
+                        "ms": {
+                          "version": "0.7.1",
+                          "from": "ms@0.7.1",
+                          "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+                        }
+                      }
+                    },
+                    "globals": {
+                      "version": "8.18.0",
+                      "from": "globals@>=8.3.0 <9.0.0",
+                      "resolved": "https://registry.npmjs.org/globals/-/globals-8.18.0.tgz"
+                    },
+                    "invariant": {
+                      "version": "2.2.1",
+                      "from": "invariant@>=2.2.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.1.tgz",
+                      "dependencies": {
+                        "loose-envify": {
+                          "version": "1.2.0",
+                          "from": "loose-envify@>=1.0.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.2.0.tgz",
+                          "dependencies": {
+                            "js-tokens": {
+                              "version": "1.0.3",
+                              "from": "js-tokens@>=1.0.1 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-1.0.3.tgz"
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            },
+            "babel-types": {
+              "version": "6.11.1",
+              "from": "babel-types@>=6.2.0 <7.0.0",
+              "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.11.1.tgz",
+              "dependencies": {
+                "babel-traverse": {
+                  "version": "6.10.4",
+                  "from": "babel-traverse@>=6.9.0 <7.0.0",
+                  "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.10.4.tgz",
+                  "dependencies": {
+                    "babel-code-frame": {
+                      "version": "6.11.0",
+                      "from": "babel-code-frame@>=6.8.0 <7.0.0",
+                      "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.11.0.tgz",
+                      "dependencies": {
+                        "chalk": {
+                          "version": "1.1.3",
+                          "from": "chalk@>=1.1.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+                          "dependencies": {
+                            "ansi-styles": {
+                              "version": "2.2.1",
+                              "from": "ansi-styles@>=2.2.1 <3.0.0",
+                              "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz"
+                            },
+                            "escape-string-regexp": {
+                              "version": "1.0.5",
+                              "from": "escape-string-regexp@>=1.0.2 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
+                            },
+                            "has-ansi": {
+                              "version": "2.0.0",
+                              "from": "has-ansi@>=2.0.0 <3.0.0",
+                              "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+                              "dependencies": {
+                                "ansi-regex": {
+                                  "version": "2.0.0",
+                                  "from": "ansi-regex@>=2.0.0 <3.0.0",
+                                  "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
+                                }
+                              }
+                            },
+                            "strip-ansi": {
+                              "version": "3.0.1",
+                              "from": "strip-ansi@>=3.0.0 <4.0.0",
+                              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+                              "dependencies": {
+                                "ansi-regex": {
+                                  "version": "2.0.0",
+                                  "from": "ansi-regex@>=2.0.0 <3.0.0",
+                                  "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
+                                }
+                              }
+                            },
+                            "supports-color": {
+                              "version": "2.0.0",
+                              "from": "supports-color@>=2.0.0 <3.0.0",
+                              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz"
+                            }
+                          }
+                        },
+                        "js-tokens": {
+                          "version": "2.0.0",
+                          "from": "js-tokens@>=2.0.0 <3.0.0",
+                          "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-2.0.0.tgz"
+                        }
+                      }
+                    },
+                    "babel-messages": {
+                      "version": "6.8.0",
+                      "from": "babel-messages@>=6.8.0 <7.0.0",
+                      "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.8.0.tgz"
+                    },
+                    "babylon": {
+                      "version": "6.8.2",
+                      "from": "babylon@>=6.7.0 <7.0.0",
+                      "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.8.2.tgz"
+                    },
+                    "debug": {
+                      "version": "2.2.0",
+                      "from": "debug@>=2.2.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+                      "dependencies": {
+                        "ms": {
+                          "version": "0.7.1",
+                          "from": "ms@0.7.1",
+                          "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+                        }
+                      }
+                    },
+                    "globals": {
+                      "version": "8.18.0",
+                      "from": "globals@>=8.3.0 <9.0.0",
+                      "resolved": "https://registry.npmjs.org/globals/-/globals-8.18.0.tgz"
+                    },
+                    "invariant": {
+                      "version": "2.2.1",
+                      "from": "invariant@>=2.2.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.1.tgz",
+                      "dependencies": {
+                        "loose-envify": {
+                          "version": "1.2.0",
+                          "from": "loose-envify@>=1.0.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.2.0.tgz",
+                          "dependencies": {
+                            "js-tokens": {
+                              "version": "1.0.3",
+                              "from": "js-tokens@>=1.0.1 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-1.0.3.tgz"
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                },
+                "esutils": {
+                  "version": "2.0.2",
+                  "from": "esutils@>=2.0.2 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz"
+                },
+                "to-fast-properties": {
+                  "version": "1.0.2",
+                  "from": "to-fast-properties@>=1.0.1 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.2.tgz"
+                }
+              }
+            }
+          }
+        },
+        "json-stringify-safe": {
+          "version": "5.0.1",
+          "from": "json-stringify-safe@>=5.0.1 <6.0.0",
+          "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
+        }
+      }
+    },
+    "cookie": {
+      "version": "0.2.4",
+      "from": "cookie@>=0.2.3 <0.3.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.2.4.tgz"
+    },
+    "cookie-parser": {
+      "version": "1.3.5",
+      "from": "cookie-parser@1.3.5",
+      "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.3.5.tgz",
+      "dependencies": {
+        "cookie": {
+          "version": "0.1.3",
+          "from": "cookie@0.1.3",
+          "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz"
+        },
+        "cookie-signature": {
+          "version": "1.0.6",
+          "from": "cookie-signature@1.0.6",
+          "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz"
+        }
+      }
+    },
+    "csurf": {
+      "version": "1.9.0",
+      "from": "csurf@>=1.8.3 <2.0.0",
+      "resolved": "https://registry.npmjs.org/csurf/-/csurf-1.9.0.tgz",
+      "dependencies": {
+        "cookie": {
+          "version": "0.3.1",
+          "from": "cookie@0.3.1",
+          "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz"
+        },
+        "cookie-signature": {
+          "version": "1.0.6",
+          "from": "cookie-signature@1.0.6",
+          "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz"
+        },
+        "csrf": {
+          "version": "3.0.3",
+          "from": "csrf@>=3.0.3 <3.1.0",
+          "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.0.3.tgz",
+          "dependencies": {
+            "base64-url": {
+              "version": "1.2.2",
+              "from": "base64-url@1.2.2",
+              "resolved": "https://registry.npmjs.org/base64-url/-/base64-url-1.2.2.tgz"
+            },
+            "rndm": {
+              "version": "1.2.0",
+              "from": "rndm@1.2.0",
+              "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz"
+            },
+            "tsscmp": {
+              "version": "1.0.5",
+              "from": "tsscmp@1.0.5",
+              "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz"
+            },
+            "uid-safe": {
+              "version": "2.1.1",
+              "from": "uid-safe@2.1.1",
+              "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.1.tgz",
+              "dependencies": {
+                "random-bytes": {
+                  "version": "1.0.0",
+                  "from": "random-bytes@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz"
+                }
+              }
+            }
+          }
+        },
+        "http-errors": {
+          "version": "1.5.0",
+          "from": "http-errors@>=1.5.0 <1.6.0",
+          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.0.tgz",
+          "dependencies": {
+            "inherits": {
+              "version": "2.0.1",
+              "from": "inherits@2.0.1",
+              "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+            },
+            "setprototypeof": {
+              "version": "1.0.1",
+              "from": "setprototypeof@1.0.1",
+              "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.1.tgz"
+            },
+            "statuses": {
+              "version": "1.3.0",
+              "from": "statuses@>=1.3.0 <2.0.0",
+              "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.0.tgz"
+            }
+          }
+        }
+      }
+    },
+    "dateformat": {
+      "version": "1.0.4-1.2.3",
+      "from": "dateformat@1.0.4-1.2.3",
+      "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.4-1.2.3.tgz"
+    },
+    "express": {
+      "version": "4.13.0",
+      "from": "express@4.13.0",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.13.0.tgz",
+      "dependencies": {
+        "accepts": {
+          "version": "1.2.13",
+          "from": "accepts@>=1.2.9 <1.3.0",
+          "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz",
+          "dependencies": {
+            "mime-types": {
+              "version": "2.1.11",
+              "from": "mime-types@>=2.1.6 <2.2.0",
+              "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz",
+              "dependencies": {
+                "mime-db": {
+                  "version": "1.23.0",
+                  "from": "mime-db@>=1.23.0 <1.24.0",
+                  "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz"
+                }
+              }
+            },
+            "negotiator": {
+              "version": "0.5.3",
+              "from": "negotiator@0.5.3",
+              "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz"
+            }
+          }
+        },
+        "array-flatten": {
+          "version": "1.1.0",
+          "from": "array-flatten@1.1.0",
+          "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.0.tgz"
+        },
+        "content-disposition": {
+          "version": "0.5.0",
+          "from": "content-disposition@0.5.0",
+          "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.0.tgz"
+        },
+        "content-type": {
+          "version": "1.0.2",
+          "from": "content-type@>=1.0.1 <1.1.0",
+          "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz"
+        },
+        "cookie": {
+          "version": "0.1.3",
+          "from": "cookie@0.1.3",
+          "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz"
+        },
+        "cookie-signature": {
+          "version": "1.0.6",
+          "from": "cookie-signature@1.0.6",
+          "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz"
+        },
+        "debug": {
+          "version": "2.2.0",
+          "from": "debug@>=2.2.0 <2.3.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+          "dependencies": {
+            "ms": {
+              "version": "0.7.1",
+              "from": "ms@0.7.1",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+            }
+          }
+        },
+        "depd": {
+          "version": "1.0.1",
+          "from": "depd@>=1.0.1 <1.1.0",
+          "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz"
+        },
+        "escape-html": {
+          "version": "1.0.2",
+          "from": "escape-html@1.0.2",
+          "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.2.tgz"
+        },
+        "etag": {
+          "version": "1.7.0",
+          "from": "etag@>=1.7.0 <1.8.0",
+          "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz"
+        },
+        "finalhandler": {
+          "version": "0.4.0",
+          "from": "finalhandler@0.4.0",
+          "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.0.tgz",
+          "dependencies": {
+            "unpipe": {
+              "version": "1.0.0",
+              "from": "unpipe@>=1.0.0 <1.1.0",
+              "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
+            }
+          }
+        },
+        "fresh": {
+          "version": "0.3.0",
+          "from": "fresh@0.3.0",
+          "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz"
+        },
+        "merge-descriptors": {
+          "version": "1.0.0",
+          "from": "merge-descriptors@1.0.0",
+          "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.0.tgz"
+        },
+        "methods": {
+          "version": "1.1.2",
+          "from": "methods@>=1.1.1 <1.2.0",
+          "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz"
+        },
+        "on-finished": {
+          "version": "2.3.0",
+          "from": "on-finished@>=2.3.0 <2.4.0",
+          "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+          "dependencies": {
+            "ee-first": {
+              "version": "1.1.1",
+              "from": "ee-first@1.1.1",
+              "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
+            }
+          }
+        },
+        "parseurl": {
+          "version": "1.3.1",
+          "from": "parseurl@>=1.3.0 <1.4.0",
+          "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz"
+        },
+        "path-to-regexp": {
+          "version": "0.1.6",
+          "from": "path-to-regexp@0.1.6",
+          "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.6.tgz"
+        },
+        "proxy-addr": {
+          "version": "1.0.10",
+          "from": "proxy-addr@>=1.0.8 <1.1.0",
+          "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.10.tgz",
+          "dependencies": {
+            "forwarded": {
+              "version": "0.1.0",
+              "from": "forwarded@>=0.1.0 <0.2.0",
+              "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz"
+            },
+            "ipaddr.js": {
+              "version": "1.0.5",
+              "from": "ipaddr.js@1.0.5",
+              "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.5.tgz"
+            }
+          }
+        },
+        "qs": {
+          "version": "2.4.2",
+          "from": "qs@2.4.2",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-2.4.2.tgz"
+        },
+        "range-parser": {
+          "version": "1.0.3",
+          "from": "range-parser@>=1.0.2 <1.1.0",
+          "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz"
+        },
+        "send": {
+          "version": "0.13.0",
+          "from": "send@0.13.0",
+          "resolved": "https://registry.npmjs.org/send/-/send-0.13.0.tgz",
+          "dependencies": {
+            "destroy": {
+              "version": "1.0.3",
+              "from": "destroy@1.0.3",
+              "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz"
+            },
+            "http-errors": {
+              "version": "1.3.1",
+              "from": "http-errors@>=1.3.1 <1.4.0",
+              "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz",
+              "dependencies": {
+                "inherits": {
+                  "version": "2.0.1",
+                  "from": "inherits@>=2.0.1 <2.1.0",
+                  "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                }
+              }
+            },
+            "mime": {
+              "version": "1.3.4",
+              "from": "mime@1.3.4",
+              "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz"
+            },
+            "ms": {
+              "version": "0.7.1",
+              "from": "ms@0.7.1",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+            },
+            "statuses": {
+              "version": "1.2.1",
+              "from": "statuses@>=1.2.1 <1.3.0",
+              "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz"
+            }
+          }
+        },
+        "serve-static": {
+          "version": "1.10.3",
+          "from": "serve-static@>=1.10.0 <1.11.0",
+          "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.10.3.tgz",
+          "dependencies": {
+            "escape-html": {
+              "version": "1.0.3",
+              "from": "escape-html@>=1.0.3 <1.1.0",
+              "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz"
+            },
+            "send": {
+              "version": "0.13.2",
+              "from": "send@0.13.2",
+              "resolved": "https://registry.npmjs.org/send/-/send-0.13.2.tgz",
+              "dependencies": {
+                "depd": {
+                  "version": "1.1.0",
+                  "from": "depd@>=1.1.0 <1.2.0",
+                  "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz"
+                },
+                "destroy": {
+                  "version": "1.0.4",
+                  "from": "destroy@>=1.0.4 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz"
+                },
+                "http-errors": {
+                  "version": "1.3.1",
+                  "from": "http-errors@>=1.3.1 <1.4.0",
+                  "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz",
+                  "dependencies": {
+                    "inherits": {
+                      "version": "2.0.1",
+                      "from": "inherits@>=2.0.1 <2.1.0",
+                      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                    }
+                  }
+                },
+                "mime": {
+                  "version": "1.3.4",
+                  "from": "mime@1.3.4",
+                  "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz"
+                },
+                "ms": {
+                  "version": "0.7.1",
+                  "from": "ms@0.7.1",
+                  "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+                },
+                "statuses": {
+                  "version": "1.2.1",
+                  "from": "statuses@>=1.2.1 <1.3.0",
+                  "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz"
+                }
+              }
+            }
+          }
+        },
+        "type-is": {
+          "version": "1.6.13",
+          "from": "type-is@>=1.6.3 <1.7.0",
+          "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.13.tgz",
+          "dependencies": {
+            "media-typer": {
+              "version": "0.3.0",
+              "from": "media-typer@0.3.0",
+              "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
+            },
+            "mime-types": {
+              "version": "2.1.11",
+              "from": "mime-types@>=2.1.11 <2.2.0",
+              "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz",
+              "dependencies": {
+                "mime-db": {
+                  "version": "1.23.0",
+                  "from": "mime-db@>=1.23.0 <1.24.0",
+                  "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz"
+                }
+              }
+            }
+          }
+        },
+        "vary": {
+          "version": "1.0.1",
+          "from": "vary@>=1.0.0 <1.1.0",
+          "resolved": "https://registry.npmjs.org/vary/-/vary-1.0.1.tgz"
+        },
+        "utils-merge": {
+          "version": "1.0.0",
+          "from": "utils-merge@1.0.0",
+          "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz"
+        }
+      }
+    },
+    "express-session": {
+      "version": "1.11.3",
+      "from": "express-session@1.11.3",
+      "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.11.3.tgz",
+      "dependencies": {
+        "cookie": {
+          "version": "0.1.3",
+          "from": "cookie@0.1.3",
+          "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz"
+        },
+        "cookie-signature": {
+          "version": "1.0.6",
+          "from": "cookie-signature@1.0.6",
+          "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz"
+        },
+        "crc": {
+          "version": "3.3.0",
+          "from": "crc@3.3.0",
+          "resolved": "https://registry.npmjs.org/crc/-/crc-3.3.0.tgz"
+        },
+        "debug": {
+          "version": "2.2.0",
+          "from": "debug@>=2.2.0 <2.3.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+          "dependencies": {
+            "ms": {
+              "version": "0.7.1",
+              "from": "ms@0.7.1",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+            }
+          }
+        },
+        "depd": {
+          "version": "1.0.1",
+          "from": "depd@>=1.0.1 <1.1.0",
+          "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz"
+        },
+        "on-headers": {
+          "version": "1.0.1",
+          "from": "on-headers@>=1.0.0 <1.1.0",
+          "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz"
+        },
+        "parseurl": {
+          "version": "1.3.1",
+          "from": "parseurl@>=1.3.0 <1.4.0",
+          "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz"
+        },
+        "uid-safe": {
+          "version": "2.0.0",
+          "from": "uid-safe@>=2.0.0 <2.1.0",
+          "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.0.0.tgz",
+          "dependencies": {
+            "base64-url": {
+              "version": "1.2.1",
+              "from": "base64-url@1.2.1",
+              "resolved": "https://registry.npmjs.org/base64-url/-/base64-url-1.2.1.tgz"
+            }
+          }
+        },
+        "utils-merge": {
+          "version": "1.0.0",
+          "from": "utils-merge@1.0.0",
+          "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz"
+        }
+      }
+    },
+    "grunt": {
+      "version": "0.4.5",
+      "from": "grunt@>=0.4.5 <0.5.0",
+      "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
+      "dependencies": {
+        "async": {
+          "version": "0.1.22",
+          "from": "async@>=0.1.22 <0.2.0",
+          "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz"
+        },
+        "coffee-script": {
+          "version": "1.3.3",
+          "from": "coffee-script@>=1.3.3 <1.4.0",
+          "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz"
+        },
+        "colors": {
+          "version": "0.6.2",
+          "from": "colors@>=0.6.2 <0.7.0",
+          "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz"
+        },
+        "dateformat": {
+          "version": "1.0.2-1.2.3",
+          "from": "dateformat@1.0.2-1.2.3",
+          "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz"
+        },
+        "eventemitter2": {
+          "version": "0.4.14",
+          "from": "eventemitter2@>=0.4.13 <0.5.0",
+          "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz"
+        },
+        "findup-sync": {
+          "version": "0.1.3",
+          "from": "findup-sync@>=0.1.2 <0.2.0",
+          "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
+          "dependencies": {
+            "glob": {
+              "version": "3.2.11",
+              "from": "glob@>=3.2.9 <3.3.0",
+              "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+              "dependencies": {
+                "inherits": {
+                  "version": "2.0.1",
+                  "from": "inherits@>=2.0.0 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                },
+                "minimatch": {
+                  "version": "0.3.0",
+                  "from": "minimatch@>=0.3.0 <0.4.0",
+                  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+                  "dependencies": {
+                    "lru-cache": {
+                      "version": "2.7.3",
+                      "from": "lru-cache@>=2.0.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz"
+                    },
+                    "sigmund": {
+                      "version": "1.0.1",
+                      "from": "sigmund@>=1.0.0 <1.1.0",
+                      "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+                    }
+                  }
+                }
+              }
+            },
+            "lodash": {
+              "version": "2.4.2",
+              "from": "lodash@>=2.4.1 <2.5.0",
+              "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+            }
+          }
+        },
+        "glob": {
+          "version": "3.1.21",
+          "from": "glob@>=3.1.21 <3.2.0",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
+          "dependencies": {
+            "graceful-fs": {
+              "version": "1.2.3",
+              "from": "graceful-fs@>=1.2.0 <1.3.0",
+              "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz"
+            },
+            "inherits": {
+              "version": "1.0.2",
+              "from": "inherits@>=1.0.0 <2.0.0",
+              "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz"
+            }
+          }
+        },
+        "hooker": {
+          "version": "0.2.3",
+          "from": "hooker@>=0.2.3 <0.3.0",
+          "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+        },
+        "iconv-lite": {
+          "version": "0.2.11",
+          "from": "iconv-lite@>=0.2.11 <0.3.0",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz"
+        },
+        "minimatch": {
+          "version": "0.2.14",
+          "from": "minimatch@>=0.2.9 <0.3.0",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+          "dependencies": {
+            "lru-cache": {
+              "version": "2.7.3",
+              "from": "lru-cache@>=2.0.0 <3.0.0",
+              "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz"
+            },
+            "sigmund": {
+              "version": "1.0.1",
+              "from": "sigmund@>=1.0.0 <1.1.0",
+              "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+            }
+          }
+        },
+        "nopt": {
+          "version": "1.0.10",
+          "from": "nopt@>=1.0.10 <1.1.0",
+          "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+          "dependencies": {
+            "abbrev": {
+              "version": "1.0.9",
+              "from": "abbrev@>=1.0.0 <2.0.0",
+              "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz"
+            }
+          }
+        },
+        "rimraf": {
+          "version": "2.2.8",
+          "from": "rimraf@>=2.2.8 <2.3.0",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
+        },
+        "lodash": {
+          "version": "0.9.2",
+          "from": "lodash@>=0.9.2 <0.10.0",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz"
+        },
+        "underscore.string": {
+          "version": "2.2.1",
+          "from": "underscore.string@>=2.2.1 <2.3.0",
+          "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz"
+        },
+        "which": {
+          "version": "1.0.9",
+          "from": "which@>=1.0.5 <1.1.0",
+          "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz"
+        },
+        "js-yaml": {
+          "version": "2.0.5",
+          "from": "js-yaml@>=2.0.5 <2.1.0",
+          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
+          "dependencies": {
+            "argparse": {
+              "version": "0.1.16",
+              "from": "argparse@>=0.1.11 <0.2.0",
+              "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
+              "dependencies": {
+                "underscore": {
+                  "version": "1.7.0",
+                  "from": "underscore@>=1.7.0 <1.8.0",
+                  "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz"
+                },
+                "underscore.string": {
+                  "version": "2.4.0",
+                  "from": "underscore.string@>=2.4.0 <2.5.0",
+                  "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz"
+                }
+              }
+            },
+            "esprima": {
+              "version": "1.0.4",
+              "from": "esprima@>=1.0.2 <1.1.0",
+              "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz"
+            }
+          }
+        },
+        "exit": {
+          "version": "0.1.2",
+          "from": "exit@>=0.1.1 <0.2.0",
+          "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+        },
+        "getobject": {
+          "version": "0.1.0",
+          "from": "getobject@>=0.1.0 <0.2.0",
+          "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz"
+        },
+        "grunt-legacy-util": {
+          "version": "0.2.0",
+          "from": "grunt-legacy-util@>=0.2.0 <0.3.0",
+          "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz"
+        },
+        "grunt-legacy-log": {
+          "version": "0.1.3",
+          "from": "grunt-legacy-log@>=0.1.0 <0.2.0",
+          "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz",
+          "dependencies": {
+            "grunt-legacy-log-utils": {
+              "version": "0.1.1",
+              "from": "grunt-legacy-log-utils@>=0.1.1 <0.2.0",
+              "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz"
+            },
+            "lodash": {
+              "version": "2.4.2",
+              "from": "lodash@>=2.4.1 <2.5.0",
+              "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+            },
+            "underscore.string": {
+              "version": "2.3.3",
+              "from": "underscore.string@>=2.3.3 <2.4.0",
+              "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz"
+            }
+          }
+        }
+      }
+    },
+    "heapdump": {
+      "version": "0.3.7",
+      "from": "heapdump@>=0.3.7 <0.4.0",
+      "resolved": "https://registry.npmjs.org/heapdump/-/heapdump-0.3.7.tgz"
+    },
+    "http-proxy": {
+      "version": "1.14.0",
+      "from": "http-proxy@>=1.8.1 <2.0.0",
+      "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.14.0.tgz",
+      "dependencies": {
+        "eventemitter3": {
+          "version": "1.2.0",
+          "from": "eventemitter3@>=1.0.0 <2.0.0",
+          "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz"
+        },
+        "requires-port": {
+          "version": "1.0.0",
+          "from": "requires-port@>=1.0.0 <2.0.0",
+          "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz"
+        }
+      }
+    },
+    "jade": {
+      "version": "1.3.1",
+      "from": "jade@>=1.3.1 <1.4.0",
+      "resolved": "https://registry.npmjs.org/jade/-/jade-1.3.1.tgz",
+      "dependencies": {
+        "commander": {
+          "version": "2.1.0",
+          "from": "commander@2.1.0",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz"
+        },
+        "mkdirp": {
+          "version": "0.3.5",
+          "from": "mkdirp@>=0.3.5 <0.4.0",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz"
+        },
+        "transformers": {
+          "version": "2.1.0",
+          "from": "transformers@2.1.0",
+          "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz",
+          "dependencies": {
+            "promise": {
+              "version": "2.0.0",
+              "from": "promise@>=2.0.0 <2.1.0",
+              "resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz",
+              "dependencies": {
+                "is-promise": {
+                  "version": "1.0.1",
+                  "from": "is-promise@>=1.0.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz"
+                }
+              }
+            },
+            "css": {
+              "version": "1.0.8",
+              "from": "css@>=1.0.8 <1.1.0",
+              "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz",
+              "dependencies": {
+                "css-parse": {
+                  "version": "1.0.4",
+                  "from": "css-parse@1.0.4",
+                  "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.0.4.tgz"
+                },
+                "css-stringify": {
+                  "version": "1.0.5",
+                  "from": "css-stringify@1.0.5",
+                  "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz"
+                }
+              }
+            },
+            "uglify-js": {
+              "version": "2.2.5",
+              "from": "uglify-js@>=2.2.5 <2.3.0",
+              "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz",
+              "dependencies": {
+                "source-map": {
+                  "version": "0.1.43",
+                  "from": "source-map@>=0.1.7 <0.2.0",
+                  "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+                  "dependencies": {
+                    "amdefine": {
+                      "version": "1.0.0",
+                      "from": "amdefine@>=0.0.4",
+                      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz"
+                    }
+                  }
+                },
+                "optimist": {
+                  "version": "0.3.7",
+                  "from": "optimist@>=0.3.5 <0.4.0",
+                  "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz",
+                  "dependencies": {
+                    "wordwrap": {
+                      "version": "0.0.3",
+                      "from": "wordwrap@>=0.0.2 <0.1.0",
+                      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz"
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "character-parser": {
+          "version": "1.2.0",
+          "from": "character-parser@1.2.0",
+          "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.0.tgz"
+        },
+        "monocle": {
+          "version": "1.1.51",
+          "from": "monocle@1.1.51",
+          "resolved": "https://registry.npmjs.org/monocle/-/monocle-1.1.51.tgz",
+          "dependencies": {
+            "readdirp": {
+              "version": "0.2.5",
+              "from": "readdirp@>=0.2.3 <0.3.0",
+              "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-0.2.5.tgz",
+              "dependencies": {
+                "minimatch": {
+                  "version": "3.0.2",
+                  "from": "minimatch@>=0.2.4",
+                  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz",
+                  "dependencies": {
+                    "brace-expansion": {
+                      "version": "1.1.5",
+                      "from": "brace-expansion@>=1.0.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.5.tgz",
+                      "dependencies": {
+                        "balanced-match": {
+                          "version": "0.4.1",
+                          "from": "balanced-match@>=0.4.1 <0.5.0",
+                          "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz"
+                        },
+                        "concat-map": {
+                          "version": "0.0.1",
+                          "from": "concat-map@0.0.1",
+                          "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "with": {
+          "version": "3.0.1",
+          "from": "with@>=3.0.0 <3.1.0",
+          "resolved": "https://registry.npmjs.org/with/-/with-3.0.1.tgz",
+          "dependencies": {
+            "uglify-js": {
+              "version": "2.4.24",
+              "from": "uglify-js@>=2.4.12 <2.5.0",
+              "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz",
+              "dependencies": {
+                "async": {
+                  "version": "0.2.10",
+                  "from": "async@>=0.2.6 <0.3.0",
+                  "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz"
+                },
+                "source-map": {
+                  "version": "0.1.34",
+                  "from": "source-map@0.1.34",
+                  "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz",
+                  "dependencies": {
+                    "amdefine": {
+                      "version": "1.0.0",
+                      "from": "amdefine@>=0.0.4",
+                      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz"
+                    }
+                  }
+                },
+                "uglify-to-browserify": {
+                  "version": "1.0.2",
+                  "from": "uglify-to-browserify@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz"
+                },
+                "yargs": {
+                  "version": "3.5.4",
+                  "from": "yargs@>=3.5.4 <3.6.0",
+                  "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz",
+                  "dependencies": {
+                    "camelcase": {
+                      "version": "1.2.1",
+                      "from": "camelcase@>=1.0.2 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz"
+                    },
+                    "decamelize": {
+                      "version": "1.2.0",
+                      "from": "decamelize@>=1.0.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz"
+                    },
+                    "window-size": {
+                      "version": "0.1.0",
+                      "from": "window-size@0.1.0",
+                      "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz"
+                    },
+                    "wordwrap": {
+                      "version": "0.0.2",
+                      "from": "wordwrap@0.0.2",
+                      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz"
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "constantinople": {
+          "version": "2.0.1",
+          "from": "constantinople@>=2.0.0 <2.1.0",
+          "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-2.0.1.tgz",
+          "dependencies": {
+            "uglify-js": {
+              "version": "2.4.24",
+              "from": "uglify-js@>=2.4.12 <2.5.0",
+              "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz",
+              "dependencies": {
+                "async": {
+                  "version": "0.2.10",
+                  "from": "async@>=0.2.6 <0.3.0",
+                  "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz"
+                },
+                "source-map": {
+                  "version": "0.1.34",
+                  "from": "source-map@0.1.34",
+                  "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz",
+                  "dependencies": {
+                    "amdefine": {
+                      "version": "1.0.0",
+                      "from": "amdefine@>=0.0.4",
+                      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz"
+                    }
+                  }
+                },
+                "uglify-to-browserify": {
+                  "version": "1.0.2",
+                  "from": "uglify-to-browserify@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz"
+                },
+                "yargs": {
+                  "version": "3.5.4",
+                  "from": "yargs@>=3.5.4 <3.6.0",
+                  "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz",
+                  "dependencies": {
+                    "camelcase": {
+                      "version": "1.2.1",
+                      "from": "camelcase@>=1.0.2 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz"
+                    },
+                    "decamelize": {
+                      "version": "1.2.0",
+                      "from": "decamelize@>=1.0.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz"
+                    },
+                    "window-size": {
+                      "version": "0.1.0",
+                      "from": "window-size@0.1.0",
+                      "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz"
+                    },
+                    "wordwrap": {
+                      "version": "0.0.2",
+                      "from": "wordwrap@0.0.2",
+                      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz"
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "ldapjs": {
+      "version": "1.0.0",
+      "from": "ldapjs@>=1.0.0 <2.0.0",
+      "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.0.tgz",
+      "dependencies": {
+        "asn1": {
+          "version": "0.2.3",
+          "from": "asn1@0.2.3",
+          "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz"
+        },
+        "assert-plus": {
+          "version": "0.1.5",
+          "from": "assert-plus@0.1.5",
+          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz"
+        },
+        "bunyan": {
+          "version": "1.5.1",
+          "from": "bunyan@1.5.1",
+          "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz",
+          "dependencies": {
+            "mv": {
+              "version": "2.1.1",
+              "from": "mv@>=2.0.0 <3.0.0",
+              "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
+              "dependencies": {
+                "mkdirp": {
+                  "version": "0.5.1",
+                  "from": "mkdirp@>=0.5.1 <0.6.0",
+                  "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+                  "dependencies": {
+                    "minimist": {
+                      "version": "0.0.8",
+                      "from": "minimist@0.0.8",
+                      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
+                    }
+                  }
+                },
+                "ncp": {
+                  "version": "2.0.0",
+                  "from": "ncp@>=2.0.0 <2.1.0",
+                  "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz"
+                },
+                "rimraf": {
+                  "version": "2.4.5",
+                  "from": "rimraf@>=2.4.0 <2.5.0",
+                  "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
+                  "dependencies": {
+                    "glob": {
+                      "version": "6.0.4",
+                      "from": "glob@>=6.0.1 <7.0.0",
+                      "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
+                      "dependencies": {
+                        "inflight": {
+                          "version": "1.0.5",
+                          "from": "inflight@>=1.0.4 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz",
+                          "dependencies": {
+                            "wrappy": {
+                              "version": "1.0.2",
+                              "from": "wrappy@>=1.0.0 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+                            }
+                          }
+                        },
+                        "inherits": {
+                          "version": "2.0.1",
+                          "from": "inherits@>=2.0.0 <3.0.0",
+                          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                        },
+                        "minimatch": {
+                          "version": "3.0.2",
+                          "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0",
+                          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz",
+                          "dependencies": {
+                            "brace-expansion": {
+                              "version": "1.1.5",
+                              "from": "brace-expansion@>=1.0.0 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.5.tgz",
+                              "dependencies": {
+                                "balanced-match": {
+                                  "version": "0.4.1",
+                                  "from": "balanced-match@>=0.4.1 <0.5.0",
+                                  "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz"
+                                },
+                                "concat-map": {
+                                  "version": "0.0.1",
+                                  "from": "concat-map@0.0.1",
+                                  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+                                }
+                              }
+                            }
+                          }
+                        },
+                        "path-is-absolute": {
+                          "version": "1.0.0",
+                          "from": "path-is-absolute@>=1.0.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz"
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            },
+            "safe-json-stringify": {
+              "version": "1.0.3",
+              "from": "safe-json-stringify@>=1.0.0 <2.0.0",
+              "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.3.tgz"
+            }
+          }
+        },
+        "dashdash": {
+          "version": "1.10.1",
+          "from": "dashdash@1.10.1",
+          "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.10.1.tgz"
+        },
+        "backoff": {
+          "version": "2.4.1",
+          "from": "backoff@2.4.1",
+          "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.4.1.tgz",
+          "dependencies": {
+            "precond": {
+              "version": "0.2.3",
+              "from": "precond@>=0.2.0 <0.3.0",
+              "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz"
+            }
+          }
+        },
+        "ldap-filter": {
+          "version": "0.2.2",
+          "from": "ldap-filter@0.2.2",
+          "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.2.2.tgz"
+        },
+        "once": {
+          "version": "1.3.2",
+          "from": "once@1.3.2",
+          "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
+          "dependencies": {
+            "wrappy": {
+              "version": "1.0.2",
+              "from": "wrappy@>=1.0.0 <2.0.0",
+              "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+            }
+          }
+        },
+        "vasync": {
+          "version": "1.6.3",
+          "from": "vasync@1.6.3",
+          "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.3.tgz"
+        },
+        "verror": {
+          "version": "1.6.0",
+          "from": "verror@1.6.0",
+          "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz",
+          "dependencies": {
+            "extsprintf": {
+              "version": "1.2.0",
+              "from": "extsprintf@1.2.0",
+              "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz"
+            }
+          }
+        },
+        "dtrace-provider": {
+          "version": "0.6.0",
+          "from": "dtrace-provider@>=0.6.0 <0.7.0",
+          "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz",
+          "dependencies": {
+            "nan": {
+              "version": "2.3.5",
+              "from": "nan@>=2.0.8 <3.0.0",
+              "resolved": "https://registry.npmjs.org/nan/-/nan-2.3.5.tgz"
+            }
+          }
+        }
+      }
+    },
+    "lodash": {
+      "version": "4.13.1",
+      "from": "lodash@>=4.13.1 <5.0.0",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.13.1.tgz"
+    },
+    "logger-sharelatex": {
+      "version": "1.3.1",
+      "from": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.3.1",
+      "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#bf413ec621a000cf0e08c939de38d5e24541a08c",
+      "dependencies": {
+        "bunyan": {
+          "version": "1.5.1",
+          "from": "bunyan@1.5.1",
+          "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz",
+          "dependencies": {
+            "dtrace-provider": {
+              "version": "0.6.0",
+              "from": "dtrace-provider@>=0.6.0 <0.7.0",
+              "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz",
+              "dependencies": {
+                "nan": {
+                  "version": "2.3.5",
+                  "from": "nan@>=2.0.8 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/nan/-/nan-2.3.5.tgz"
+                }
+              }
+            },
+            "mv": {
+              "version": "2.1.1",
+              "from": "mv@>=2.0.0 <3.0.0",
+              "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
+              "dependencies": {
+                "mkdirp": {
+                  "version": "0.5.1",
+                  "from": "mkdirp@>=0.5.1 <0.6.0",
+                  "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+                  "dependencies": {
+                    "minimist": {
+                      "version": "0.0.8",
+                      "from": "minimist@0.0.8",
+                      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
+                    }
+                  }
+                },
+                "ncp": {
+                  "version": "2.0.0",
+                  "from": "ncp@>=2.0.0 <2.1.0",
+                  "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz"
+                },
+                "rimraf": {
+                  "version": "2.4.5",
+                  "from": "rimraf@>=2.4.0 <2.5.0",
+                  "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
+                  "dependencies": {
+                    "glob": {
+                      "version": "6.0.4",
+                      "from": "glob@>=6.0.1 <7.0.0",
+                      "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
+                      "dependencies": {
+                        "inflight": {
+                          "version": "1.0.5",
+                          "from": "inflight@>=1.0.4 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz",
+                          "dependencies": {
+                            "wrappy": {
+                              "version": "1.0.2",
+                              "from": "wrappy@>=1.0.0 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+                            }
+                          }
+                        },
+                        "inherits": {
+                          "version": "2.0.1",
+                          "from": "inherits@>=2.0.0 <3.0.0",
+                          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                        },
+                        "minimatch": {
+                          "version": "3.0.2",
+                          "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0",
+                          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz",
+                          "dependencies": {
+                            "brace-expansion": {
+                              "version": "1.1.5",
+                              "from": "brace-expansion@>=1.0.0 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.5.tgz",
+                              "dependencies": {
+                                "balanced-match": {
+                                  "version": "0.4.1",
+                                  "from": "balanced-match@>=0.4.1 <0.5.0",
+                                  "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz"
+                                },
+                                "concat-map": {
+                                  "version": "0.0.1",
+                                  "from": "concat-map@0.0.1",
+                                  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+                                }
+                              }
+                            }
+                          }
+                        },
+                        "once": {
+                          "version": "1.3.3",
+                          "from": "once@>=1.3.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
+                          "dependencies": {
+                            "wrappy": {
+                              "version": "1.0.2",
+                              "from": "wrappy@>=1.0.0 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+                            }
+                          }
+                        },
+                        "path-is-absolute": {
+                          "version": "1.0.0",
+                          "from": "path-is-absolute@>=1.0.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz"
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            },
+            "safe-json-stringify": {
+              "version": "1.0.3",
+              "from": "safe-json-stringify@>=1.0.0 <2.0.0",
+              "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.3.tgz"
+            }
+          }
+        },
+        "coffee-script": {
+          "version": "1.4.0",
+          "from": "coffee-script@1.4.0",
+          "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz"
+        },
+        "raven": {
+          "version": "0.8.1",
+          "from": "raven@>=0.8.0 <0.9.0",
+          "resolved": "https://registry.npmjs.org/raven/-/raven-0.8.1.tgz",
+          "dependencies": {
+            "cookie": {
+              "version": "0.1.0",
+              "from": "cookie@0.1.0",
+              "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz"
+            },
+            "lsmod": {
+              "version": "0.0.3",
+              "from": "lsmod@>=0.0.3 <0.1.0",
+              "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-0.0.3.tgz"
+            },
+            "stack-trace": {
+              "version": "0.0.7",
+              "from": "stack-trace@0.0.7",
+              "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.7.tgz"
+            }
+          }
+        }
+      }
+    },
+    "lynx": {
+      "version": "0.1.1",
+      "from": "lynx@0.1.1",
+      "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz",
+      "dependencies": {
+        "mersenne": {
+          "version": "0.0.3",
+          "from": "mersenne@>=0.0.3 <0.1.0",
+          "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.3.tgz"
+        },
+        "statsd-parser": {
+          "version": "0.0.4",
+          "from": "statsd-parser@>=0.0.4 <0.1.0",
+          "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz"
+        }
+      }
+    },
+    "marked": {
+      "version": "0.3.5",
+      "from": "marked@>=0.3.5 <0.4.0",
+      "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.5.tgz"
+    },
+    "method-override": {
+      "version": "2.3.6",
+      "from": "method-override@>=2.3.3 <3.0.0",
+      "resolved": "https://registry.npmjs.org/method-override/-/method-override-2.3.6.tgz",
+      "dependencies": {
+        "debug": {
+          "version": "2.2.0",
+          "from": "debug@>=2.2.0 <2.3.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+          "dependencies": {
+            "ms": {
+              "version": "0.7.1",
+              "from": "ms@0.7.1",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+            }
+          }
+        },
+        "methods": {
+          "version": "1.1.2",
+          "from": "methods@>=1.1.2 <1.2.0",
+          "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz"
+        },
+        "parseurl": {
+          "version": "1.3.1",
+          "from": "parseurl@>=1.3.0 <1.4.0",
+          "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz"
+        },
+        "vary": {
+          "version": "1.1.0",
+          "from": "vary@>=1.1.0 <1.2.0",
+          "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.0.tgz"
+        }
+      }
+    },
+    "metrics-sharelatex": {
+      "version": "1.3.0",
+      "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.3.0",
+      "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#080c4aeb696edcd5d6d86f202f2c528f0661d7a6",
+      "dependencies": {
+        "coffee-script": {
+          "version": "1.6.0",
+          "from": "coffee-script@1.6.0",
+          "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz"
+        }
+      }
+    },
+    "mimelib": {
+      "version": "0.2.14",
+      "from": "mimelib@0.2.14",
+      "resolved": "https://registry.npmjs.org/mimelib/-/mimelib-0.2.14.tgz",
+      "dependencies": {
+        "encoding": {
+          "version": "0.1.12",
+          "from": "encoding@>=0.1.0 <0.2.0",
+          "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+          "dependencies": {
+            "iconv-lite": {
+              "version": "0.4.13",
+              "from": "iconv-lite@>=0.4.13 <0.5.0",
+              "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz"
+            }
+          }
+        },
+        "addressparser": {
+          "version": "0.2.1",
+          "from": "addressparser@>=0.2.0 <0.3.0",
+          "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-0.2.1.tgz"
+        }
+      }
+    },
+    "mocha": {
+      "version": "1.17.1",
+      "from": "mocha@1.17.1",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.17.1.tgz",
+      "dependencies": {
+        "commander": {
+          "version": "2.0.0",
+          "from": "commander@2.0.0",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz"
+        },
+        "growl": {
+          "version": "1.7.0",
+          "from": "growl@>=1.7.0 <1.8.0",
+          "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz"
+        },
+        "jade": {
+          "version": "0.26.3",
+          "from": "jade@0.26.3",
+          "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
+          "dependencies": {
+            "commander": {
+              "version": "0.6.1",
+              "from": "commander@0.6.1",
+              "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz"
+            },
+            "mkdirp": {
+              "version": "0.3.0",
+              "from": "mkdirp@0.3.0",
+              "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz"
+            }
+          }
+        },
+        "diff": {
+          "version": "1.0.7",
+          "from": "diff@1.0.7",
+          "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz"
+        },
+        "debug": {
+          "version": "2.2.0",
+          "from": "debug@*",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+          "dependencies": {
+            "ms": {
+              "version": "0.7.1",
+              "from": "ms@0.7.1",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+            }
+          }
+        },
+        "mkdirp": {
+          "version": "0.3.5",
+          "from": "mkdirp@0.3.5",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz"
+        },
+        "glob": {
+          "version": "3.2.3",
+          "from": "glob@3.2.3",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz",
+          "dependencies": {
+            "minimatch": {
+              "version": "0.2.14",
+              "from": "minimatch@>=0.2.11 <0.3.0",
+              "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+              "dependencies": {
+                "lru-cache": {
+                  "version": "2.7.3",
+                  "from": "lru-cache@>=2.0.0 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz"
+                },
+                "sigmund": {
+                  "version": "1.0.1",
+                  "from": "sigmund@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+                }
+              }
+            },
+            "graceful-fs": {
+              "version": "2.0.3",
+              "from": "graceful-fs@>=2.0.0 <2.1.0",
+              "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz"
+            },
+            "inherits": {
+              "version": "2.0.1",
+              "from": "inherits@>=2.0.0 <3.0.0",
+              "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+            }
+          }
+        }
+      }
+    },
+    "mongojs": {
+      "version": "0.18.2",
+      "from": "mongojs@0.18.2",
+      "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-0.18.2.tgz",
+      "dependencies": {
+        "thunky": {
+          "version": "0.1.0",
+          "from": "thunky@>=0.1.0 <0.2.0",
+          "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz"
+        },
+        "readable-stream": {
+          "version": "1.1.14",
+          "from": "readable-stream@>=1.1.0 <1.2.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+          "dependencies": {
+            "core-util-is": {
+              "version": "1.0.2",
+              "from": "core-util-is@>=1.0.0 <1.1.0",
+              "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+            },
+            "isarray": {
+              "version": "0.0.1",
+              "from": "isarray@0.0.1",
+              "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+            },
+            "string_decoder": {
+              "version": "0.10.31",
+              "from": "string_decoder@>=0.10.0 <0.11.0",
+              "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+            },
+            "inherits": {
+              "version": "2.0.1",
+              "from": "inherits@>=2.0.1 <2.1.0",
+              "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+            }
+          }
+        },
+        "mongodb": {
+          "version": "1.4.32",
+          "from": "mongodb@1.4.32",
+          "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-1.4.32.tgz",
+          "dependencies": {
+            "bson": {
+              "version": "0.2.22",
+              "from": "bson@>=0.2.0 <0.3.0",
+              "resolved": "https://registry.npmjs.org/bson/-/bson-0.2.22.tgz",
+              "dependencies": {
+                "nan": {
+                  "version": "1.8.4",
+                  "from": "nan@>=1.8.0 <1.9.0",
+                  "resolved": "https://registry.npmjs.org/nan/-/nan-1.8.4.tgz"
+                }
+              }
+            },
+            "kerberos": {
+              "version": "0.0.9",
+              "from": "kerberos@0.0.9",
+              "resolved": "https://registry.npmjs.org/kerberos/-/kerberos-0.0.9.tgz",
+              "dependencies": {
+                "nan": {
+                  "version": "1.6.2",
+                  "from": "nan@1.6.2",
+                  "resolved": "https://registry.npmjs.org/nan/-/nan-1.6.2.tgz"
+                }
+              }
+            },
+            "readable-stream": {
+              "version": "2.1.4",
+              "from": "readable-stream@latest",
+              "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.4.tgz",
+              "dependencies": {
+                "buffer-shims": {
+                  "version": "1.0.0",
+                  "from": "buffer-shims@>=1.0.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz"
+                },
+                "core-util-is": {
+                  "version": "1.0.2",
+                  "from": "core-util-is@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+                },
+                "inherits": {
+                  "version": "2.0.1",
+                  "from": "inherits@>=2.0.1 <2.1.0",
+                  "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                },
+                "isarray": {
+                  "version": "1.0.0",
+                  "from": "isarray@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
+                },
+                "process-nextick-args": {
+                  "version": "1.0.7",
+                  "from": "process-nextick-args@>=1.0.6 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
+                },
+                "string_decoder": {
+                  "version": "0.10.31",
+                  "from": "string_decoder@>=0.10.0 <0.11.0",
+                  "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+                },
+                "util-deprecate": {
+                  "version": "1.0.2",
+                  "from": "util-deprecate@>=1.0.1 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "mongoose": {
+      "version": "4.1.0",
+      "from": "mongoose@4.1.0",
+      "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.1.0.tgz",
+      "dependencies": {
+        "async": {
+          "version": "0.9.0",
+          "from": "async@0.9.0",
+          "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz"
+        },
+        "bson": {
+          "version": "0.3.2",
+          "from": "bson@>=0.3.0 <0.4.0",
+          "resolved": "https://registry.npmjs.org/bson/-/bson-0.3.2.tgz",
+          "dependencies": {
+            "bson-ext": {
+              "version": "0.1.13",
+              "from": "bson-ext@>=0.1.0 <0.2.0",
+              "resolved": "https://registry.npmjs.org/bson-ext/-/bson-ext-0.1.13.tgz",
+              "dependencies": {
+                "bindings": {
+                  "version": "1.2.1",
+                  "from": "bindings@>=1.2.1 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
+                },
+                "nan": {
+                  "version": "2.0.9",
+                  "from": "nan@>=2.0.9 <2.1.0",
+                  "resolved": "https://registry.npmjs.org/nan/-/nan-2.0.9.tgz"
+                }
+              }
+            }
+          }
+        },
+        "hooks-fixed": {
+          "version": "1.1.0",
+          "from": "hooks-fixed@1.1.0",
+          "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-1.1.0.tgz"
+        },
+        "kareem": {
+          "version": "1.0.1",
+          "from": "kareem@1.0.1",
+          "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.0.1.tgz"
+        },
+        "mongodb": {
+          "version": "2.0.34",
+          "from": "mongodb@2.0.34",
+          "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.0.34.tgz",
+          "dependencies": {
+            "mongodb-core": {
+              "version": "1.2.0",
+              "from": "mongodb-core@1.2.0",
+              "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.2.0.tgz",
+              "dependencies": {
+                "bson": {
+                  "version": "0.4.23",
+                  "from": "bson@>=0.4.0 <0.5.0",
+                  "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz"
+                },
+                "kerberos": {
+                  "version": "0.0.21",
+                  "from": "kerberos@>=0.0.0 <0.1.0",
+                  "resolved": "https://registry.npmjs.org/kerberos/-/kerberos-0.0.21.tgz",
+                  "dependencies": {
+                    "nan": {
+                      "version": "2.3.5",
+                      "from": "nan@>=2.3.0 <2.4.0",
+                      "resolved": "https://registry.npmjs.org/nan/-/nan-2.3.5.tgz"
+                    }
+                  }
+                }
+              }
+            },
+            "readable-stream": {
+              "version": "1.0.31",
+              "from": "readable-stream@1.0.31",
+              "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.31.tgz",
+              "dependencies": {
+                "core-util-is": {
+                  "version": "1.0.2",
+                  "from": "core-util-is@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+                },
+                "isarray": {
+                  "version": "0.0.1",
+                  "from": "isarray@0.0.1",
+                  "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+                },
+                "string_decoder": {
+                  "version": "0.10.31",
+                  "from": "string_decoder@>=0.10.0 <0.11.0",
+                  "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+                },
+                "inherits": {
+                  "version": "2.0.1",
+                  "from": "inherits@>=2.0.1 <2.1.0",
+                  "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                }
+              }
+            }
+          }
+        },
+        "mpath": {
+          "version": "0.1.1",
+          "from": "mpath@0.1.1",
+          "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.1.1.tgz"
+        },
+        "mpromise": {
+          "version": "0.5.4",
+          "from": "mpromise@0.5.4",
+          "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.4.tgz"
+        },
+        "mquery": {
+          "version": "1.6.1",
+          "from": "mquery@1.6.1",
+          "resolved": "https://registry.npmjs.org/mquery/-/mquery-1.6.1.tgz",
+          "dependencies": {
+            "bluebird": {
+              "version": "2.9.26",
+              "from": "bluebird@2.9.26",
+              "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.26.tgz"
+            },
+            "debug": {
+              "version": "2.2.0",
+              "from": "debug@2.2.0",
+              "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+              "dependencies": {
+                "ms": {
+                  "version": "0.7.1",
+                  "from": "ms@0.7.1",
+                  "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+                }
+              }
+            }
+          }
+        },
+        "ms": {
+          "version": "0.1.0",
+          "from": "ms@0.1.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-0.1.0.tgz"
+        },
+        "muri": {
+          "version": "1.0.0",
+          "from": "muri@1.0.0",
+          "resolved": "https://registry.npmjs.org/muri/-/muri-1.0.0.tgz"
+        },
+        "regexp-clone": {
+          "version": "0.0.1",
+          "from": "regexp-clone@0.0.1",
+          "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz"
+        },
+        "sliced": {
+          "version": "0.0.5",
+          "from": "sliced@0.0.5",
+          "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz"
+        }
+      }
+    },
+    "multer": {
+      "version": "0.1.8",
+      "from": "multer@>=0.1.8 <0.2.0",
+      "resolved": "https://registry.npmjs.org/multer/-/multer-0.1.8.tgz",
+      "dependencies": {
+        "busboy": {
+          "version": "0.2.13",
+          "from": "busboy@>=0.2.9 <0.3.0",
+          "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.13.tgz",
+          "dependencies": {
+            "dicer": {
+              "version": "0.2.5",
+              "from": "dicer@0.2.5",
+              "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
+              "dependencies": {
+                "streamsearch": {
+                  "version": "0.1.2",
+                  "from": "streamsearch@0.1.2",
+                  "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz"
+                }
+              }
+            },
+            "readable-stream": {
+              "version": "1.1.14",
+              "from": "readable-stream@>=1.1.0 <1.2.0",
+              "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+              "dependencies": {
+                "core-util-is": {
+                  "version": "1.0.2",
+                  "from": "core-util-is@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+                },
+                "isarray": {
+                  "version": "0.0.1",
+                  "from": "isarray@0.0.1",
+                  "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+                },
+                "string_decoder": {
+                  "version": "0.10.31",
+                  "from": "string_decoder@>=0.10.0 <0.11.0",
+                  "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+                },
+                "inherits": {
+                  "version": "2.0.1",
+                  "from": "inherits@>=2.0.1 <2.1.0",
+                  "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                }
+              }
+            }
+          }
+        },
+        "mkdirp": {
+          "version": "0.3.5",
+          "from": "mkdirp@>=0.3.5 <0.4.0",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz"
+        },
+        "qs": {
+          "version": "1.2.2",
+          "from": "qs@>=1.2.2 <1.3.0",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-1.2.2.tgz"
+        },
+        "type-is": {
+          "version": "1.5.7",
+          "from": "type-is@>=1.5.2 <1.6.0",
+          "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.5.7.tgz",
+          "dependencies": {
+            "media-typer": {
+              "version": "0.3.0",
+              "from": "media-typer@0.3.0",
+              "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
+            },
+            "mime-types": {
+              "version": "2.0.14",
+              "from": "mime-types@>=2.0.9 <2.1.0",
+              "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz",
+              "dependencies": {
+                "mime-db": {
+                  "version": "1.12.0",
+                  "from": "mime-db@>=1.12.0 <1.13.0",
+                  "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "node-uuid": {
+      "version": "1.4.1",
+      "from": "node-uuid@1.4.1",
+      "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.1.tgz"
+    },
+    "nodemailer": {
+      "version": "2.1.0",
+      "from": "nodemailer@2.1.0",
+      "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-2.1.0.tgz",
+      "dependencies": {
+        "libmime": {
+          "version": "2.0.0",
+          "from": "libmime@2.0.0",
+          "resolved": "https://registry.npmjs.org/libmime/-/libmime-2.0.0.tgz",
+          "dependencies": {
+            "iconv-lite": {
+              "version": "0.4.13",
+              "from": "iconv-lite@0.4.13",
+              "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz"
+            },
+            "libbase64": {
+              "version": "0.1.0",
+              "from": "libbase64@0.1.0",
+              "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz"
+            },
+            "libqp": {
+              "version": "1.1.0",
+              "from": "libqp@1.1.0",
+              "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz"
+            }
+          }
+        },
+        "mailcomposer": {
+          "version": "3.3.2",
+          "from": "mailcomposer@3.3.2",
+          "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-3.3.2.tgz",
+          "dependencies": {
+            "buildmail": {
+              "version": "3.3.2",
+              "from": "buildmail@3.3.2",
+              "resolved": "https://registry.npmjs.org/buildmail/-/buildmail-3.3.2.tgz",
+              "dependencies": {
+                "addressparser": {
+                  "version": "1.0.0",
+                  "from": "addressparser@1.0.0",
+                  "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.0.tgz"
+                },
+                "libbase64": {
+                  "version": "0.1.0",
+                  "from": "libbase64@0.1.0",
+                  "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz"
+                },
+                "libqp": {
+                  "version": "1.1.0",
+                  "from": "libqp@1.1.0",
+                  "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz"
+                },
+                "nodemailer-fetch": {
+                  "version": "1.2.1",
+                  "from": "nodemailer-fetch@1.2.1",
+                  "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.2.1.tgz"
+                }
+              }
+            }
+          }
+        },
+        "nodemailer-direct-transport": {
+          "version": "2.0.1",
+          "from": "nodemailer-direct-transport@2.0.1",
+          "resolved": "https://registry.npmjs.org/nodemailer-direct-transport/-/nodemailer-direct-transport-2.0.1.tgz",
+          "dependencies": {
+            "smtp-connection": {
+              "version": "2.0.1",
+              "from": "smtp-connection@2.0.1",
+              "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.0.1.tgz"
+            }
+          }
+        },
+        "nodemailer-shared": {
+          "version": "1.0.3",
+          "from": "nodemailer-shared@1.0.3",
+          "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.0.3.tgz",
+          "dependencies": {
+            "nodemailer-fetch": {
+              "version": "1.2.1",
+              "from": "nodemailer-fetch@1.2.1",
+              "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.2.1.tgz"
+            }
+          }
+        },
+        "nodemailer-smtp-pool": {
+          "version": "2.1.0",
+          "from": "nodemailer-smtp-pool@2.1.0",
+          "resolved": "https://registry.npmjs.org/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.1.0.tgz",
+          "dependencies": {
+            "clone": {
+              "version": "1.0.2",
+              "from": "clone@1.0.2",
+              "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz"
+            },
+            "nodemailer-wellknown": {
+              "version": "0.1.7",
+              "from": "nodemailer-wellknown@0.1.7",
+              "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.7.tgz"
+            },
+            "smtp-connection": {
+              "version": "2.0.1",
+              "from": "smtp-connection@2.0.1",
+              "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.0.1.tgz"
+            }
+          }
+        },
+        "nodemailer-smtp-transport": {
+          "version": "2.0.1",
+          "from": "nodemailer-smtp-transport@2.0.1",
+          "resolved": "https://registry.npmjs.org/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.0.1.tgz",
+          "dependencies": {
+            "clone": {
+              "version": "1.0.2",
+              "from": "clone@1.0.2",
+              "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz"
+            },
+            "nodemailer-wellknown": {
+              "version": "0.1.7",
+              "from": "nodemailer-wellknown@0.1.7",
+              "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.7.tgz"
+            },
+            "smtp-connection": {
+              "version": "2.0.1",
+              "from": "smtp-connection@2.0.1",
+              "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.0.1.tgz"
+            }
+          }
+        }
+      }
+    },
+    "nodemailer-sendgrid-transport": {
+      "version": "0.2.0",
+      "from": "nodemailer-sendgrid-transport@>=0.2.0 <0.3.0",
+      "resolved": "https://registry.npmjs.org/nodemailer-sendgrid-transport/-/nodemailer-sendgrid-transport-0.2.0.tgz",
+      "dependencies": {
+        "sendgrid": {
+          "version": "1.9.2",
+          "from": "sendgrid@>=1.8.0 <2.0.0",
+          "resolved": "https://registry.npmjs.org/sendgrid/-/sendgrid-1.9.2.tgz",
+          "dependencies": {
+            "mime": {
+              "version": "1.3.4",
+              "from": "mime@>=1.2.9 <2.0.0",
+              "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz"
+            },
+            "lodash": {
+              "version": "3.10.1",
+              "from": "lodash@>=3.0.1 <4.0.0||>=2.0.0 <3.0.0",
+              "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz"
+            },
+            "smtpapi": {
+              "version": "1.2.0",
+              "from": "smtpapi@>=1.2.0 <2.0.0",
+              "resolved": "https://registry.npmjs.org/smtpapi/-/smtpapi-1.2.0.tgz"
+            }
+          }
+        }
+      }
+    },
+    "nodemailer-ses-transport": {
+      "version": "1.4.0",
+      "from": "nodemailer-ses-transport@>=1.3.0 <2.0.0",
+      "resolved": "https://registry.npmjs.org/nodemailer-ses-transport/-/nodemailer-ses-transport-1.4.0.tgz",
+      "dependencies": {
+        "aws-sdk": {
+          "version": "2.4.2",
+          "from": "aws-sdk@>=2.4.2 <3.0.0",
+          "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.4.2.tgz",
+          "dependencies": {
+            "sax": {
+              "version": "1.1.5",
+              "from": "sax@1.1.5",
+              "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.5.tgz"
+            },
+            "xml2js": {
+              "version": "0.4.15",
+              "from": "xml2js@0.4.15",
+              "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.15.tgz"
+            },
+            "xmlbuilder": {
+              "version": "2.6.2",
+              "from": "xmlbuilder@2.6.2",
+              "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.2.tgz",
+              "dependencies": {
+                "lodash": {
+                  "version": "3.5.0",
+                  "from": "lodash@>=3.5.0 <3.6.0",
+                  "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz"
+                }
+              }
+            },
+            "jmespath": {
+              "version": "0.15.0",
+              "from": "jmespath@0.15.0",
+              "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz"
+            }
+          }
+        }
+      }
+    },
+    "optimist": {
+      "version": "0.6.1",
+      "from": "optimist@0.6.1",
+      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+      "dependencies": {
+        "wordwrap": {
+          "version": "0.0.3",
+          "from": "wordwrap@>=0.0.2 <0.1.0",
+          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz"
+        },
+        "minimist": {
+          "version": "0.0.10",
+          "from": "minimist@>=0.0.1 <0.1.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz"
+        }
+      }
+    },
+    "redback": {
+      "version": "0.4.0",
+      "from": "redback@0.4.0",
+      "resolved": "https://registry.npmjs.org/redback/-/redback-0.4.0.tgz"
+    },
+    "redis": {
+      "version": "0.10.1",
+      "from": "redis@0.10.1",
+      "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.1.tgz"
+    },
+    "redis-sharelatex": {
+      "version": "0.0.9",
+      "from": "redis-sharelatex@0.0.9",
+      "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-0.0.9.tgz",
+      "dependencies": {
+        "chai": {
+          "version": "1.9.1",
+          "from": "chai@1.9.1",
+          "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz",
+          "dependencies": {
+            "assertion-error": {
+              "version": "1.0.0",
+              "from": "assertion-error@1.0.0",
+              "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz"
+            },
+            "deep-eql": {
+              "version": "0.1.3",
+              "from": "deep-eql@0.1.3",
+              "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
+              "dependencies": {
+                "type-detect": {
+                  "version": "0.1.1",
+                  "from": "type-detect@0.1.1",
+                  "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz"
+                }
+              }
+            }
+          }
+        },
+        "coffee-script": {
+          "version": "1.8.0",
+          "from": "coffee-script@1.8.0",
+          "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz",
+          "dependencies": {
+            "mkdirp": {
+              "version": "0.3.5",
+              "from": "mkdirp@>=0.3.5 <0.4.0",
+              "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz"
+            }
+          }
+        },
+        "grunt-contrib-coffee": {
+          "version": "0.11.1",
+          "from": "grunt-contrib-coffee@0.11.1",
+          "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz",
+          "dependencies": {
+            "coffee-script": {
+              "version": "1.7.1",
+              "from": "coffee-script@>=1.7.0 <1.8.0",
+              "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz",
+              "dependencies": {
+                "mkdirp": {
+                  "version": "0.3.5",
+                  "from": "mkdirp@>=0.3.5 <0.4.0",
+                  "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz"
+                }
+              }
+            },
+            "chalk": {
+              "version": "0.5.1",
+              "from": "chalk@>=0.5.0 <0.6.0",
+              "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
+              "dependencies": {
+                "ansi-styles": {
+                  "version": "1.1.0",
+                  "from": "ansi-styles@>=1.1.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz"
+                },
+                "escape-string-regexp": {
+                  "version": "1.0.5",
+                  "from": "escape-string-regexp@>=1.0.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
+                },
+                "has-ansi": {
+                  "version": "0.1.0",
+                  "from": "has-ansi@>=0.1.0 <0.2.0",
+                  "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
+                  "dependencies": {
+                    "ansi-regex": {
+                      "version": "0.2.1",
+                      "from": "ansi-regex@>=0.2.0 <0.3.0",
+                      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz"
+                    }
+                  }
+                },
+                "strip-ansi": {
+                  "version": "0.3.0",
+                  "from": "strip-ansi@>=0.3.0 <0.4.0",
+                  "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
+                  "dependencies": {
+                    "ansi-regex": {
+                      "version": "0.2.1",
+                      "from": "ansi-regex@>=0.2.0 <0.3.0",
+                      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz"
+                    }
+                  }
+                },
+                "supports-color": {
+                  "version": "0.2.0",
+                  "from": "supports-color@>=0.2.0 <0.3.0",
+                  "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz"
+                }
+              }
+            },
+            "lodash": {
+              "version": "2.4.2",
+              "from": "lodash@>=2.4.1 <2.5.0",
+              "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+            }
+          }
+        },
+        "grunt-mocha-test": {
+          "version": "0.12.0",
+          "from": "grunt-mocha-test@0.12.0",
+          "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz",
+          "dependencies": {
+            "hooker": {
+              "version": "0.2.3",
+              "from": "hooker@>=0.2.3 <0.3.0",
+              "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+            },
+            "fs-extra": {
+              "version": "0.11.1",
+              "from": "fs-extra@>=0.11.1 <0.12.0",
+              "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz",
+              "dependencies": {
+                "ncp": {
+                  "version": "0.6.0",
+                  "from": "ncp@>=0.6.0 <0.7.0",
+                  "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz"
+                },
+                "mkdirp": {
+                  "version": "0.5.1",
+                  "from": "mkdirp@>=0.5.1 <0.6.0",
+                  "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+                  "dependencies": {
+                    "minimist": {
+                      "version": "0.0.8",
+                      "from": "minimist@0.0.8",
+                      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
+                    }
+                  }
+                },
+                "jsonfile": {
+                  "version": "2.3.1",
+                  "from": "jsonfile@>=2.0.0 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.3.1.tgz"
+                },
+                "rimraf": {
+                  "version": "2.5.2",
+                  "from": "rimraf@>=2.2.8 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz",
+                  "dependencies": {
+                    "glob": {
+                      "version": "7.0.5",
+                      "from": "glob@>=7.0.0 <8.0.0",
+                      "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz",
+                      "dependencies": {
+                        "fs.realpath": {
+                          "version": "1.0.0",
+                          "from": "fs.realpath@>=1.0.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
+                        },
+                        "inflight": {
+                          "version": "1.0.5",
+                          "from": "inflight@>=1.0.4 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz",
+                          "dependencies": {
+                            "wrappy": {
+                              "version": "1.0.2",
+                              "from": "wrappy@>=1.0.0 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+                            }
+                          }
+                        },
+                        "inherits": {
+                          "version": "2.0.1",
+                          "from": "inherits@>=2.0.0 <3.0.0",
+                          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                        },
+                        "minimatch": {
+                          "version": "3.0.2",
+                          "from": "minimatch@>=3.0.2 <4.0.0",
+                          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz",
+                          "dependencies": {
+                            "brace-expansion": {
+                              "version": "1.1.5",
+                              "from": "brace-expansion@>=1.0.0 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.5.tgz",
+                              "dependencies": {
+                                "balanced-match": {
+                                  "version": "0.4.1",
+                                  "from": "balanced-match@>=0.4.1 <0.5.0",
+                                  "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz"
+                                },
+                                "concat-map": {
+                                  "version": "0.0.1",
+                                  "from": "concat-map@0.0.1",
+                                  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+                                }
+                              }
+                            }
+                          }
+                        },
+                        "once": {
+                          "version": "1.3.3",
+                          "from": "once@>=1.3.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
+                          "dependencies": {
+                            "wrappy": {
+                              "version": "1.0.2",
+                              "from": "wrappy@>=1.0.0 <2.0.0",
+                              "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+                            }
+                          }
+                        },
+                        "path-is-absolute": {
+                          "version": "1.0.0",
+                          "from": "path-is-absolute@>=1.0.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz"
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "mocha": {
+          "version": "1.21.4",
+          "from": "mocha@1.21.4",
+          "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz",
+          "dependencies": {
+            "commander": {
+              "version": "2.0.0",
+              "from": "commander@2.0.0",
+              "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz"
+            },
+            "growl": {
+              "version": "1.8.1",
+              "from": "growl@>=1.8.0 <1.9.0",
+              "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz"
+            },
+            "jade": {
+              "version": "0.26.3",
+              "from": "jade@0.26.3",
+              "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
+              "dependencies": {
+                "commander": {
+                  "version": "0.6.1",
+                  "from": "commander@0.6.1",
+                  "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz"
+                },
+                "mkdirp": {
+                  "version": "0.3.0",
+                  "from": "mkdirp@0.3.0",
+                  "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz"
+                }
+              }
+            },
+            "diff": {
+              "version": "1.0.7",
+              "from": "diff@1.0.7",
+              "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz"
+            },
+            "debug": {
+              "version": "2.2.0",
+              "from": "debug@*",
+              "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+              "dependencies": {
+                "ms": {
+                  "version": "0.7.1",
+                  "from": "ms@0.7.1",
+                  "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+                }
+              }
+            },
+            "mkdirp": {
+              "version": "0.3.5",
+              "from": "mkdirp@0.3.5",
+              "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz"
+            },
+            "glob": {
+              "version": "3.2.3",
+              "from": "glob@3.2.3",
+              "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz",
+              "dependencies": {
+                "minimatch": {
+                  "version": "0.2.14",
+                  "from": "minimatch@>=0.2.11 <0.3.0",
+                  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+                  "dependencies": {
+                    "lru-cache": {
+                      "version": "2.7.3",
+                      "from": "lru-cache@>=2.0.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz"
+                    },
+                    "sigmund": {
+                      "version": "1.0.1",
+                      "from": "sigmund@>=1.0.0 <1.1.0",
+                      "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+                    }
+                  }
+                },
+                "graceful-fs": {
+                  "version": "2.0.3",
+                  "from": "graceful-fs@>=2.0.0 <2.1.0",
+                  "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz"
+                },
+                "inherits": {
+                  "version": "2.0.1",
+                  "from": "inherits@>=2.0.0 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                }
+              }
+            }
+          }
+        },
+        "redis": {
+          "version": "0.12.1",
+          "from": "redis@>=0.12.1 <0.13.0",
+          "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz"
+        },
+        "redis-sentinel": {
+          "version": "0.1.1",
+          "from": "redis-sentinel@0.1.1",
+          "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz",
+          "dependencies": {
+            "redis": {
+              "version": "0.11.0",
+              "from": "redis@>=0.11.0 <0.12.0",
+              "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz"
+            },
+            "q": {
+              "version": "0.9.2",
+              "from": "q@0.9.2",
+              "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz"
+            }
+          }
+        },
+        "sandboxed-module": {
+          "version": "1.0.1",
+          "from": "sandboxed-module@1.0.1",
+          "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz",
+          "dependencies": {
+            "require-like": {
+              "version": "0.1.2",
+              "from": "require-like@0.1.2",
+              "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz"
+            },
+            "stack-trace": {
+              "version": "0.0.9",
+              "from": "stack-trace@0.0.9",
+              "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz"
+            }
+          }
+        },
+        "sinon": {
+          "version": "1.10.3",
+          "from": "sinon@1.10.3",
+          "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz",
+          "dependencies": {
+            "formatio": {
+              "version": "1.0.2",
+              "from": "formatio@>=1.0.0 <1.1.0",
+              "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz",
+              "dependencies": {
+                "samsam": {
+                  "version": "1.1.3",
+                  "from": "samsam@>=1.1.0 <1.2.0",
+                  "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz"
+                }
+              }
+            },
+            "util": {
+              "version": "0.10.3",
+              "from": "util@>=0.10.3 <1.0.0",
+              "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+              "dependencies": {
+                "inherits": {
+                  "version": "2.0.1",
+                  "from": "inherits@2.0.1",
+                  "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                }
+              }
+            }
+          }
+        },
+        "underscore": {
+          "version": "1.7.0",
+          "from": "underscore@1.7.0",
+          "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz"
+        }
+      }
+    },
+    "request": {
+      "version": "2.72.0",
+      "from": "request@>=2.69.0 <3.0.0",
+      "resolved": "https://registry.npmjs.org/request/-/request-2.72.0.tgz",
+      "dependencies": {
+        "aws-sign2": {
+          "version": "0.6.0",
+          "from": "aws-sign2@>=0.6.0 <0.7.0",
+          "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz"
+        },
+        "aws4": {
+          "version": "1.4.1",
+          "from": "aws4@>=1.2.1 <2.0.0",
+          "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.4.1.tgz"
+        },
+        "bl": {
+          "version": "1.1.2",
+          "from": "bl@>=1.1.2 <1.2.0",
+          "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz",
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.0.6",
+              "from": "readable-stream@>=2.0.5 <2.1.0",
+              "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
+              "dependencies": {
+                "core-util-is": {
+                  "version": "1.0.2",
+                  "from": "core-util-is@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+                },
+                "inherits": {
+                  "version": "2.0.1",
+                  "from": "inherits@>=2.0.1 <2.1.0",
+                  "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                },
+                "isarray": {
+                  "version": "1.0.0",
+                  "from": "isarray@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
+                },
+                "process-nextick-args": {
+                  "version": "1.0.7",
+                  "from": "process-nextick-args@>=1.0.6 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
+                },
+                "string_decoder": {
+                  "version": "0.10.31",
+                  "from": "string_decoder@>=0.10.0 <0.11.0",
+                  "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+                },
+                "util-deprecate": {
+                  "version": "1.0.2",
+                  "from": "util-deprecate@>=1.0.1 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
+                }
+              }
+            }
+          }
+        },
+        "caseless": {
+          "version": "0.11.0",
+          "from": "caseless@>=0.11.0 <0.12.0",
+          "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz"
+        },
+        "combined-stream": {
+          "version": "1.0.5",
+          "from": "combined-stream@>=1.0.5 <1.1.0",
+          "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
+          "dependencies": {
+            "delayed-stream": {
+              "version": "1.0.0",
+              "from": "delayed-stream@>=1.0.0 <1.1.0",
+              "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
+            }
+          }
+        },
+        "extend": {
+          "version": "3.0.0",
+          "from": "extend@>=3.0.0 <3.1.0",
+          "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz"
+        },
+        "forever-agent": {
+          "version": "0.6.1",
+          "from": "forever-agent@>=0.6.1 <0.7.0",
+          "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz"
+        },
+        "form-data": {
+          "version": "1.0.0-rc4",
+          "from": "form-data@>=1.0.0-rc3 <1.1.0",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc4.tgz",
+          "dependencies": {
+            "async": {
+              "version": "1.5.2",
+              "from": "async@>=1.5.2 <2.0.0",
+              "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz"
+            }
+          }
+        },
+        "har-validator": {
+          "version": "2.0.6",
+          "from": "har-validator@>=2.0.6 <2.1.0",
+          "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
+          "dependencies": {
+            "chalk": {
+              "version": "1.1.3",
+              "from": "chalk@>=1.1.1 <2.0.0",
+              "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+              "dependencies": {
+                "ansi-styles": {
+                  "version": "2.2.1",
+                  "from": "ansi-styles@>=2.2.1 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz"
+                },
+                "escape-string-regexp": {
+                  "version": "1.0.5",
+                  "from": "escape-string-regexp@>=1.0.2 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
+                },
+                "has-ansi": {
+                  "version": "2.0.0",
+                  "from": "has-ansi@>=2.0.0 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+                  "dependencies": {
+                    "ansi-regex": {
+                      "version": "2.0.0",
+                      "from": "ansi-regex@>=2.0.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
+                    }
+                  }
+                },
+                "strip-ansi": {
+                  "version": "3.0.1",
+                  "from": "strip-ansi@>=3.0.0 <4.0.0",
+                  "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+                  "dependencies": {
+                    "ansi-regex": {
+                      "version": "2.0.0",
+                      "from": "ansi-regex@>=2.0.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
+                    }
+                  }
+                },
+                "supports-color": {
+                  "version": "2.0.0",
+                  "from": "supports-color@>=2.0.0 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz"
+                }
+              }
+            },
+            "commander": {
+              "version": "2.9.0",
+              "from": "commander@>=2.9.0 <3.0.0",
+              "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
+              "dependencies": {
+                "graceful-readlink": {
+                  "version": "1.0.1",
+                  "from": "graceful-readlink@>=1.0.0",
+                  "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz"
+                }
+              }
+            },
+            "is-my-json-valid": {
+              "version": "2.13.1",
+              "from": "is-my-json-valid@>=2.12.4 <3.0.0",
+              "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz",
+              "dependencies": {
+                "generate-function": {
+                  "version": "2.0.0",
+                  "from": "generate-function@>=2.0.0 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz"
+                },
+                "generate-object-property": {
+                  "version": "1.2.0",
+                  "from": "generate-object-property@>=1.1.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
+                  "dependencies": {
+                    "is-property": {
+                      "version": "1.0.2",
+                      "from": "is-property@>=1.0.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz"
+                    }
+                  }
+                },
+                "jsonpointer": {
+                  "version": "2.0.0",
+                  "from": "jsonpointer@2.0.0",
+                  "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz"
+                },
+                "xtend": {
+                  "version": "4.0.1",
+                  "from": "xtend@>=4.0.0 <5.0.0",
+                  "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz"
+                }
+              }
+            },
+            "pinkie-promise": {
+              "version": "2.0.1",
+              "from": "pinkie-promise@>=2.0.0 <3.0.0",
+              "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+              "dependencies": {
+                "pinkie": {
+                  "version": "2.0.4",
+                  "from": "pinkie@>=2.0.0 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz"
+                }
+              }
+            }
+          }
+        },
+        "hawk": {
+          "version": "3.1.3",
+          "from": "hawk@>=3.1.3 <3.2.0",
+          "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
+          "dependencies": {
+            "hoek": {
+              "version": "2.16.3",
+              "from": "hoek@>=2.0.0 <3.0.0",
+              "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz"
+            },
+            "boom": {
+              "version": "2.10.1",
+              "from": "boom@>=2.0.0 <3.0.0",
+              "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz"
+            },
+            "cryptiles": {
+              "version": "2.0.5",
+              "from": "cryptiles@>=2.0.0 <3.0.0",
+              "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz"
+            },
+            "sntp": {
+              "version": "1.0.9",
+              "from": "sntp@>=1.0.0 <2.0.0",
+              "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz"
+            }
+          }
+        },
+        "http-signature": {
+          "version": "1.1.1",
+          "from": "http-signature@>=1.1.0 <1.2.0",
+          "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
+          "dependencies": {
+            "assert-plus": {
+              "version": "0.2.0",
+              "from": "assert-plus@>=0.2.0 <0.3.0",
+              "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz"
+            },
+            "jsprim": {
+              "version": "1.3.0",
+              "from": "jsprim@>=1.2.2 <2.0.0",
+              "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.3.0.tgz",
+              "dependencies": {
+                "extsprintf": {
+                  "version": "1.0.2",
+                  "from": "extsprintf@1.0.2",
+                  "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz"
+                },
+                "json-schema": {
+                  "version": "0.2.2",
+                  "from": "json-schema@0.2.2",
+                  "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz"
+                },
+                "verror": {
+                  "version": "1.3.6",
+                  "from": "verror@1.3.6",
+                  "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz"
+                }
+              }
+            },
+            "sshpk": {
+              "version": "1.8.3",
+              "from": "sshpk@>=1.7.0 <2.0.0",
+              "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.8.3.tgz",
+              "dependencies": {
+                "asn1": {
+                  "version": "0.2.3",
+                  "from": "asn1@>=0.2.3 <0.3.0",
+                  "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz"
+                },
+                "assert-plus": {
+                  "version": "1.0.0",
+                  "from": "assert-plus@>=1.0.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
+                },
+                "dashdash": {
+                  "version": "1.14.0",
+                  "from": "dashdash@>=1.12.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.0.tgz"
+                },
+                "getpass": {
+                  "version": "0.1.6",
+                  "from": "getpass@>=0.1.1 <0.2.0",
+                  "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz"
+                },
+                "jsbn": {
+                  "version": "0.1.0",
+                  "from": "jsbn@>=0.1.0 <0.2.0",
+                  "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz"
+                },
+                "tweetnacl": {
+                  "version": "0.13.3",
+                  "from": "tweetnacl@>=0.13.0 <0.14.0",
+                  "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.13.3.tgz"
+                },
+                "jodid25519": {
+                  "version": "1.0.2",
+                  "from": "jodid25519@>=1.0.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz"
+                },
+                "ecc-jsbn": {
+                  "version": "0.1.1",
+                  "from": "ecc-jsbn@>=0.1.1 <0.2.0",
+                  "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz"
+                }
+              }
+            }
+          }
+        },
+        "is-typedarray": {
+          "version": "1.0.0",
+          "from": "is-typedarray@>=1.0.0 <1.1.0",
+          "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz"
+        },
+        "isstream": {
+          "version": "0.1.2",
+          "from": "isstream@>=0.1.2 <0.2.0",
+          "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz"
+        },
+        "json-stringify-safe": {
+          "version": "5.0.1",
+          "from": "json-stringify-safe@>=5.0.1 <6.0.0",
+          "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
+        },
+        "mime-types": {
+          "version": "2.1.11",
+          "from": "mime-types@>=2.1.7 <2.2.0",
+          "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz",
+          "dependencies": {
+            "mime-db": {
+              "version": "1.23.0",
+              "from": "mime-db@>=1.23.0 <1.24.0",
+              "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz"
+            }
+          }
+        },
+        "node-uuid": {
+          "version": "1.4.7",
+          "from": "node-uuid@>=1.4.7 <1.5.0",
+          "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz"
+        },
+        "oauth-sign": {
+          "version": "0.8.2",
+          "from": "oauth-sign@>=0.8.1 <0.9.0",
+          "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz"
+        },
+        "qs": {
+          "version": "6.1.0",
+          "from": "qs@>=6.1.0 <6.2.0",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.1.0.tgz"
+        },
+        "stringstream": {
+          "version": "0.0.5",
+          "from": "stringstream@>=0.0.4 <0.1.0",
+          "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz"
+        },
+        "tough-cookie": {
+          "version": "2.2.2",
+          "from": "tough-cookie@>=2.2.0 <2.3.0",
+          "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz"
+        },
+        "tunnel-agent": {
+          "version": "0.4.3",
+          "from": "tunnel-agent@>=0.4.1 <0.5.0",
+          "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz"
+        }
+      }
+    },
+    "requests": {
+      "version": "0.1.7",
+      "from": "requests@>=0.1.7 <0.2.0",
+      "resolved": "https://registry.npmjs.org/requests/-/requests-0.1.7.tgz",
+      "dependencies": {
+        "axo": {
+          "version": "0.0.1",
+          "from": "axo@>=0.0.0 <0.1.0",
+          "resolved": "https://registry.npmjs.org/axo/-/axo-0.0.1.tgz"
+        },
+        "eventemitter3": {
+          "version": "1.1.1",
+          "from": "eventemitter3@>=1.1.0 <1.2.0",
+          "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.1.1.tgz"
+        },
+        "extendible": {
+          "version": "0.1.1",
+          "from": "extendible@>=0.1.0 <0.2.0",
+          "resolved": "https://registry.npmjs.org/extendible/-/extendible-0.1.1.tgz"
+        },
+        "hang": {
+          "version": "1.0.0",
+          "from": "hang@>=1.0.0 <1.1.0",
+          "resolved": "https://registry.npmjs.org/hang/-/hang-1.0.0.tgz"
+        },
+        "loads": {
+          "version": "0.0.4",
+          "from": "loads@>=0.0.0 <0.1.0",
+          "resolved": "https://registry.npmjs.org/loads/-/loads-0.0.4.tgz",
+          "dependencies": {
+            "failure": {
+              "version": "1.1.1",
+              "from": "failure@>=1.1.0 <1.2.0",
+              "resolved": "https://registry.npmjs.org/failure/-/failure-1.1.1.tgz"
+            },
+            "one-time": {
+              "version": "0.0.4",
+              "from": "one-time@>=0.0.0 <0.1.0",
+              "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz"
+            },
+            "xhr-response": {
+              "version": "1.0.1",
+              "from": "xhr-response@>=1.0.0 <1.1.0",
+              "resolved": "https://registry.npmjs.org/xhr-response/-/xhr-response-1.0.1.tgz"
+            },
+            "xhr-status": {
+              "version": "1.0.0",
+              "from": "xhr-status@>=1.0.0 <1.1.0",
+              "resolved": "https://registry.npmjs.org/xhr-status/-/xhr-status-1.0.0.tgz"
+            }
+          }
+        },
+        "xhr-send": {
+          "version": "1.0.0",
+          "from": "xhr-send@>=1.0.0 <1.1.0",
+          "resolved": "https://registry.npmjs.org/xhr-send/-/xhr-send-1.0.0.tgz"
+        }
+      }
+    },
+    "rimraf": {
+      "version": "2.2.6",
+      "from": "rimraf@2.2.6",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.6.tgz"
+    },
+    "sanitizer": {
+      "version": "0.1.1",
+      "from": "sanitizer@0.1.1",
+      "resolved": "https://registry.npmjs.org/sanitizer/-/sanitizer-0.1.1.tgz"
+    },
+    "settings-sharelatex": {
+      "version": "1.0.0",
+      "from": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0",
+      "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559",
+      "dependencies": {
+        "coffee-script": {
+          "version": "1.6.0",
+          "from": "coffee-script@1.6.0",
+          "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz"
+        }
+      }
+    },
+    "sixpack-client": {
+      "version": "1.0.0",
+      "from": "sixpack-client@>=1.0.0 <2.0.0",
+      "resolved": "https://registry.npmjs.org/sixpack-client/-/sixpack-client-1.0.0.tgz"
+    },
+    "temp": {
+      "version": "0.8.3",
+      "from": "temp@>=0.8.3 <0.9.0",
+      "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz",
+      "dependencies": {
+        "os-tmpdir": {
+          "version": "1.0.1",
+          "from": "os-tmpdir@>=1.0.0 <2.0.0",
+          "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.1.tgz"
+        }
+      }
+    },
+    "translations-sharelatex": {
+      "version": "0.1.4",
+      "from": "git+https://github.com/sharelatex/translations-sharelatex.git#master",
+      "resolved": "git+https://github.com/sharelatex/translations-sharelatex.git#80fc5f140a5bddbe758cfc60651fb411e5b0ca5d",
+      "dependencies": {
+        "coffee-script": {
+          "version": "1.10.0",
+          "from": "coffee-script@>=1.7.1 <2.0.0",
+          "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.10.0.tgz"
+        },
+        "i18next": {
+          "version": "1.7.10",
+          "from": "i18next@>=1.7.1 <1.8.0",
+          "resolved": "https://registry.npmjs.org/i18next/-/i18next-1.7.10.tgz",
+          "dependencies": {
+            "cookies": {
+              "version": "0.6.1",
+              "from": "cookies@>=0.2.2",
+              "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.6.1.tgz",
+              "dependencies": {
+                "depd": {
+                  "version": "1.1.0",
+                  "from": "depd@>=1.1.0 <1.2.0",
+                  "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz"
+                },
+                "keygrip": {
+                  "version": "1.0.1",
+                  "from": "keygrip@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.1.tgz"
+                }
+              }
+            },
+            "json5": {
+              "version": "0.2.0",
+              "from": "json5@>=0.2.0 <0.3.0",
+              "resolved": "https://registry.npmjs.org/json5/-/json5-0.2.0.tgz"
+            }
+          }
+        },
+        "onesky": {
+          "version": "0.1.6",
+          "from": "onesky@>=0.1.5 <0.2.0",
+          "resolved": "https://registry.npmjs.org/onesky/-/onesky-0.1.6.tgz",
+          "dependencies": {
+            "request": {
+              "version": "2.40.0",
+              "from": "request@>=2.40.0 <2.41.0",
+              "resolved": "https://registry.npmjs.org/request/-/request-2.40.0.tgz",
+              "dependencies": {
+                "qs": {
+                  "version": "1.0.2",
+                  "from": "qs@>=1.0.0 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/qs/-/qs-1.0.2.tgz"
+                },
+                "json-stringify-safe": {
+                  "version": "5.0.1",
+                  "from": "json-stringify-safe@>=5.0.0 <5.1.0",
+                  "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
+                },
+                "mime-types": {
+                  "version": "1.0.2",
+                  "from": "mime-types@>=1.0.1 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz"
+                },
+                "forever-agent": {
+                  "version": "0.5.2",
+                  "from": "forever-agent@>=0.5.0 <0.6.0",
+                  "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz"
+                },
+                "tough-cookie": {
+                  "version": "2.2.2",
+                  "from": "tough-cookie@>=0.12.0",
+                  "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz"
+                },
+                "form-data": {
+                  "version": "0.1.4",
+                  "from": "form-data@>=0.1.0 <0.2.0",
+                  "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz",
+                  "dependencies": {
+                    "combined-stream": {
+                      "version": "0.0.7",
+                      "from": "combined-stream@>=0.0.4 <0.1.0",
+                      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz",
+                      "dependencies": {
+                        "delayed-stream": {
+                          "version": "0.0.5",
+                          "from": "delayed-stream@0.0.5",
+                          "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz"
+                        }
+                      }
+                    },
+                    "mime": {
+                      "version": "1.2.11",
+                      "from": "mime@>=1.2.11 <1.3.0",
+                      "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
+                    },
+                    "async": {
+                      "version": "0.9.2",
+                      "from": "async@>=0.9.0 <0.10.0",
+                      "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz"
+                    }
+                  }
+                },
+                "tunnel-agent": {
+                  "version": "0.4.3",
+                  "from": "tunnel-agent@>=0.4.0 <0.5.0",
+                  "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz"
+                },
+                "http-signature": {
+                  "version": "0.10.1",
+                  "from": "http-signature@>=0.10.0 <0.11.0",
+                  "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz",
+                  "dependencies": {
+                    "assert-plus": {
+                      "version": "0.1.5",
+                      "from": "assert-plus@>=0.1.5 <0.2.0",
+                      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz"
+                    },
+                    "asn1": {
+                      "version": "0.1.11",
+                      "from": "asn1@0.1.11",
+                      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz"
+                    },
+                    "ctype": {
+                      "version": "0.5.3",
+                      "from": "ctype@0.5.3",
+                      "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz"
+                    }
+                  }
+                },
+                "oauth-sign": {
+                  "version": "0.3.0",
+                  "from": "oauth-sign@>=0.3.0 <0.4.0",
+                  "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz"
+                },
+                "hawk": {
+                  "version": "1.1.1",
+                  "from": "hawk@1.1.1",
+                  "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz",
+                  "dependencies": {
+                    "hoek": {
+                      "version": "0.9.1",
+                      "from": "hoek@>=0.9.0 <0.10.0",
+                      "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz"
+                    },
+                    "boom": {
+                      "version": "0.4.2",
+                      "from": "boom@>=0.4.0 <0.5.0",
+                      "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz"
+                    },
+                    "cryptiles": {
+                      "version": "0.2.2",
+                      "from": "cryptiles@>=0.2.0 <0.3.0",
+                      "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz"
+                    },
+                    "sntp": {
+                      "version": "0.2.4",
+                      "from": "sntp@>=0.2.0 <0.3.0",
+                      "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz"
+                    }
+                  }
+                },
+                "aws-sign2": {
+                  "version": "0.5.0",
+                  "from": "aws-sign2@>=0.5.0 <0.6.0",
+                  "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz"
+                },
+                "stringstream": {
+                  "version": "0.0.5",
+                  "from": "stringstream@>=0.0.4 <0.1.0",
+                  "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "underscore": {
+      "version": "1.6.0",
+      "from": "underscore@1.6.0",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz"
+    },
+    "v8-profiler": {
+      "version": "5.6.5",
+      "from": "v8-profiler@>=5.2.3 <6.0.0",
+      "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.6.5.tgz",
+      "dependencies": {
+        "nan": {
+          "version": "2.3.5",
+          "from": "nan@>=2.3.2 <3.0.0",
+          "resolved": "https://registry.npmjs.org/nan/-/nan-2.3.5.tgz"
+        },
+        "node-pre-gyp": {
+          "version": "0.6.28",
+          "from": "node-pre-gyp@>=0.6.5 <0.7.0",
+          "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.28.tgz",
+          "dependencies": {
+            "mkdirp": {
+              "version": "0.5.1",
+              "from": "mkdirp@>=0.5.0 <0.6.0",
+              "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+              "dependencies": {
+                "minimist": {
+                  "version": "0.0.8",
+                  "from": "minimist@0.0.8",
+                  "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
+                }
+              }
+            },
+            "nopt": {
+              "version": "3.0.6",
+              "from": "nopt@>=3.0.1 <3.1.0",
+              "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+              "dependencies": {
+                "abbrev": {
+                  "version": "1.0.9",
+                  "from": "abbrev@>=1.0.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz"
+                }
+              }
+            },
+            "npmlog": {
+              "version": "2.0.4",
+              "from": "npmlog@>=2.0.0 <2.1.0",
+              "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-2.0.4.tgz",
+              "dependencies": {
+                "ansi": {
+                  "version": "0.3.1",
+                  "from": "ansi@>=0.3.1 <0.4.0",
+                  "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz"
+                },
+                "are-we-there-yet": {
+                  "version": "1.1.2",
+                  "from": "are-we-there-yet@>=1.1.2 <1.2.0",
+                  "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz",
+                  "dependencies": {
+                    "delegates": {
+                      "version": "1.0.0",
+                      "from": "delegates@>=1.0.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz"
+                    },
+                    "readable-stream": {
+                      "version": "2.1.4",
+                      "from": "readable-stream@>=2.0.0 <3.0.0||>=1.1.13 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.4.tgz",
+                      "dependencies": {
+                        "buffer-shims": {
+                          "version": "1.0.0",
+                          "from": "buffer-shims@>=1.0.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz"
+                        },
+                        "core-util-is": {
+                          "version": "1.0.2",
+                          "from": "core-util-is@>=1.0.0 <1.1.0",
+                          "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+                        },
+                        "inherits": {
+                          "version": "2.0.1",
+                          "from": "inherits@>=2.0.1 <2.1.0",
+                          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                        },
+                        "isarray": {
+                          "version": "1.0.0",
+                          "from": "isarray@>=1.0.0 <1.1.0",
+                          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
+                        },
+                        "process-nextick-args": {
+                          "version": "1.0.7",
+                          "from": "process-nextick-args@>=1.0.6 <1.1.0",
+                          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
+                        },
+                        "string_decoder": {
+                          "version": "0.10.31",
+                          "from": "string_decoder@>=0.10.0 <0.11.0",
+                          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+                        },
+                        "util-deprecate": {
+                          "version": "1.0.2",
+                          "from": "util-deprecate@>=1.0.1 <1.1.0",
+                          "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
+                        }
+                      }
+                    }
+                  }
+                },
+                "gauge": {
+                  "version": "1.2.7",
+                  "from": "gauge@>=1.2.5 <1.3.0",
+                  "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz",
+                  "dependencies": {
+                    "has-unicode": {
+                      "version": "2.0.1",
+                      "from": "has-unicode@>=2.0.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz"
+                    },
+                    "lodash.pad": {
+                      "version": "4.4.0",
+                      "from": "lodash.pad@>=4.1.0 <5.0.0",
+                      "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.4.0.tgz",
+                      "dependencies": {
+                        "lodash._baseslice": {
+                          "version": "4.0.0",
+                          "from": "lodash._baseslice@>=4.0.0 <4.1.0",
+                          "resolved": "https://registry.npmjs.org/lodash._baseslice/-/lodash._baseslice-4.0.0.tgz"
+                        },
+                        "lodash._basetostring": {
+                          "version": "4.12.0",
+                          "from": "lodash._basetostring@>=4.12.0 <4.13.0",
+                          "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz"
+                        },
+                        "lodash.tostring": {
+                          "version": "4.1.3",
+                          "from": "lodash.tostring@>=4.0.0 <5.0.0",
+                          "resolved": "https://registry.npmjs.org/lodash.tostring/-/lodash.tostring-4.1.3.tgz"
+                        }
+                      }
+                    },
+                    "lodash.padend": {
+                      "version": "4.5.0",
+                      "from": "lodash.padend@>=4.1.0 <5.0.0",
+                      "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.5.0.tgz",
+                      "dependencies": {
+                        "lodash._baseslice": {
+                          "version": "4.0.0",
+                          "from": "lodash._baseslice@>=4.0.0 <4.1.0",
+                          "resolved": "https://registry.npmjs.org/lodash._baseslice/-/lodash._baseslice-4.0.0.tgz"
+                        },
+                        "lodash._basetostring": {
+                          "version": "4.12.0",
+                          "from": "lodash._basetostring@>=4.12.0 <4.13.0",
+                          "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz"
+                        },
+                        "lodash.tostring": {
+                          "version": "4.1.3",
+                          "from": "lodash.tostring@>=4.0.0 <5.0.0",
+                          "resolved": "https://registry.npmjs.org/lodash.tostring/-/lodash.tostring-4.1.3.tgz"
+                        }
+                      }
+                    },
+                    "lodash.padstart": {
+                      "version": "4.5.0",
+                      "from": "lodash.padstart@>=4.1.0 <5.0.0",
+                      "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.5.0.tgz",
+                      "dependencies": {
+                        "lodash._baseslice": {
+                          "version": "4.0.0",
+                          "from": "lodash._baseslice@>=4.0.0 <4.1.0",
+                          "resolved": "https://registry.npmjs.org/lodash._baseslice/-/lodash._baseslice-4.0.0.tgz"
+                        },
+                        "lodash._basetostring": {
+                          "version": "4.12.0",
+                          "from": "lodash._basetostring@>=4.12.0 <4.13.0",
+                          "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz"
+                        },
+                        "lodash.tostring": {
+                          "version": "4.1.3",
+                          "from": "lodash.tostring@>=4.0.0 <5.0.0",
+                          "resolved": "https://registry.npmjs.org/lodash.tostring/-/lodash.tostring-4.1.3.tgz"
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            },
+            "rc": {
+              "version": "1.1.6",
+              "from": "rc@>=1.1.0 <1.2.0",
+              "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz",
+              "dependencies": {
+                "deep-extend": {
+                  "version": "0.4.1",
+                  "from": "deep-extend@>=0.4.0 <0.5.0",
+                  "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz"
+                },
+                "ini": {
+                  "version": "1.3.4",
+                  "from": "ini@>=1.3.0 <1.4.0",
+                  "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz"
+                },
+                "minimist": {
+                  "version": "1.2.0",
+                  "from": "minimist@>=1.2.0 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz"
+                },
+                "strip-json-comments": {
+                  "version": "1.0.4",
+                  "from": "strip-json-comments@>=1.0.4 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz"
+                }
+              }
+            },
+            "rimraf": {
+              "version": "2.5.2",
+              "from": "rimraf@>=2.5.0 <2.6.0",
+              "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz",
+              "dependencies": {
+                "glob": {
+                  "version": "7.0.5",
+                  "from": "glob@>=7.0.0 <8.0.0",
+                  "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz",
+                  "dependencies": {
+                    "fs.realpath": {
+                      "version": "1.0.0",
+                      "from": "fs.realpath@>=1.0.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
+                    },
+                    "inflight": {
+                      "version": "1.0.5",
+                      "from": "inflight@>=1.0.4 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz",
+                      "dependencies": {
+                        "wrappy": {
+                          "version": "1.0.2",
+                          "from": "wrappy@>=1.0.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+                        }
+                      }
+                    },
+                    "inherits": {
+                      "version": "2.0.1",
+                      "from": "inherits@>=2.0.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                    },
+                    "minimatch": {
+                      "version": "3.0.2",
+                      "from": "minimatch@>=3.0.2 <4.0.0",
+                      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz",
+                      "dependencies": {
+                        "brace-expansion": {
+                          "version": "1.1.5",
+                          "from": "brace-expansion@>=1.0.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.5.tgz",
+                          "dependencies": {
+                            "balanced-match": {
+                              "version": "0.4.1",
+                              "from": "balanced-match@>=0.4.1 <0.5.0",
+                              "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz"
+                            },
+                            "concat-map": {
+                              "version": "0.0.1",
+                              "from": "concat-map@0.0.1",
+                              "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "once": {
+                      "version": "1.3.3",
+                      "from": "once@>=1.3.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
+                      "dependencies": {
+                        "wrappy": {
+                          "version": "1.0.2",
+                          "from": "wrappy@>=1.0.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+                        }
+                      }
+                    },
+                    "path-is-absolute": {
+                      "version": "1.0.0",
+                      "from": "path-is-absolute@>=1.0.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz"
+                    }
+                  }
+                }
+              }
+            },
+            "semver": {
+              "version": "5.1.1",
+              "from": "semver@>=5.1.0 <5.2.0",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.1.tgz"
+            },
+            "tar": {
+              "version": "2.2.1",
+              "from": "tar@>=2.2.0 <2.3.0",
+              "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
+              "dependencies": {
+                "block-stream": {
+                  "version": "0.0.9",
+                  "from": "block-stream@*",
+                  "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz"
+                },
+                "fstream": {
+                  "version": "1.0.10",
+                  "from": "fstream@>=1.0.2 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.10.tgz",
+                  "dependencies": {
+                    "graceful-fs": {
+                      "version": "4.1.4",
+                      "from": "graceful-fs@>=4.1.2 <5.0.0",
+                      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.4.tgz"
+                    }
+                  }
+                },
+                "inherits": {
+                  "version": "2.0.1",
+                  "from": "inherits@>=2.0.0 <3.0.0",
+                  "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                }
+              }
+            },
+            "tar-pack": {
+              "version": "3.1.4",
+              "from": "tar-pack@>=3.1.0 <3.2.0",
+              "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.1.4.tgz",
+              "dependencies": {
+                "debug": {
+                  "version": "2.2.0",
+                  "from": "debug@>=2.2.0 <2.3.0",
+                  "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+                  "dependencies": {
+                    "ms": {
+                      "version": "0.7.1",
+                      "from": "ms@0.7.1",
+                      "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+                    }
+                  }
+                },
+                "fstream": {
+                  "version": "1.0.10",
+                  "from": "fstream@>=1.0.2 <2.0.0",
+                  "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.10.tgz",
+                  "dependencies": {
+                    "graceful-fs": {
+                      "version": "4.1.4",
+                      "from": "graceful-fs@>=4.1.2 <5.0.0",
+                      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.4.tgz"
+                    },
+                    "inherits": {
+                      "version": "2.0.1",
+                      "from": "inherits@>=2.0.0 <2.1.0",
+                      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                    }
+                  }
+                },
+                "fstream-ignore": {
+                  "version": "1.0.5",
+                  "from": "fstream-ignore@>=1.0.5 <1.1.0",
+                  "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz",
+                  "dependencies": {
+                    "inherits": {
+                      "version": "2.0.1",
+                      "from": "inherits@>=2.0.0 <3.0.0",
+                      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                    },
+                    "minimatch": {
+                      "version": "3.0.2",
+                      "from": "minimatch@>=3.0.0 <4.0.0",
+                      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz",
+                      "dependencies": {
+                        "brace-expansion": {
+                          "version": "1.1.5",
+                          "from": "brace-expansion@>=1.0.0 <2.0.0",
+                          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.5.tgz",
+                          "dependencies": {
+                            "balanced-match": {
+                              "version": "0.4.1",
+                              "from": "balanced-match@>=0.4.1 <0.5.0",
+                              "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz"
+                            },
+                            "concat-map": {
+                              "version": "0.0.1",
+                              "from": "concat-map@0.0.1",
+                              "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                },
+                "once": {
+                  "version": "1.3.3",
+                  "from": "once@>=1.3.3 <1.4.0",
+                  "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
+                  "dependencies": {
+                    "wrappy": {
+                      "version": "1.0.2",
+                      "from": "wrappy@>=1.0.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+                    }
+                  }
+                },
+                "readable-stream": {
+                  "version": "2.1.4",
+                  "from": "readable-stream@>=2.1.4 <2.2.0",
+                  "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.4.tgz",
+                  "dependencies": {
+                    "buffer-shims": {
+                      "version": "1.0.0",
+                      "from": "buffer-shims@>=1.0.0 <2.0.0",
+                      "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz"
+                    },
+                    "core-util-is": {
+                      "version": "1.0.2",
+                      "from": "core-util-is@>=1.0.0 <1.1.0",
+                      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+                    },
+                    "inherits": {
+                      "version": "2.0.1",
+                      "from": "inherits@>=2.0.1 <2.1.0",
+                      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+                    },
+                    "isarray": {
+                      "version": "1.0.0",
+                      "from": "isarray@>=1.0.0 <1.1.0",
+                      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
+                    },
+                    "process-nextick-args": {
+                      "version": "1.0.7",
+                      "from": "process-nextick-args@>=1.0.6 <1.1.0",
+                      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
+                    },
+                    "string_decoder": {
+                      "version": "0.10.31",
+                      "from": "string_decoder@>=0.10.0 <0.11.0",
+                      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+                    },
+                    "util-deprecate": {
+                      "version": "1.0.2",
+                      "from": "util-deprecate@>=1.0.1 <1.1.0",
+                      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
+                    }
+                  }
+                },
+                "uid-number": {
+                  "version": "0.0.6",
+                  "from": "uid-number@>=0.0.6 <0.1.0",
+                  "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "xml2js": {
+      "version": "0.2.0",
+      "from": "xml2js@0.2.0",
+      "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.0.tgz",
+      "dependencies": {
+        "sax": {
+          "version": "1.2.1",
+          "from": "sax@>=0.1.1",
+          "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz"
+        }
+      }
+    }
+  }
+}

+ 1 - 0
services/web/package.json

@@ -76,6 +76,7 @@
     "grunt-contrib-watch": "^1.0.0",
     "grunt-env": "0.4.4",
     "grunt-exec": "^0.4.7",
+    "grunt-execute": "^0.2.2",
     "grunt-file-append": "0.0.6",
     "grunt-git-rev-parse": "^0.1.4",
     "grunt-mocha-test": "0.9.0",

+ 4 - 2
services/web/public/coffee/ide/editor/directives/aceEditor.coffee

@@ -74,15 +74,17 @@ define [
 				editor.commands.removeCommand "foldall"
 				
 				# For European keyboards, the / is above 7 so needs Shift pressing.
-				# This comes through as Ctrl-Shift-/ which is mapped to toggleBlockComment.
+				# This comes through as Command-Shift-/ on OS X, which is mapped to 
+				# toggleBlockComment.
 				# This doesn't do anything for LaTeX, so remap this to togglecomment to
 				# work for European keyboards as normal.
+				# On Windows, the key combo comes as Ctrl-Shift-7.
 				editor.commands.removeCommand "toggleBlockComment"
 				editor.commands.removeCommand "togglecomment"
 				
 				editor.commands.addCommand {
 					name: "togglecomment",
-					bindKey: { win: "Ctrl-/|Ctrl-Shift-/", mac: "Command-/|Command-Shift-/" },
+					bindKey: { win: "Ctrl-/|Ctrl-Shift-7", mac: "Command-/|Command-Shift-/" },
 					exec: (editor) -> editor.toggleCommentLines(),
 					multiSelectAction: "forEachLine",
 					scrollIntoView: "selectionPart"

+ 18 - 18
services/web/public/coffee/ide/human-readable-logs/HumanReadableLogsRules.coffee

@@ -1,90 +1,90 @@
 define -> [
 		regexToMatch: /Misplaced alignment tab character \&/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:Misplaced_alignment_tab_character_%26"
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Misplaced_alignment_tab_character_%26"
 		humanReadableHint: """
 			You have placed an alignment tab character '&' in the wrong place. If you want to align something, you must write it inside an align environment such as \\begin{align} \u2026 \\end{align}, \\begin{tabular} \u2026 \\end{tabular}, etc. If you want to write an ampersand '&' in text, you must write \\& instead.
 		"""
 	,
 		regexToMatch: /Extra alignment tab has been changed to \\cr/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:Extra_alignment_tab_has_been_changed_to_%5Ccr"
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Extra_alignment_tab_has_been_changed_to_%5Ccr"
 		humanReadableHint: """
 			You have written too many alignment tabs in a table, causing one of them to be turned into a line break. Make sure you have specified the correct number of columns in your <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Tables\">table</a>.
 		"""
 	,
 		regexToMatch: /Display math should end with \$\$/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:Display_math_should_end_with_$$."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Display_math_should_end_with_$$."
 		humanReadableHint: """
 			You have forgotten a $ sign at the end of 'display math' mode. When writing in display math mode, you must always math write inside $$ \u2026 $$. Check that the number of $s match around each math expression.
 		"""
 	,
 		regexToMatch: /Missing [{$] inserted./
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:Missing_$_inserted"
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Missing_$_inserted"
 		humanReadableHint: """
-			Check that your $'s match around math expressions. If they do, then you've probably used a symbol in normal text that needs to be in math mode. Symbols such as subscripts ( _ ), integrals ( \\int ), Greek letters ( \\alpha, \\beta, \\delta ), and modifiers (\\vec{x}, \\tilde{x} ) must be written in math mode. See the full list <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Errors:Missing_$_inserted\">here</a>.
+			Check that your $'s match around math expressions. If they do, then you've probably used a symbol in normal text that needs to be in math mode. Symbols such as subscripts ( _ ), integrals ( \\int ), Greek letters ( \\alpha, \\beta, \\delta ), and modifiers (\\vec{x}, \\tilde{x} ) must be written in math mode. See the full list <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Errors/Missing_$_inserted \">here</a>.If you intended to use mathematics mode, then use $ \u2026 $ for 'inline math mode', $$ \u2026 $$ for 'display math mode' or alternatively \begin{math} \u2026 \end{math}.
 		"""
 	,
 		regexToMatch: /(undefined )?[rR]eference(s)?.+(undefined)?/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:There_were_undefined_references."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/There_were_undefined_references."
 		humanReadableHint: """
 			You have referenced something which has not yet been labelled. If you have labelled it already, make sure that what is written inside \\ref{...} is the same as what is written inside \\label{...}.
 		"""
 	,
 		regexToMatch: /Citation .+ on page .+ undefined on input line .+/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:Citation_XXX_on_page_XXX_undefined_on_input_line_XXX."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Citation_XXX_on_page_XXX_undefined_on_input_line_XXX."
 		humanReadableHint: """
 			You have cited something which is not included in your bibliography. Make sure that the citation (\\cite{...}) has a corresponding key in your bibliography, and that both are spelled the same way.
 		"""
 	,
 		regexToMatch: /(Label .+)? multiply[ -]defined( labels)?/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:There_were_multiply-defined_labels."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/There_were_multiply-defined_labels."
 		humanReadableHint: """
 			You have used the same label more than once. Check that each \\label{...} labels only one item.
 		"""
 	,
 		regexToMatch: /`!?h' float specifier changed to `!?ht'/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:%60!h%27_float_specifier_changed_to_%60!ht%27."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/%60!h%27_float_specifier_changed_to_%60!ht%27."
 		humanReadableHint: """
 			The float specifier 'h' is too strict of a demand for LaTeX to place your float in a nice way here. Try relaxing it by using 'ht', or even 'htbp' if necessary. If you want to try keep the float here anyway, check out the <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Positioning_of_Figures\">float package</a>.
 		"""
 	,
 		regexToMatch: /No positions in optional float specifier/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:No_positions_in_optional_float_specifier."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/No_positions_in_optional_float_specifier."
 		humanReadableHint: """
 			You have forgotten to include a float specifier, which tells LaTeX where to position your figure. Find out more about float specifiers <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Positioning_of_Figures\">here</a>.
 		"""
 	,
 		regexToMatch: /Undefined control sequence/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:Undefined_control_sequence."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Undefined_control_sequence."
 		humanReadableHint: """
 			The compiler is having trouble understanding a command you have used. Check that the command is spelled correctly. If the command is part of a package, make sure you have included the package in your preamble using \\usepackage{...}.
 		"""
 	,
 		regexToMatch: /File .+ not found/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:File_XXX_not_found_on_input_line_XXX."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/File_XXX_not_found_on_input_line_XXX."
 		humanReadableHint: """
-			The compiler cannot find the file you want to include. Make sure that you have <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Including_images_in_ShareLaTeX\">uploaded the file</a> and <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Errors:File_XXX_not_found_on_input_line_XXX.\">specified the file location correctly</a>.
+			The compiler cannot find the file you want to include. Make sure that you have <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Including_images_in_ShareLaTeX\">uploaded the file</a> and <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Errors/File_XXX_not_found_on_input_line_XXX.\">specified the file location correctly</a>.
 		"""
 	,
 		regexToMatch: /LaTeX Error: Unknown graphics extension: \..+/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:LaTeX_Error:_Unknown_graphics_extension:_.gif."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_graphics_extension:_.gif."
 		humanReadableHint: """
-			The compiler does not recognise the file type of one of your images. Make sure you are using a <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Errors:LaTeX_Error:_Unknown_graphics_extension:_.gif.\">supported image format</a> for your choice of compiler, and check that there are no periods (.) in the name of your image.
+			The compiler does not recognise the file type of one of your images. Make sure you are using a <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_graphics_extension:_.gif.\">supported image format</a> for your choice of compiler, and check that there are no periods (.) in the name of your image.
 		"""
 	,
 		regexToMatch: /LaTeX Error: Unknown float option `H'/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:LaTeX_Error:_Unknown_float_option_%60H%27."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_float_option_%60H%27."
 		humanReadableHint: """
 			The compiler isn't recognizing the float option 'H'. Include \\usepackage{float} in your preamble to fix this.
 		"""
 	,
 		regexToMatch: /LaTeX Error: Unknown float option `.+'/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:LaTeX_Error:_Unknown_float_option_%60H%27."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_float_option_%60H%27."
 		humanReadableHint: """
 			You have used a float specifier which the compiler does not understand. You can learn more about the different float options available for placing figures <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Positioning_of_Figures\">here</a>.
 		"""
 	,
 		regexToMatch: /LaTeX Error: \\math.+ allowed only in math mode/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:LaTeX_Error:_%5Cmathrm_allowed_only_in_math_mode."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_%5Cmathrm_allowed_only_in_math_mode."
 		humanReadableHint: """
 			You have used a font command which is only available in math mode. To use this command, you must be in maths mode (E.g. $ \u2026 $ or \\begin{math} \u2026 \\end{math}). If you want to use it outside of math mode, use the text version instead: \\textrm, \\textit, etc.
 		"""

+ 11 - 5
services/web/public/coffee/ide/pdf/controllers/PdfController.coffee

@@ -6,13 +6,14 @@ define [
 ], (App, Ace, HumanReadableLogs, BibLogParser) ->
 	App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, localStorage) ->
 
-		# enable per-user containers if querystring includes isolated=true
-		perUserCompile = window.location?.search?.match(/isolated=true/)? or undefined
+		# enable per-user containers by default
+		perUserCompile = true
 		autoCompile = true
 
 		# pdf.view = uncompiled | pdf | errors
 		$scope.pdf.view = if $scope?.pdf?.url then 'pdf' else 'uncompiled'
 		$scope.shouldShowLogs = false
+		$scope.wikiEnabled = window.wikiEnabled;
 
 		if ace.require("ace/lib/useragent").isMac
 			$scope.modifierKey = "Cmd"
@@ -24,6 +25,11 @@ define [
 			qs_args = ("#{k}=#{v}" for k, v of args)
 			if qs_args.length then "?" + qs_args.join("&") else ""
 
+		$scope.stripHTMLFromString = (htmlStr) ->
+   			tmp = document.createElement("DIV")
+   			tmp.innerHTML = htmlStr
+   			return tmp.textContent || tmp.innerText || ""
+
 		$scope.$on "project:joined", () ->
 			return if !autoCompile
 			autoCompile = false
@@ -173,7 +179,7 @@ define [
 			accumulateResults = (newEntries) ->
 				for key in ['all', 'errors', 'warnings']
 					logEntries[key] = logEntries[key].concat newEntries[key]
-				
+
 			# use the parsers for each file type
 			processLog = (log) ->
 				$scope.pdf.rawLog = log
@@ -319,8 +325,8 @@ define [
 			$scope.startedFreeTrial = true
 
 	App.factory "synctex", ["ide", "$http", "$q", (ide, $http, $q) ->
-		# enable per-user containers if querystring includes isolated=true
-		perUserCompile = window.location?.search?.match(/isolated=true/)? or undefined
+		# enable per-user containers by default
+		perUserCompile = true
 
 		synctex =
 			syncToPdf: (cursorPosition) ->

+ 3 - 1
services/web/public/coffee/ide/wordcount/controllers/WordCountModalController.coffee

@@ -5,7 +5,9 @@ define [
 		$scope.status = 
 			loading:true
 
-		perUserCompile = window.location?.search?.match(/isolated=true/)? or undefined
+		# enable per-user containers by default
+		perUserCompile = true
+
 		opts =
 			url:"/project/#{ide.project_id}/wordcount"
 			method:"GET"

+ 7 - 0
services/web/public/coffee/main/new-subscription.coffee

@@ -115,6 +115,13 @@ define [
 						currencyCode:pricing.items.currency
 						plan_code:pricing.items.plan.code
 						coupon_code:pricing.items?.coupon?.code || ""
+						isPaypal: $scope.paymentMethod == 'paypal'
+						address:
+							address1:    $scope.data.address1
+							address2:    $scope.data.address2
+							country:     $scope.data.country
+							state:       $scope.data.state
+							postal_code: $scope.data.postal_code
 				$http.post("/user/subscription/create", postData)
 					.success (data, status, headers)->
 						sixpack.convert "in-editor-free-trial-plan", pricing.items.plan.code, (err)->

+ 24 - 0
services/web/public/stylesheets/app/editor/pdf.less

@@ -120,6 +120,10 @@
 			float: right;
 			color: @gray;
 			font-weight: 700;
+
+			.fa {
+				display: none;
+			}
 		}
 		.entry-message {
 			font-weight: 700;
@@ -130,6 +134,26 @@
 			font-size: 0.8rem;
 			//font-family: @font-family-monospace;
 		}
+
+		&:hover .line-no {
+			color: inherit;
+			.fa {
+				display: inline-block;
+			}
+		}
+
+		&.alert-danger:hover {
+			background-color: darken(@alert-danger-bg, 5%);
+		}
+
+		&.alert-warning:hover {
+			background-color: darken(@alert-warning-bg, 5%);
+		}
+
+		&.alert-info:hover {
+			background-color: darken(@alert-info-bg, 5%);
+		}
+
 	}
 	pre {
 		font-size: 12px;

+ 8 - 3
services/web/public/stylesheets/app/wiki.less

@@ -39,6 +39,8 @@
 	}
 	
 	.example {
+		max-width: 100%;
+
 		.code {
 			pre {
 				background-color: @gray-lightest;
@@ -49,10 +51,13 @@
 			}
 		}
 		.output {
+			text-align: center;
+			padding-top: 10px;
+			
 			img {
-				width: auto !important;
-				height: auto !important;
-				max-width: 100% !important;
+				width: auto;
+				height: auto;
+				max-width: 100%;
 				box-shadow: 0 1px 3px @gray-light;
 				border-radius: 6px;
 			}

+ 32 - 1
services/web/test/UnitTests/coffee/Authentication/AuthenticationManagerTests.coffee

@@ -16,6 +16,7 @@ describe "AuthenticationManager", ->
 					users: {}
 				ObjectId: ObjectId
 			"bcrypt": @bcrypt = {}
+			"settings-sharelatex": { security: { bcryptRounds: 12 } }
 		@callback = sinon.stub()
 
 	describe "authenticate", ->
@@ -31,6 +32,7 @@ describe "AuthenticationManager", ->
 				beforeEach (done) ->
 					@user.hashedPassword = @hashedPassword = "asdfjadflasdf"
 					@bcrypt.compare = sinon.stub().callsArgWith(2, null, true)
+					@bcrypt.getRounds = sinon.stub().returns 12
 					@AuthenticationManager.authenticate email: @email, @unencryptedPassword, (error, user) =>
 						@callback(error, user)
 						done()
@@ -54,6 +56,35 @@ describe "AuthenticationManager", ->
 				it "should not return the user", ->
 					@callback.calledWith(null, null).should.equal true
 
+			describe "when the hashed password matches but the number of rounds is too low", ->
+				beforeEach (done) ->
+					@user.hashedPassword = @hashedPassword = "asdfjadflasdf"
+					@bcrypt.compare = sinon.stub().callsArgWith(2, null, true)
+					@bcrypt.getRounds = sinon.stub().returns 7
+					@AuthenticationManager.setUserPassword = sinon.stub().callsArgWith(2, null)
+					@AuthenticationManager.authenticate email: @email, @unencryptedPassword, (error, user) =>
+						@callback(error, user)
+						done()
+
+				it "should look up the correct user in the database", ->
+					@User.findOne.calledWith(email: @email).should.equal true
+
+				it "should check that the passwords match", ->
+					@bcrypt.compare
+						.calledWith(@unencryptedPassword, @hashedPassword)
+						.should.equal true
+
+				it "should check the number of rounds", ->
+					@bcrypt.getRounds.called.should.equal true
+
+				it "should set the users password (with a higher number of rounds)", ->
+					@AuthenticationManager.setUserPassword
+						.calledWith("user-id", @unencryptedPassword)
+						.should.equal true
+
+				it "should return the user", ->
+					@callback.calledWith(null, @user).should.equal true
+
 		describe "when the user does not exist in the database", ->
 			beforeEach ->
 				@User.findOne = sinon.stub().callsArgWith(1, null, null)
@@ -87,7 +118,7 @@ describe "AuthenticationManager", ->
 
 		it "should hash the password", ->
 			@bcrypt.genSalt
-				.calledWith(7)
+				.calledWith(12)
 				.should.equal true
 			@bcrypt.hash
 				.calledWith(@password, @salt)

+ 766 - 80
services/web/test/UnitTests/coffee/Subscription/RecurlyWrapperTests.coffee

@@ -1,9 +1,10 @@
 should = require('chai').should()
+expect = require('chai').expect
 sinon = require 'sinon'
 crypto = require 'crypto'
 querystring = require 'querystring'
-RecurlyWrapper = require "../../../../app/js/Features/Subscription/RecurlyWrapper"
-Settings = require "settings-sharelatex"
+modulePath = "../../../../app/js/Features/Subscription/RecurlyWrapper"
+SandboxedModule = require('sandboxed-module')
 tk = require("timekeeper")
 
 fixtures =
@@ -97,22 +98,37 @@ mockApiRequest = (options, callback) ->
 
 
 describe "RecurlyWrapper", ->
-	beforeEach ->
-		Settings.plans = [{
-			planCode: "collaborator"
-			name: "Collaborator"
-			features:
-				collaborators: -1
-				versioning: true
-		}]
-		Settings.defaultPlanCode =
-			collaborators: 0
-			versioning: false
+
+	before ->
+		@settings =
+			plans: [{
+				planCode: "collaborator"
+				name: "Collaborator"
+				features:
+					collaborators: -1
+					versioning: true
+			}]
+			defaultPlanCode:
+				collaborators: 0
+				versioning: false
+			apis:
+				recurly:
+					apiKey: 'nonsense'
+					privateKey: 'private_nonsense'
+
+		@RecurlyWrapper = RecurlyWrapper = SandboxedModule.require modulePath, requires:
+			"settings-sharelatex": @settings
+			"logger-sharelatex":
+				err:   sinon.stub()
+				error: sinon.stub()
+				log:   sinon.stub()
+			"request": sinon.stub()
 
 	describe "sign", ->
+
 		before (done) ->
 			tk.freeze Date.now() # freeze the time for these tests
-			RecurlyWrapper.sign({
+			@RecurlyWrapper.sign({
 				subscription :
 					plan_code : "gold"
 					name      : "$$$"
@@ -127,7 +143,7 @@ describe "RecurlyWrapper", ->
 		it "should be signed correctly", ->
 			signed = @signature.split("|")[0]
 			query = @signature.split("|")[1]
-			crypto.createHmac("sha1", Settings.apis.recurly.privateKey).update(query).digest("hex").should.equal signed
+			crypto.createHmac("sha1", @settings.apis.recurly.privateKey).update(query).digest("hex").should.equal signed
 
 		it "should be url escaped", ->
 			query = @signature.split("|")[1]
@@ -149,38 +165,39 @@ describe "RecurlyWrapper", ->
 
 	describe "_parseXml", ->
 		it "should convert different data types into correct representations", (done) ->
-			xml =
-				"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
-				"<subscription href=\"https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96\">" +
-				"  <account href=\"https://api.recurly.com/v2/accounts/1\"/>" +
-				"  <plan href=\"https://api.recurly.com/v2/plans/gold\">" +
-				"    <plan_code>gold</plan_code>" +
-				"    <name>Gold plan</name>" +
-				"  </plan>" +
-				"  <uuid>44f83d7cba354d5b84812419f923ea96</uuid>" +
-				"  <state>active</state>" +
-				"  <unit_amount_in_cents type=\"integer\">800</unit_amount_in_cents>" +
-				"  <currency>EUR</currency>" +
-				"  <quantity type=\"integer\">1</quantity>" +
-				"  <activated_at type=\"datetime\">2011-05-27T07:00:00Z</activated_at>" +
-				"  <canceled_at nil=\"nil\"></canceled_at>" +
-				"  <expires_at nil=\"nil\"></expires_at>" +
-				"  <current_period_started_at type=\"datetime\">2011-06-27T07:00:00Z</current_period_started_at>" +
-				"  <current_period_ends_at type=\"datetime\">2011-07-27T07:00:00Z</current_period_ends_at>" +
-				"  <trial_started_at nil=\"nil\"></trial_started_at>" +
-				"  <trial_ends_at nil=\"nil\"></trial_ends_at>" +
-				"  <subscription_add_ons type=\"array\">" +
-				"    <subscription_add_on>" +
-				"      <add_on_code>ipaddresses</add_on_code>" +
-				"      <quantity>10</quantity>" +
-				"      <unit_amount_in_cents>150</unit_amount_in_cents>" +
-				"    </subscription_add_on>" +
-				"  </subscription_add_ons>" +
-				"  <a name=\"cancel\" href=\"https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96/cancel\" method=\"put\"/>" +
-				"  <a name=\"terminate\" href=\"https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96/terminate\" method=\"put\"/>" +
-				"  <a name=\"postpone\" href=\"https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96/postpone\" method=\"put\"/>" +
-				"</subscription>"
-			RecurlyWrapper._parseXml xml, (error, data) ->
+			xml = """
+				<?xml version="1.0" encoding="UTF-8"?>
+				<subscription href="https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96">
+				  <account href="https://api.recurly.com/v2/accounts/1"/>
+				  <plan href="https://api.recurly.com/v2/plans/gold">
+				    <plan_code>gold</plan_code>
+				    <name>Gold plan</name>
+				  </plan>
+				  <uuid>44f83d7cba354d5b84812419f923ea96</uuid>
+				  <state>active</state>
+				  <unit_amount_in_cents type="integer">800</unit_amount_in_cents>
+				  <currency>EUR</currency>
+				  <quantity type="integer">1</quantity>
+				  <activated_at type="datetime">2011-05-27T07:00:00Z</activated_at>
+				  <canceled_at nil="nil"></canceled_at>
+				  <expires_at nil="nil"></expires_at>
+				  <current_period_started_at type="datetime">2011-06-27T07:00:00Z</current_period_started_at>
+				  <current_period_ends_at type="datetime">2011-07-27T07:00:00Z</current_period_ends_at>
+				  <trial_started_at nil="nil"></trial_started_at>
+				  <trial_ends_at nil="nil"></trial_ends_at>
+				  <subscription_add_ons type="array">
+				    <subscription_add_on>
+				      <add_on_code>ipaddresses</add_on_code>
+				      <quantity>10</quantity>
+				      <unit_amount_in_cents>150</unit_amount_in_cents>
+				    </subscription_add_on>
+				  </subscription_add_ons>
+				  <a name="cancel" href="https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96/cancel" method="put"/>
+				  <a name="terminate" href="https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96/terminate" method="put"/>
+				  <a name="postpone" href="https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96/postpone" method="put"/>
+				</subscription>
+				"""
+			@RecurlyWrapper._parseXml xml, (error, data) ->
 				data.subscription.plan.plan_code.should.equal "gold"
 				data.subscription.plan.name.should.equal "Gold plan"
 				data.subscription.uuid.should.equal "44f83d7cba354d5b84812419f923ea96"
@@ -188,32 +205,37 @@ describe "RecurlyWrapper", ->
 				data.subscription.unit_amount_in_cents.should.equal 800
 				data.subscription.currency.should.equal "EUR"
 				data.subscription.quantity.should.equal 1
+
 				data.subscription.activated_at.should.deep.equal new Date("2011-05-27T07:00:00Z")
 				should.equal data.subscription.canceled_at, null
 				should.equal data.subscription.expires_at, null
+
 				data.subscription.current_period_started_at.should.deep.equal new Date("2011-06-27T07:00:00Z")
+
 				data.subscription.current_period_ends_at.should.deep.equal new Date("2011-07-27T07:00:00Z")
 				should.equal data.subscription.trial_started_at, null
 				should.equal data.subscription.trial_ends_at, null
-				data.subscription.subscription_add_ons.should.deep.equal [{
+
+				data.subscription.subscription_add_ons[0].should.deep.equal {
 					add_on_code: "ipaddresses"
 					quantity: "10"
 					unit_amount_in_cents: "150"
-				}]
+				}
 				data.subscription.account.url.should.equal "https://api.recurly.com/v2/accounts/1"
 				data.subscription.url.should.equal "https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96"
 				data.subscription.plan.url.should.equal "https://api.recurly.com/v2/plans/gold"
 				done()
-	
+
 	describe "getSubscription", ->
+
 		describe "with proper subscription id", ->
 			before ->
-				@apiRequest = sinon.stub(RecurlyWrapper, "apiRequest", mockApiRequest)
-				RecurlyWrapper.getSubscription "44f83d7cba354d5b84812419f923ea96", (error, recurlySubscription) =>
+				@apiRequest = sinon.stub(@RecurlyWrapper, "apiRequest", mockApiRequest)
+				@RecurlyWrapper.getSubscription "44f83d7cba354d5b84812419f923ea96", (error, recurlySubscription) =>
 					@recurlySubscription = recurlySubscription
 			after ->
-				RecurlyWrapper.apiRequest.restore()
-			
+				@RecurlyWrapper.apiRequest.restore()
+
 			it "should look up the subscription at the normal API end point", ->
 				@apiRequest.args[0][0].url.should.equal "subscriptions/44f83d7cba354d5b84812419f923ea96"
 
@@ -222,12 +244,12 @@ describe "RecurlyWrapper", ->
 
 		describe "with ReculyJS token", ->
 			before ->
-				@apiRequest = sinon.stub(RecurlyWrapper, "apiRequest", mockApiRequest)
-				RecurlyWrapper.getSubscription "70db44b10f5f4b238669480c9903f6f5", {recurlyJsResult: true}, (error, recurlySubscription) =>
+				@apiRequest = sinon.stub(@RecurlyWrapper, "apiRequest", mockApiRequest)
+				@RecurlyWrapper.getSubscription "70db44b10f5f4b238669480c9903f6f5", {recurlyJsResult: true}, (error, recurlySubscription) =>
 					@recurlySubscription = recurlySubscription
 			after ->
-				RecurlyWrapper.apiRequest.restore()
-				
+				@RecurlyWrapper.apiRequest.restore()
+
 			it "should return the subscription", ->
 				@recurlySubscription.uuid.should.equal "44f83d7cba354d5b84812419f923ea96"
 
@@ -236,30 +258,30 @@ describe "RecurlyWrapper", ->
 
 		describe "with includeAccount", ->
 			beforeEach ->
-				@apiRequest = sinon.stub(RecurlyWrapper, "apiRequest", mockApiRequest)
-				RecurlyWrapper.getSubscription "44f83d7cba354d5b84812419f923ea96", {includeAccount: true}, (error, recurlySubscription) =>
+				@apiRequest = sinon.stub(@RecurlyWrapper, "apiRequest", mockApiRequest)
+				@RecurlyWrapper.getSubscription "44f83d7cba354d5b84812419f923ea96", {includeAccount: true}, (error, recurlySubscription) =>
 					@recurlySubscription = recurlySubscription
 			afterEach ->
-				RecurlyWrapper.apiRequest.restore()
+				@RecurlyWrapper.apiRequest.restore()
 
 			it "should request the account from the API", ->
 				@apiRequest.args[1][0].url.should.equal "accounts/104"
-				
+
 			it "should populate the account attribute", ->
 				@recurlySubscription.account.account_code.should.equal "104"
-			
+
 
 	describe "updateSubscription", ->
 		beforeEach (done) ->
 			@recurlySubscriptionId = "subscription-id-123"
-			@apiRequest = sinon.stub RecurlyWrapper, "apiRequest", (options, callback) =>
+			@apiRequest = sinon.stub @RecurlyWrapper, "apiRequest", (options, callback) =>
 				@requestOptions = options
 				callback null, {}, fixtures["subscriptions/44f83d7cba354d5b84812419f923ea96"]
-			RecurlyWrapper.updateSubscription @recurlySubscriptionId, { plan_code : "silver", timeframe: "now" }, (error, recurlySubscription) =>
+			@RecurlyWrapper.updateSubscription @recurlySubscriptionId, { plan_code : "silver", timeframe: "now" }, (error, recurlySubscription) =>
 				@recurlySubscription = recurlySubscription
 				done()
 		afterEach ->
-			RecurlyWrapper.apiRequest.restore()
+			@RecurlyWrapper.apiRequest.restore()
 
 		it "should send an update request to the API", ->
 			@apiRequest.called.should.equal true
@@ -275,59 +297,723 @@ describe "RecurlyWrapper", ->
 		it "should return the updated subscription", ->
 			should.exist @recurlySubscription
 			@recurlySubscription.plan.plan_code.should.equal "gold"
-			
+
 
 	describe "cancelSubscription", ->
 		beforeEach (done) ->
 			@recurlySubscriptionId = "subscription-id-123"
-			@apiRequest = sinon.stub RecurlyWrapper, "apiRequest", (options, callback) =>
+			@apiRequest = sinon.stub @RecurlyWrapper, "apiRequest", (options, callback) =>
 				options.url.should.equal "subscriptions/#{@recurlySubscriptionId}/cancel"
 				options.method.should.equal "put"
 				callback()
-			RecurlyWrapper.cancelSubscription(@recurlySubscriptionId, done)
+			@RecurlyWrapper.cancelSubscription(@recurlySubscriptionId, done)
 
 		afterEach ->
-			RecurlyWrapper.apiRequest.restore()
+			@RecurlyWrapper.apiRequest.restore()
 
 		it "should send a cancel request to the API", ->
 			@apiRequest.called.should.equal true
-	
+
 	describe "reactivateSubscription", ->
 		beforeEach (done) ->
 			@recurlySubscriptionId = "subscription-id-123"
-			@apiRequest = sinon.stub RecurlyWrapper, "apiRequest", (options, callback) =>
+			@apiRequest = sinon.stub @RecurlyWrapper, "apiRequest", (options, callback) =>
 				options.url.should.equal "subscriptions/#{@recurlySubscriptionId}/reactivate"
 				options.method.should.equal "put"
 				callback()
-			RecurlyWrapper.reactivateSubscription(@recurlySubscriptionId, done)
+			@RecurlyWrapper.reactivateSubscription(@recurlySubscriptionId, done)
 
 		afterEach ->
-			RecurlyWrapper.apiRequest.restore()
+			@RecurlyWrapper.apiRequest.restore()
 
 		it "should send a cancel request to the API", ->
 			@apiRequest.called.should.equal true
-	
-		
+
+
 
 	describe "redeemCoupon", ->
 
 		beforeEach (done) ->
 			@recurlyAccountId = "account-id-123"
 			@coupon_code = "312321312"
-			@apiRequest = sinon.stub RecurlyWrapper, "apiRequest", (options, callback) =>
+			@apiRequest = sinon.stub @RecurlyWrapper, "apiRequest", (options, callback) =>
 				options.url.should.equal "coupons/#{@coupon_code}/redeem"
 				options.body.indexOf("<account_code>#{@recurlyAccountId}</account_code>").should.not.equal -1
 				options.body.indexOf("<currency>USD</currency>").should.not.equal -1
 				options.method.should.equal "post"
 				callback()
-			RecurlyWrapper.redeemCoupon(@recurlyAccountId, @coupon_code, done)
+			@RecurlyWrapper.redeemCoupon(@recurlyAccountId, @coupon_code, done)
 
 		afterEach ->
-			RecurlyWrapper.apiRequest.restore()
+			@RecurlyWrapper.apiRequest.restore()
 
 		it "should send the request to redem the coupon", ->
 			@apiRequest.called.should.equal true
-	
 
+	describe "_addressToXml", ->
+
+		beforeEach ->
+			@address =
+				address1:    "addr_one"
+				address2:    "addr_two"
+				country:     "some_country"
+				state:       "some_state"
+				postal_code: "some_zip"
+				nonsenseKey: "rubbish"
+
+		it 'should generate the correct xml', () ->
+			result = @RecurlyWrapper._addressToXml @address
+			should.equal(
+				result,
+				"""
+				<billing_info>
+				<address1>addr_one</address1>
+				<address2 nil="nil">addr_two</address2>
+				<country>some_country</country>
+				<state>some_state</state>
+				<zip>some_zip</zip>
+				</billing_info>\n
+				"""
+			)
+
+	describe 'createSubscription', ->
+
+		beforeEach ->
+			@user =
+				_id: 'some_id'
+				email: 'user@example.com'
+			@subscriptionDetails =
+				currencyCode: "EUR"
+				plan_code:    "some_plan_code"
+				coupon_code:  ""
+				isPaypal: true
+				address:
+					address1: "addr_one"
+					address2: "addr_two"
+					country:  "some_country"
+					state:    "some_state"
+					zip:      "some_zip"
+			@subscription = {}
+			@recurly_token_id = "a-token-id"
+			@call = (callback) =>
+				@RecurlyWrapper.createSubscription(@user, @subscriptionDetails, @recurly_token_id, callback)
+
+
+		describe 'when paypal', ->
+
+			beforeEach ->
+				@subscriptionDetails.isPaypal = true
+				@_createPaypalSubscription = sinon.stub(@RecurlyWrapper, '_createPaypalSubscription')
+				@_createPaypalSubscription.callsArgWith(3, null, @subscription)
+
+			afterEach ->
+				@_createPaypalSubscription.restore()
+
+			it 'should not produce an error', (done) ->
+				@call (err, sub) =>
+					expect(err).to.equal null
+					expect(err).to.not.be.instanceof Error
+					done()
+
+			it 'should produce a subscription object', (done) ->
+				@call (err, sub) =>
+					expect(sub).to.deep.equal @subscription
+					done()
+
+			it 'should call _createPaypalSubscription', (done) ->
+				@call (err, sub) =>
+					@_createPaypalSubscription.callCount.should.equal 1
+					done()
+
+			describe "when _createPaypalSubscription produces an error", ->
+
+				beforeEach ->
+					@_createPaypalSubscription.callsArgWith(3, new Error('woops'))
+
+				it 'should produce an error', (done) ->
+					@call (err, sub) =>
+						expect(err).to.be.instanceof Error
+						done()
+
+		describe 'when not paypal', ->
+
+			beforeEach ->
+				@subscriptionDetails.isPaypal = false
+				@_createCreditCardSubscription = sinon.stub(@RecurlyWrapper, '_createCreditCardSubscription')
+				@_createCreditCardSubscription.callsArgWith(3, null, @subscription)
+
+			afterEach ->
+				@_createCreditCardSubscription.restore()
+
+			it 'should not produce an error', (done) ->
+				@call (err, sub) =>
+					expect(err).to.equal null
+					expect(err).to.not.be.instanceof Error
+					done()
+
+			it 'should produce a subscription object', (done) ->
+				@call (err, sub) =>
+					expect(sub).to.deep.equal @subscription
+					done()
+
+			it 'should call _createCreditCardSubscription', (done) ->
+				@call (err, sub) =>
+					@_createCreditCardSubscription.callCount.should.equal 1
+					done()
+
+			describe "when _createCreditCardSubscription produces an error", ->
+
+				beforeEach ->
+					@_createCreditCardSubscription.callsArgWith(3, new Error('woops'))
+
+				it 'should produce an error', (done) ->
+					@call (err, sub) =>
+						expect(err).to.be.instanceof Error
+						done()
+
+
+	describe '_createCreditCardSubscription', ->
+
+		beforeEach ->
+			@user =
+				_id: 'some_id'
+				email: 'user@example.com'
+			@subscriptionDetails =
+				currencyCode: "EUR"
+				plan_code:    "some_plan_code"
+				coupon_code:  ""
+				isPaypal: true
+				address:
+					address1: "addr_one"
+					address2: "addr_two"
+					country:  "some_country"
+					state:    "some_state"
+					zip:      "some_zip"
+			@subscription = {}
+			@recurly_token_id = "a-token-id"
+			@apiRequest = sinon.stub(@RecurlyWrapper, 'apiRequest')
+			@response =
+				statusCode: 200
+			@body = "<xml>is_bad</xml>"
+			@apiRequest.callsArgWith(1, null, @response, @body)
+			@_parseSubscriptionXml = sinon.stub(@RecurlyWrapper, '_parseSubscriptionXml')
+			@_parseSubscriptionXml.callsArgWith(1, null, @subscription)
+			@call = (callback) =>
+				@RecurlyWrapper._createCreditCardSubscription(@user, @subscriptionDetails, @recurly_token_id, callback)
+
+		afterEach ->
+			@apiRequest.restore()
+			@_parseSubscriptionXml.restore()
+
+		it 'should not produce an error', (done) ->
+			@call (err, sub) =>
+				expect(err).to.not.be.instanceof Error
+				expect(err).to.equal null
+				done()
+
+		it 'should produce a subscription', (done) ->
+			@call (err, sub) =>
+				expect(sub).to.equal @subscription
+				done()
+
+		it 'should call apiRequest', (done) ->
+			@call (err, sub) =>
+				@apiRequest.callCount.should.equal 1
+				done()
+
+		it 'should call _parseSubscriptionXml', (done) ->
+			@call (err, sub) =>
+				@_parseSubscriptionXml.callCount.should.equal 1
+				done()
+
+		describe 'when api request produces an error', ->
+
+			beforeEach ->
+				@apiRequest.callsArgWith(1, new Error('woops'))
+
+			it 'should produce an error', (done) ->
+				@call (err, sub) =>
+					expect(err).to.be.instanceof Error
+					done()
+
+			it 'should call apiRequest', (done) ->
+				@call (err, sub) =>
+					@apiRequest.callCount.should.equal 1
+					done()
+
+			it 'should not _parseSubscriptionXml', (done) ->
+				@call (err, sub) =>
+					@_parseSubscriptionXml.callCount.should.equal 0
+					done()
+
+		describe 'when parse xml produces an error', ->
+
+			beforeEach ->
+				@_parseSubscriptionXml.callsArgWith(1, new Error('woops'))
+
+			it 'should produce an error', (done) ->
+				@call (err, sub) =>
+					expect(err).to.be.instanceof Error
+					done()
+
+	describe '_createPaypalSubscription', ->
+
+		beforeEach ->
+			@checkAccountExists = sinon.stub(@RecurlyWrapper._paypal, 'checkAccountExists')
+			@createAccount = sinon.stub(@RecurlyWrapper._paypal, 'createAccount')
+			@createBillingInfo = sinon.stub(@RecurlyWrapper._paypal, 'createBillingInfo')
+			@setAddress = sinon.stub(@RecurlyWrapper._paypal, 'setAddress')
+			@createSubscription = sinon.stub(@RecurlyWrapper._paypal, 'createSubscription')
+			@user =
+				_id: 'some_id'
+				email: 'user@example.com'
+			@subscriptionDetails =
+				currencyCode: "EUR"
+				plan_code:    "some_plan_code"
+				coupon_code:  ""
+				isPaypal: true
+				address:
+					address1: "addr_one"
+					address2: "addr_two"
+					country:  "some_country"
+					state:    "some_state"
+					zip:      "some_zip"
+			@subscription = {}
+			@recurly_token_id = "a-token-id"
+
+			# set up data callbacks
+			user = @user
+			subscriptionDetails = @subscriptionDetails
+			recurly_token_id = @recurly_token_id
+
+			@checkAccountExists.callsArgWith(1, null,
+				{user, subscriptionDetails, recurly_token_id,
+				userExists: false, account: {accountCode: 'xx'}}
+			)
+			@createAccount.callsArgWith(1, null,
+				{user, subscriptionDetails, recurly_token_id,
+				userExists: false, account: {accountCode: 'xx'}}
+			)
+			@createBillingInfo.callsArgWith(1, null,
+				{user, subscriptionDetails, recurly_token_id,
+				userExists: false, account: {accountCode: 'xx'}, billingInfo: {token_id: 'abc'}}
+			)
+			@setAddress.callsArgWith(1, null,
+				{user, subscriptionDetails, recurly_token_id,
+				userExists: false, account: {accountCode: 'xx'}, billingInfo: {token_id: 'abc'}}
+			)
+			@createSubscription.callsArgWith(1, null,
+				{user, subscriptionDetails, recurly_token_id,
+				userExists: false, account: {accountCode: 'xx'}, billingInfo: {token_id: 'abc'}, subscription: @subscription}
+			)
+
+			@call = (callback) =>
+				@RecurlyWrapper._createPaypalSubscription @user, @subscriptionDetails, @recurly_token_id, callback
+
+		afterEach ->
+			@checkAccountExists.restore()
+			@createAccount.restore()
+			@createBillingInfo.restore()
+			@setAddress.restore()
+			@createSubscription.restore()
+
+		it 'should not produce an error', (done) ->
+			@call (err, sub) =>
+				expect(err).to.not.be.instanceof Error
+				done()
+
+		it 'should produce a subscription object', (done) ->
+			@call (err, sub) =>
+				expect(sub).to.not.equal null
+				expect(sub).to.equal @subscription
+				done()
+
+		it 'should call each of the paypal stages', (done) ->
+			@call (err, sub) =>
+				@checkAccountExists.callCount.should.equal 1
+				@createAccount.callCount.should.equal 1
+				@createBillingInfo.callCount.should.equal 1
+				@setAddress.callCount.should.equal 1
+				@createSubscription.callCount.should.equal 1
+				done()
+
+		describe 'when one of the paypal stages produces an error', ->
+
+			beforeEach ->
+				@createAccount.callsArgWith(1, new Error('woops'))
+
+			it 'should produce an error', (done) ->
+				@call (err, sub) =>
+					expect(err).to.be.instanceof Error
+					done()
+
+			it 'should stop calling the paypal stages after the error', (done) ->
+				@call (err, sub) =>
+					@checkAccountExists.callCount.should.equal 1
+					@createAccount.callCount.should.equal 1
+					@createBillingInfo.callCount.should.equal 0
+					@setAddress.callCount.should.equal 0
+					@createSubscription.callCount.should.equal 0
+					done()
+
+	describe 'paypal actions', ->
+
+		beforeEach ->
+			@apiRequest = sinon.stub(@RecurlyWrapper, 'apiRequest')
+			@_parseAccountXml = sinon.spy(@RecurlyWrapper, '_parseAccountXml')
+			@_parseBillingInfoXml = sinon.spy(@RecurlyWrapper, '_parseBillingInfoXml')
+			@_parseSubscriptionXml = sinon.spy(@RecurlyWrapper, '_parseSubscriptionXml')
+			@cache =
+				user: @user = {_id: 'some_id'}
+				recurly_token_id: @recurly_token_id = "some_token"
+				subscriptionDetails: @subscriptionDetails =
+					currencyCode: "EUR"
+					plan_code:    "some_plan_code"
+					coupon_code:  ""
+					isPaypal: true
+					address:
+						address1: "addr_one"
+						address2: "addr_two"
+						country:  "some_country"
+						state:    "some_state"
+						zip:      "some_zip"
+
+		afterEach ->
+			@apiRequest.restore()
+			@_parseAccountXml.restore()
+			@_parseBillingInfoXml.restore()
+			@_parseSubscriptionXml.restore()
+
+		describe '_paypal.checkAccountExists', ->
+
+			beforeEach ->
+				@call = (callback) =>
+					@RecurlyWrapper._paypal.checkAccountExists @cache, callback
+
+			describe 'when the account exists', ->
+
+				beforeEach ->
+					resultXml = '<account><account_code>abc</account_code></account>'
+					@apiRequest.callsArgWith(1, null, {statusCode: 200}, resultXml)
+
+				it 'should not produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.not.be.instanceof Error
+						done()
+
+				it 'should call apiRequest', (done) ->
+					@call (err, result) =>
+						@apiRequest.callCount.should.equal 1
+						done()
+
+				it 'should call _parseAccountXml', (done) ->
+					@call (err, result) =>
+						@RecurlyWrapper._parseAccountXml.callCount.should.equal 1
+						done()
+
+				it 'should add the account to the cumulative result', (done) ->
+					@call (err, result) =>
+						expect(result.account).to.not.equal null
+						expect(result.account).to.not.equal undefined
+						expect(result.account).to.deep.equal {
+							account_code: 'abc'
+						}
+						done()
+
+				it 'should set userExists to true', (done) ->
+					@call (err, result) =>
+						expect(result.userExists).to.equal true
+						done()
+
+			describe 'when the account does not exist', ->
+
+				beforeEach ->
+					@apiRequest.callsArgWith(1, new Error('not found'), {statusCode: 404}, '')
+
+				it 'should not produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.not.be.instanceof Error
+						done()
+
+				it 'should call apiRequest', (done) ->
+					@call (err, result) =>
+						@apiRequest.callCount.should.equal 1
+						@apiRequest.firstCall.args[0].method.should.equal 'GET'
+						done()
+
+				it 'should not call _parseAccountXml', (done) ->
+					@call (err, result) =>
+						@RecurlyWrapper._parseAccountXml.callCount.should.equal 0
+						done()
+
+				it 'should not add the account to result', (done) ->
+					@call (err, result) =>
+						expect(result.account).to.equal undefined
+						done()
+
+				it 'should set userExists to false', (done) ->
+					@call (err, result) =>
+						expect(result.userExists).to.equal false
+						done()
+
+			describe 'when apiRequest produces an error', ->
+
+				beforeEach ->
+					@apiRequest.callsArgWith(1, new Error('woops'), {statusCode: 500})
+
+				it 'should produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.be.instanceof Error
+						done()
+
+		describe '_paypal.createAccount', ->
+
+			beforeEach ->
+				@call = (callback) =>
+					@RecurlyWrapper._paypal.createAccount @cache, callback
+
+			describe 'when address is missing from subscriptionDetails', ->
+
+				beforeEach ->
+					@cache.subscriptionDetails.address = null
+
+				it 'should produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.be.instanceof Error
+						done()
+
+			describe 'when account already exists', ->
+
+				beforeEach ->
+					@cache.userExists = true
+					@cache.account =
+						account_code: 'abc'
+
+				it 'should not produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.not.be.instanceof Error
+						done()
+
+				it 'should produce cache object', (done) ->
+					@call (err, result) =>
+						expect(result).to.deep.equal @cache
+						expect(result.account).to.deep.equal {
+							account_code: 'abc'
+						}
+						done()
+
+				it 'should not call apiRequest', (done) ->
+					@call (err, result) =>
+						@apiRequest.callCount.should.equal 0
+						done()
+
+				it 'should not call _parseAccountXml', (done) ->
+					@call (err, result) =>
+						@RecurlyWrapper._parseAccountXml.callCount.should.equal 0
+						done()
+
+			describe 'when account does not exist', ->
+
+				beforeEach ->
+					@cache.userExists = false
+					resultXml = '<account><account_code>abc</account_code></account>'
+					@apiRequest.callsArgWith(1, null, {statusCode: 200}, resultXml)
+
+				it 'should not produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.not.be.instanceof Error
+						done()
+
+				it 'should call apiRequest', (done) ->
+					@call (err, result) =>
+						@apiRequest.callCount.should.equal 1
+						@apiRequest.firstCall.args[0].method.should.equal 'POST'
+						done()
+
+				it 'should call _parseAccountXml', (done) ->
+					@call (err, result) =>
+						@RecurlyWrapper._parseAccountXml.callCount.should.equal 1
+						done()
+
+				describe 'when apiRequest produces an error', ->
+
+					beforeEach ->
+						@apiRequest.callsArgWith(1, new Error('woops'), {statusCode: 500})
+
+					it 'should produce an error', (done) ->
+						@call (err, result) =>
+							expect(err).to.be.instanceof Error
+							done()
+
+		describe '_paypal.createBillingInfo', ->
+
+			beforeEach ->
+				@cache.account =
+					account_code: 'abc'
+				@call = (callback) =>
+					@RecurlyWrapper._paypal.createBillingInfo @cache, callback
 
+			describe 'when account_code is missing from cache', ->
 
+				beforeEach ->
+					@cache.account.account_code = null
+
+				it 'should produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.be.instanceof Error
+						done()
+
+			describe 'when all goes well', ->
+
+				beforeEach ->
+					resultXml = '<billing_info><a>1</a></billing_info>'
+					@apiRequest.callsArgWith(1, null, {statusCode: 200}, resultXml)
+
+				it 'should not produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.not.be.instanceof Error
+						done()
+
+				it 'should call apiRequest', (done) ->
+					@call (err, result) =>
+						@apiRequest.callCount.should.equal 1
+						@apiRequest.firstCall.args[0].method.should.equal 'POST'
+						done()
+
+				it 'should call _parseBillingInfoXml', (done) ->
+					@call (err, result) =>
+						@RecurlyWrapper._parseBillingInfoXml.callCount.should.equal 1
+						done()
+
+				it 'should set billingInfo on cache', (done) ->
+					@call (err, result) =>
+						expect(result.billingInfo).to.deep.equal {
+							a: "1"
+						}
+						done()
+
+			describe 'when apiRequest produces an error', ->
+
+				beforeEach ->
+					@apiRequest.callsArgWith(1, new Error('woops'), {statusCode: 500})
+
+				it 'should produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.be.instanceof Error
+						done()
+
+		describe '_paypal.setAddress', ->
+
+			beforeEach ->
+				@cache.account =
+					account_code: 'abc'
+				@cache.billingInfo = {}
+				@call = (callback) =>
+					@RecurlyWrapper._paypal.setAddress @cache, callback
+
+			describe 'when account_code is missing from cache', ->
+
+				beforeEach ->
+					@cache.account.account_code = null
+
+				it 'should produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.be.instanceof Error
+						done()
+
+			describe 'when address is missing from subscriptionDetails', ->
+
+				beforeEach ->
+					@cache.subscriptionDetails.address = null
+
+				it 'should produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.be.instanceof Error
+						done()
+
+			describe 'when all goes well', ->
+
+				beforeEach ->
+					resultXml = '<billing_info><city>London</city></billing_info>'
+					@apiRequest.callsArgWith(1, null, {statusCode: 200}, resultXml)
+
+				it 'should not produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.not.be.instanceof Error
+						done()
+
+				it 'should call apiRequest', (done) ->
+					@call (err, result) =>
+						@apiRequest.callCount.should.equal 1
+						@apiRequest.firstCall.args[0].method.should.equal 'PUT'
+						done()
+
+				it 'should call _parseBillingInfoXml', (done) ->
+					@call (err, result) =>
+						@RecurlyWrapper._parseBillingInfoXml.callCount.should.equal 1
+						done()
+
+				it 'should set billingInfo on cache', (done) ->
+					@call (err, result) =>
+						expect(result.billingInfo).to.deep.equal {
+							city: 'London'
+						}
+						done()
+
+			describe 'when apiRequest produces an error', ->
+
+				beforeEach ->
+					@apiRequest.callsArgWith(1, new Error('woops'), {statusCode: 500})
+
+				it 'should produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.be.instanceof Error
+						done()
+
+		describe '_paypal.createSubscription', ->
+
+			beforeEach ->
+				@cache.account =
+					account_code: 'abc'
+				@cache.billingInfo = {}
+				@call = (callback) =>
+					@RecurlyWrapper._paypal.createSubscription @cache, callback
+
+			describe 'when all goes well', ->
+
+				beforeEach ->
+					resultXml = '<subscription><a>1</a></subscription>'
+					@apiRequest.callsArgWith(1, null, {statusCode: 200}, resultXml)
+
+				it 'should not produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.not.be.instanceof Error
+						done()
+
+				it 'should call apiRequest', (done) ->
+					@call (err, result) =>
+						@apiRequest.callCount.should.equal 1
+						@apiRequest.firstCall.args[0].method.should.equal 'POST'
+						done()
+
+				it 'should call _parseSubscriptionXml', (done) ->
+					@call (err, result) =>
+						@RecurlyWrapper._parseSubscriptionXml.callCount.should.equal 1
+						done()
+
+				it 'should set subscription on cache', (done) ->
+					@call (err, result) =>
+						expect(result.subscription).to.deep.equal {
+							a: "1"
+						}
+						done()
+
+			describe 'when apiRequest produces an error', ->
+
+				beforeEach ->
+					@apiRequest.callsArgWith(1, new Error('woops'), {statusCode: 500})
+
+				it 'should produce an error', (done) ->
+					@call (err, result) =>
+						expect(err).to.be.instanceof Error
+						done()