Index: imaps-frontend/node_modules/relateurl/lib/parse/host.js
===================================================================
--- imaps-frontend/node_modules/relateurl/lib/parse/host.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/relateurl/lib/parse/host.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,26 @@
+"use strict";
+
+function parseHost(urlObj, options)
+{
+	// TWEAK :: condition only for speed optimization
+	if (options.ignore_www)
+	{
+		var host = urlObj.host.full;
+		
+		if (host)
+		{
+			var stripped = host;
+			
+			if (host.indexOf("www.") === 0)
+			{
+				stripped = host.substr(4);
+			}
+			
+			urlObj.host.stripped = stripped;
+		}
+	}
+}
+
+
+
+module.exports = parseHost;
Index: imaps-frontend/node_modules/relateurl/lib/parse/hrefInfo.js
===================================================================
--- imaps-frontend/node_modules/relateurl/lib/parse/hrefInfo.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/relateurl/lib/parse/hrefInfo.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,20 @@
+"use strict";
+
+function hrefInfo(urlObj)
+{
+	var minimumPathOnly     = (!urlObj.scheme && !urlObj.auth && !urlObj.host.full && !urlObj.port);
+	var minimumResourceOnly = (minimumPathOnly && !urlObj.path.absolute.string);
+	var minimumQueryOnly    = (minimumResourceOnly && !urlObj.resource);
+	var minimumHashOnly     = (minimumQueryOnly && !urlObj.query.string.full.length);
+	var empty               = (minimumHashOnly && !urlObj.hash);
+	
+	urlObj.extra.hrefInfo.minimumPathOnly     = minimumPathOnly;
+	urlObj.extra.hrefInfo.minimumResourceOnly = minimumResourceOnly;
+	urlObj.extra.hrefInfo.minimumQueryOnly    = minimumQueryOnly;
+	urlObj.extra.hrefInfo.minimumHashOnly     = minimumHashOnly;
+	urlObj.extra.hrefInfo.empty = empty;
+}
+
+
+
+module.exports = hrefInfo;
Index: imaps-frontend/node_modules/relateurl/lib/parse/index.js
===================================================================
--- imaps-frontend/node_modules/relateurl/lib/parse/index.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/relateurl/lib/parse/index.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,58 @@
+"use strict";
+
+var hrefInfo   = require("./hrefInfo");
+var parseHost  = require("./host");
+var parsePath  = require("./path");
+var parsePort  = require("./port");
+var parseQuery = require("./query");
+var parseUrlString = require("./urlstring");
+var pathUtils      = require("../util/path");
+
+
+
+function parseFromUrl(url, options, fallback)
+{
+	if (url)
+	{
+		var urlObj = parseUrl(url, options);
+		
+		// Because the following occurs in the relate stage for "to" URLs,
+		// such had to be mostly duplicated here
+		
+		var pathArray = pathUtils.resolveDotSegments(urlObj.path.absolute.array);
+		
+		urlObj.path.absolute.array  = pathArray;
+		urlObj.path.absolute.string = "/" + pathUtils.join(pathArray);
+		
+		return urlObj;
+	}
+	else
+	{
+		return fallback;
+	}
+}
+
+
+
+function parseUrl(url, options)
+{
+	var urlObj = parseUrlString(url, options);
+	
+	if (urlObj.valid===false) return urlObj;
+	
+	parseHost(urlObj, options);
+	parsePort(urlObj, options);
+	parsePath(urlObj, options);
+	parseQuery(urlObj, options);
+	hrefInfo(urlObj);
+	
+	return urlObj;
+}
+
+
+
+module.exports =
+{
+	from: parseFromUrl,
+	to:   parseUrl
+};
Index: imaps-frontend/node_modules/relateurl/lib/parse/path.js
===================================================================
--- imaps-frontend/node_modules/relateurl/lib/parse/path.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/relateurl/lib/parse/path.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,100 @@
+"use strict";
+
+function isDirectoryIndex(resource, options)
+{
+	var verdict = false;
+	
+	options.directoryIndexes.every( function(index)
+	{
+		if (index === resource)
+		{
+			verdict = true;
+			return false;
+		}
+		
+		return true;
+	});
+	
+	return verdict;
+}
+
+
+
+function parsePath(urlObj, options)
+{
+	var path = urlObj.path.absolute.string;
+	
+	if (path)
+	{
+		var lastSlash = path.lastIndexOf("/");
+		
+		if (lastSlash > -1)
+		{
+			if (++lastSlash < path.length)
+			{
+				var resource = path.substr(lastSlash);
+				
+				if (resource!=="." && resource!=="..")
+				{
+					urlObj.resource = resource;
+					path = path.substr(0, lastSlash);
+				}
+				else
+				{
+					path += "/";
+				}
+			}
+			
+			urlObj.path.absolute.string = path;
+			urlObj.path.absolute.array = splitPath(path);
+		}
+		else if (path==="." || path==="..")
+		{
+			// "..?var", "..#anchor", etc ... not "..index.html"
+			path += "/";
+			
+			urlObj.path.absolute.string = path;
+			urlObj.path.absolute.array = splitPath(path);
+		}
+		else
+		{
+			// Resource-only
+			urlObj.resource = path;
+			urlObj.path.absolute.string = null;
+		}
+		
+		urlObj.extra.resourceIsIndex = isDirectoryIndex(urlObj.resource, options);
+	}
+	// Else: query/hash-only or empty
+}
+
+
+
+function splitPath(path)
+{
+	// TWEAK :: condition only for speed optimization
+	if (path !== "/")
+	{
+		var cleaned = [];
+		
+		path.split("/").forEach( function(dir)
+		{
+			// Cleanup -- splitting "/dir/" becomes ["","dir",""]
+			if (dir !== "")
+			{
+				cleaned.push(dir);
+			}
+		});
+		
+		return cleaned;
+	}
+	else
+	{
+		// Faster to skip the above block and just create an array
+		return [];
+	}
+}
+
+
+
+module.exports = parsePath;
Index: imaps-frontend/node_modules/relateurl/lib/parse/port.js
===================================================================
--- imaps-frontend/node_modules/relateurl/lib/parse/port.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/relateurl/lib/parse/port.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,32 @@
+"use strict";
+
+function parsePort(urlObj, options)
+{
+	var defaultPort = -1;
+	
+	for (var i in options.defaultPorts)
+	{
+		if ( i===urlObj.scheme && options.defaultPorts.hasOwnProperty(i) )
+		{
+			defaultPort = options.defaultPorts[i];
+			break;
+		}
+	}
+	
+	if (defaultPort > -1)
+	{
+		// Force same type as urlObj.port
+		defaultPort = defaultPort.toString();
+		
+		if (urlObj.port === null)
+		{
+			urlObj.port = defaultPort;
+		}
+		
+		urlObj.extra.portIsDefault = (urlObj.port === defaultPort);
+	}
+}
+
+
+
+module.exports = parsePort;
Index: imaps-frontend/node_modules/relateurl/lib/parse/query.js
===================================================================
--- imaps-frontend/node_modules/relateurl/lib/parse/query.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/relateurl/lib/parse/query.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,53 @@
+"use strict";
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+
+
+function parseQuery(urlObj, options)
+{
+	urlObj.query.string.full = stringify(urlObj.query.object, false);
+	
+	// TWEAK :: condition only for speed optimization
+	if (options.removeEmptyQueries)
+	{
+		urlObj.query.string.stripped = stringify(urlObj.query.object, true);
+	}
+}
+
+
+
+function stringify(queryObj, removeEmptyQueries)
+{
+	var count = 0;
+	var str = "";
+	
+	for (var i in queryObj)
+	{
+		if ( i!=="" && hasOwnProperty.call(queryObj, i)===true )
+		{
+			var value = queryObj[i];
+			
+			if (value !== "" || !removeEmptyQueries)
+			{
+				str += (++count===1) ? "?" : "&";
+				
+				i = encodeURIComponent(i);
+				
+				if (value !== "")
+				{
+					str += i +"="+ encodeURIComponent(value).replace(/%20/g,"+");
+				}
+				else
+				{
+					str += i;
+				}
+			}
+		}
+	}
+	
+	return str;
+}
+
+
+
+module.exports = parseQuery;
Index: imaps-frontend/node_modules/relateurl/lib/parse/urlstring.js
===================================================================
--- imaps-frontend/node_modules/relateurl/lib/parse/urlstring.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/relateurl/lib/parse/urlstring.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,146 @@
+"use strict";
+
+var _parseUrl = require("url").parse;
+
+
+
+/*
+	Customize the URL object that Node generates
+	because:
+	
+	* necessary data for later
+	* urlObj.host is useless
+	* urlObj.hostname is too long
+	* urlObj.path is useless
+	* urlObj.pathname is too long
+	* urlObj.protocol is inaccurate; should be called "scheme"
+	* urlObj.search is mostly useless
+*/
+function clean(urlObj)
+{
+	var scheme = urlObj.protocol;
+	
+	if (scheme)
+	{
+		// Remove ":" suffix
+		if (scheme.indexOf(":") === scheme.length-1)
+		{
+			scheme = scheme.substr(0, scheme.length-1);
+		}
+	}
+	
+	urlObj.host =
+	{
+		// TODO :: unescape(encodeURIComponent(s)) ? ... http://ecmanaut.blogspot.ca/2006/07/encoding-decoding-utf8-in-javascript.html
+		full: urlObj.hostname,
+		stripped: null
+	};
+	
+	urlObj.path =
+	{
+		absolute:
+		{
+			array: null,
+			string: urlObj.pathname
+		},
+		relative:
+		{
+			array: null,
+			string: null
+		}
+	};
+	
+	urlObj.query =
+	{
+		object: urlObj.query,
+		string:
+		{
+			full: null,
+			stripped: null
+		}
+	};
+	
+	urlObj.extra =
+	{
+		hrefInfo:
+		{
+			minimumPathOnly: null,
+			minimumResourceOnly: null,
+			minimumQueryOnly: null,
+			minimumHashOnly: null,
+			empty: null,
+			
+			separatorOnlyQuery: urlObj.search==="?"
+		},
+		portIsDefault: null,
+		relation:
+		{
+			maximumScheme: null,
+			maximumAuth: null,
+			maximumHost: null,
+			maximumPort: null,
+			maximumPath: null,
+			maximumResource: null,
+			maximumQuery: null,
+			maximumHash: null,
+			
+			minimumScheme: null,
+			minimumAuth: null,
+			minimumHost: null,
+			minimumPort: null,
+			minimumPath: null,
+			minimumResource: null,
+			minimumQuery: null,
+			minimumHash: null,
+			
+			overridesQuery: null
+		},
+		resourceIsIndex: null,
+		slashes: urlObj.slashes
+	};
+	
+	urlObj.resource = null;
+	urlObj.scheme = scheme;
+	delete urlObj.hostname;
+	delete urlObj.pathname;
+	delete urlObj.protocol;
+	delete urlObj.search;
+	delete urlObj.slashes;
+	
+	return urlObj;
+}
+
+
+
+function validScheme(url, options)
+{
+	var valid = true;
+	
+	options.rejectedSchemes.every( function(rejectedScheme)
+	{
+		valid = !(url.indexOf(rejectedScheme+":") === 0);
+		
+		// Break loop
+		return valid;
+	});
+	
+	return valid;
+}
+
+
+
+function parseUrlString(url, options)
+{
+	if ( validScheme(url,options) )
+	{
+		return clean( _parseUrl(url, true, options.slashesDenoteHost) );
+	}
+	else
+	{
+		return {href:url, valid:false};
+	}
+}
+
+
+
+module.exports = parseUrlString;
