@@ -19,6 +19,10 @@ function splitString (str, splitIndex) {
 	return [str.substring(0, splitIndex), str.substring(splitIndex)];
 }
 
+function trimRight (str) {
+	return str.replace(/\s+$/, '')
+}
+
 /**
  * Take a relative or absolute URL and pull it into it's indivisual pieces
  *
/* eslint max-statements:["error",18], complexity:["error",27], no-script-url:0 */
/**
 * Check if a string contains mostly numbers
 */
function isMostlyNumbers (str = '') {
	return (
		str.length !== 0 &&
		(str.match(/[0-9]/g) || '').length >= str.length / 2
	);
}

/**
 * Spit a string into an array with two pieces, at a given index
 * @param String	string to split
 * @param Number	index at which to split
 * @return Array
 */
function splitString (str, splitIndex) {
	return [str.substring(0, splitIndex), str.substring(splitIndex)];
}

/**
 * Take a relative or absolute URL and pull it into it's indivisual pieces
 *
 * @param url (string)
 * @return urlPieces
 *	 .protocol	The protocol used, e.g. 'https://'
 *	 .domain		Domain name including sub domains and TLD, e.g. 'docs.deque.com'
 *	 .port			The port number, e.g. ':8080'
 *	 .path			Path after the domain, e.g. '/home.html'
 *	 .query		 Query string, e.g. '?user=admin&password=pass'
 *	 .hash			Hash / internal reference, e.g. '#footer'
 */
function uriParser (url) {
	let original = url;
	let protocol = '', domain = '', port = '', path = '', query = '', hash = '';
	if (url.includes('#')) {
		[url, hash] = splitString(url, url.indexOf('#'));
	}

	if (url.includes('?')) {
		[url, query] = splitString(url, url.indexOf('?'));
	}

	if (url.includes('://')) {
		[protocol, url] = url.split('://');
		[domain, url] = splitString(url, url.indexOf('/'));
	} else if (url.substr(0,2) === '//') {
		url = url.substr(2);
		[domain, url] = splitString(url, url.indexOf('/'));
	}

	if (domain.substr(0,4) === 'www.') {
		domain = domain.substr(4);
	}

	if (domain && domain.includes(':')) {
		[domain, port] = splitString(domain, domain.indexOf(':'));
	}

	path = url; // Whatever is left, must be the path
	return { original, protocol, domain, port, path, query, hash };
}

/**
 * Try to, at the end of the URI, find a string that a user can identify the URI by
 *
 * @param uri			 The URI to use
 * @param options
 *	 .currentDomain	The current domain name (optional)
 *	 .maxLength			Max length of the returned string (default: 25)
 * @return string	 A portion at the end of the uri, no longer than maxLength
 */
axe.utils.getFriendlyUriEnd = function getFriendlyUriEnd (uri = '', options = {}) {
	if (// Skip certain URIs:
		uri.length <= 1 || // very short
		uri.substr(0, 5) === 'data:' || // data URIs are unreadable
		uri.substr(0, 11) === 'javascript:' || // JS isn't a URL
		uri.includes('?') // query strings aren't very readable either
	) {
		return;
	}

	const { currentDomain, maxLength = 25 } = options;
	const { path, domain, hash } = uriParser(uri);
	// Split the path at the last / that has text after it
	const pathEnd = path.substr(
		path.substr(0, path.length-2).lastIndexOf('/') + 1
	);

	if (hash) {
		if (pathEnd && (pathEnd + hash).length <= maxLength) {
			return pathEnd + hash;
		} else if (pathEnd.length < 2 && hash.length > 2 && hash.length <= maxLength) {
			return hash;
		} else {
			return;
		}
	} else if (domain && domain.length < maxLength && path.length <= 1) {// '' or '/'
		return domain + path;
	}

	// See if the domain should be returned
	if (path === '/' + pathEnd &&
		domain && currentDomain &&
		domain !== currentDomain &&
		(domain + path).length <= maxLength
	) {
		return domain + path;
	}

	const lastDotIndex = pathEnd.lastIndexOf('.');
	if (// Exclude very short or very long string
		(lastDotIndex === -1 || lastDotIndex > 1) &&
		(lastDotIndex !== -1 || pathEnd.length > 2) &&
		pathEnd.length <= maxLength &&
		// Exclude index files
		!pathEnd.match(/index(\.[a-zA-Z]{2-4})?/) &&
		// Exclude files that are likely to be database IDs
		!isMostlyNumbers(pathEnd)
	) {
		return pathEnd;
	}
};

Use prototype methods - `.trimRight()` or `.trimEnd()`? Need IE Support?