function guessFrom (no, strict) {

	var results = {};
	var best = false;
	var more = false;
	var newNo = false;

	if (!no) return results;
	no = no.replace(/ /g,'');

	if (no.match(/^1Z[0-9A-Z]{16}$/i)) {
		best = ['www.ups.com'];
	} else if (matches = no.match(/^([A-Z]{2}) ?[0-9]{3} ?[0-9]{3} ?[0-9]{3} ?([A-Z]{2})$/i)) {
		var country = matches[2].toUpperCase();
		var ems = ['www.usps.com','em.canadapost.ca','www.royalmail.com','www.parcelforce.com','www.post.at',
			'www.japanpost.jp/yupack','www.chronopost.fr','www.laposte.fr','www.poste.it','auspost.com.au',
			'www.postnl.nl','www.posten.no','www.posten.se','www.post.ch','www.postdanmark.dk','www.hongkongpost.com'];
		switch (country) {
			case 'AT': best = ['www.post.at']; more = ems; break;
			case 'AU': best = ['auspost.com.au']; more = ems; break;
			case 'CA': best = ['em.canadapost.ca']; more = ems; break;
			case 'CH': best = ['www.post.ch']; more = ems; break;
			case 'DE': best = ['nolp.dhl.de']; more = ems; break;
			case 'DK': best = ['www.postdanmark.dk']; more = ems; break;
			case 'FR': best = ['www.chronopost.fr','www.laposte.fr']; more = ems; break;
			case 'GB': best = ['www.royalmail.com','www.parcelforce.com']; more = ems; break;
			case 'HK': best = ['www.hongkongpost.com']; more = ems; break;
			case 'IT': best = ['www.poste.it']; more = ems; break;
			case 'JB': best = ['www.chronopost.fr','www.laposte.fr']; break;
			case 'JP': best = ['www.japanpost.jp/yupack']; more = ems; break;
			case 'NL': best = ['www.postnl.nl']; more = ems; break;
			case 'NO': best = ['www.posten.no']; more = ems; break;
			case 'SE': best = ['www.posten.se']; more = ems; break;
			case 'US': best = ['www.usps.com']; more = ems; break;
			default: more = ems; break;
		}
	} else if (no.match(/^96(11|12|13)[0-9]{18}$/)) { // && checkFedEx15(no.substring(7))) { // 22 digits, last digit is a check
		best = ['www.fedex.com'];
	} else if (matches = no.match(/^(420[0-9]{5,9})?(9[0-9]{21,25})$/)) { // 22, 26, 30, 34, or 38 digits
		// Can remove the 420+5 then validate with checkUSPS
		var stc = getUSPSSTC(no);
		best = ['www.usps.com'];
		if ((stc >= 612 && stc <= 652) || (stc >= 743 && stc <= 749) || (stc >= 778 && stc <= 789)) {
			more = ['www.fedex.com','www.dhlglobalmail.com','www.ups.com'];
		}
		if (matches[1] && checkUSPS(matches[2])) newNo = matches[2];
	} else if (no.match(/^[0-9]{20}$/) && checkUSPS('92'+no)) { // 20 digits, FedEx SmartPost
		// Won't work with USPS unless 92 is added
		best = ['www.fedex.com'];
	} else if (no.match(/^00340(161|433|434)[0-9]{12}$/)) { // 20 digits
		best = ['nolp.dhl.de'];
	} else if (no.match(/^J?JD[0-9]{2}\.?[0-9]{2}\.?[0-9]{3}\.?[0-9]{3}\.?[0-9]{3}\.?([0-9]{3,10})?$/i)) {
		best = ['nolp.dhl.de','www.dhl.com','www.yodel.co.uk'];
	} else if (no.match(/^[0-1][1-3][0-9]{18}$/)) { // 20 digits, ^03|13 validates with checkUSPS
		best = ['www.usps.com'];
	} else if (no.match(/^70[0-9]{18}$/)) { // 20 digits, Certified Mail, validates with checkUSPS
		best = ['www.usps.com'];
	} else if (no.match(/^230[0-9]{17}$/)) { // 20 digits, Signature Confirmation, validates with checkUSPS
		best = ['www.usps.com'];
	} else if (no.match(/^[0-9]{15}$/)) { // 15 digits
		if (checkFedEx15(no)) best = ['www.fedex.com'];
		more = ['www.hlg.de','www.dpd.com','www.dpd.co.uk'];
	} else if (no.match(/^[0-9]{12}$/)) { // 12 digits, including UPS InfoNotice
		if (checkFedEx12(no)) {
			best = ['www.fedex.com'];
		} else if (no.match(/^330[0-9]{9}$/)) {
			best = ['www.purolator.com'];
		}
		more = ['nolp.dhl.de','www.kuronekoyamato.co.jp','www.sagawa-exp.co.jp','www.hlg.de',
			'www.japanpost.jp/yupack','www.gls-germany.com','www.ups.com'];
	} else if (no.match(/^DT[0-9]{12}$/)) { // DT followed by 12 digits, FedEx delivery notice number
		best = ['www.fedex.com'];
	} else if (no.match(/^(00)?[0-3](40|50|57|70|73)[0-9]{15}$/)) { // 18 or 20 digits
		best = ['www.posten.no','www.postdanmark.dk','www.posten.se'];
		if (no.match(/^[0-9]{18}$/)) best.push('www.dhl.com'); // 18 digits, odds are low
	} else if (no.match(/^(98|99)\.?[0-9]{2}\.?[0-9]{6}\.?[0-9]{8}$/)) { // 18 digits possibly with periods
		best = ['www.post.ch'];
		if (no.match(/^[0-9]{18}$/)) best.push('www.dhl.com'); // 18 digits, odds are low
	} else if (no.match(/^[0-9]{18}$/)) { // 18 digits
		more = ['www.dhl.com','www.ups.com'];
	} else if (no.match(/^[0-9]{10}$/)) { // 10 digits
		// www.tnt.com/ref always starts with 8? apple always starts with 71?
		more = ['www.dhl.com','www.aramex.com','www.tnt.com/ref','www.apple.com','www.apple.com/japan',
			'www.apple.com/hk'];
	} else if (no.match(/^P[0-9]{12}$/i)) { // P plus 12 digits
		best = ['nolp.dhl.de'];
	} else if (no.match(/^[0-9]{3}-[0-9]{7}-[0-9]{7}$/)) {
		best = ['www.amazon.com','www.amazon.de','www.amazon.co.jp','www.amazon.co.uk','www.amazon.fr',
			'www.amazon.it','www.amazon.ca','www.amazon.es','www.amazon.at','www.amazon.in'];
	} else if (no.match(/^WR?[0-9]{8,9}$/i)) {
		best = ['www.apple.com','www.apple.com/japan','www.apple.com/hk'];
	} else if (no.match(/^7[0-9]{16}$/)) { // 17 digits
		best = ['www.posten.no'];
	} else if (no.match(/^[0-9]{4}-[0-9]{4}-[0-9]{4}$/)) { // 12 digits with dashes
		best = ['www.kuronekoyamato.co.jp','www.sagawa-exp.co.jp','www.japanpost.jp/yupack'];
	} else if (no.match(/^[0-9]{3}-[0-9]{2}-[0-9]{5}-[0-9]{1}$/)) { // 11 digits with dashes
		best = ['www.japanpost.jp/yupack'];
	} else if (no.match(/^[0-9]{11}$/)) { // 11 digits
		best = ['www.gls-germany.com','www.japanpost.jp/yupack','www.dhlglobalmail.com'];
	} else if (no.match(/^[0-9]{14}[A-Z]$/i)) { // 14 digits plus letter
		best = ['www.dpd.com','www.dpd.co.uk'];
	} else if (no.match(/^[0-9]{14}$/)) { // 14 digits
		more = ['www.dpd.com','www.dpd.co.uk','www.hlg.de','www.posten.se','www.gls-germany.com'];
	} else if (no.match(/^000[0-9]{4}0530800815[0-9]{4}101[0-9]{3}[A-Z0-9]$/i)) {
		best = ['www.dpd.com'];
	} else if (no.match(/^[0-9]{16}$/)) { // 16 digits
		more = ['em.canadapost.ca','www.hlg.de','www.dhlglobalmail.com'];
	} else if (no.match(/^[0-9]{9}$/)) { // 9 digits, UPS freight PRO number
		more = ['www.tnt.com','www.ups.com'];
	} else if (no.match(/^[0-9]{2}.[0-9]{3}.[0-9].[0-9]{2}.[0-9]{5}.[0-9]$/)) { // 14 digits with periods
		best = ['www.hlg.de'];
	} else if (no.match(/^(C1|D1)[0-9]{13}$/i)) { // 1 letter and 14 digits
		best = ['www.ontrac.com'];
	} else if (no.match(/^(L[A-Z][0-9]{8}|Q[0-9]{8}[A-Z]?|[0-9]LS[0-9]{12,17}|[0-9]LS[0-9]{13}-[0-9])$/i)) { // 9, 10, 11, 15, 20, or 16 characters with a -1
		best = ['www.lasership.com'];
	} else if (no.match(/^3S[A-Z]{4}[0-9]{6,9}$/i)) {
		best = ['www.postnl.nl'];
	} else if (no.match(/^[0-9]{5}\.[0-9]{2}\.([0-9]{4}|[0-9]{6})$/)) { // 11 or 13 digits with periods
		best = ['www.gls-germany.com'];
	} else if (no.match(/^([A-Z]{3}[0-9]{5})$/i)) { // 8 characters, 3 letters and 5 digits
		best = ['www.gls-germany.com'];
	} else if (no.match(/^([0-9]{1}[A-Z90-9]{5}|[A-Z][0-9A-Z]{7})$/i) && no.match(/[0-9]/) && no.match(/[A-Z]/)) { // 6 or 8 characters
		best = ['www.gls-germany.com'];
	} else if (no.match(/^(PB[A-Z]{2}[0-9]{7}001|TW[A-Z]{2}[0-9]{7}GB)$/i)) {
		best = ['www.parcelforce.com'];
	} else if (no.match(/^[A-Z]{2}[0-9]{7}$/i)) {
		more = ['www.parcelforce.com'];
	} else if (no.match(/^[A-Z]{3}[0-1]00[0-9]{6}$/i)) {
		best = ['www.purolator.com'];
	} else if (no.match(/^GM[0-9]{18}$/i)) { // GM then 18 digits
		best = ['www.dhlglobalmail.com'];
	} else if (no.match(/^[0-9][A-Z][0-9]{11}$/i)) { // 13 characters, second is a letter
		best = ['www.laposte.fr'];
	} else if (no.match(/^[0-9]{11}DK$/i)) {
		best = ['www.postdanmark.dk'];
	} else if (no.match(/^10[1-2][0-9]{19}$/i)) { // 22 digits
		best = ['www.post.at'];
	} else if (no.match(/^[0-9][A-Z]{2}[0-9]{7}([0-9]{6})?$/i)) { // 10 or 16 characters
		best = ['auspost.com.au'];
	} else if (no.match(/^APK[0-9]{7}$/)) { // 10 digits, starting with APK
		best = ['www.tntexpress.com.au'];
	} else if (no.match(/^AD[0-9]{9}[A-Z]{0,4}$/i)) {
		best = ['www.adobe.com'];
	} else if (no.match(/^K[0-9]{10}$/i)) { // K then 10 digits
		best = ['www.ups.com'];
	} else if (no.match(/^V0[0-9]{9}$/i)) { // V then 10 digits, might be the same as above
		best = ['www.ups.com'];
	} else if (no.match(/^TBA([0-9]{3}|[A-Z]{3})[0-9]{9}$/i) || no.match(/^Q[0-9]{11}$/i)) {
		if (!strict) {
			best = ['www.amazon.com/url'];
			newNo = '';
		}
	} else if (no.match(/^[0-9]{32}$/) && checkFedEx12(no.substring(16,28))) { // 32 digits, 12 digits inside a barcode
		best = ['www.fedex.com'];
		newNo = no.substring(16,28);
	} else if (no.match(/^[0-9]{34}$/) && checkFedEx12(no.substring(22,34))) { // 34 digits, 12 digits at the end of a barcode
		best = ['www.fedex.com'];
		newNo = no.substring(22,34);
	} else if (no.match(/^[A-Z0-9][A-Z]{3}[0-9]{24}$/)) { // 28 digits, 16 digits inside a barcode
		best = ['em.canadapost.ca'];
		newNo = no.substring(7,23);
	} else if (no.match(/^WW[0-9]{9}[A-Z]{3}[0-9]{12}$/)) { // 26 digits, 9 digits inside a key
		best = ['www.tnt.com'];
		newNo = no.substring(2,11);
	} else if (no.match(/^(B2C|MPS)([0-9]{14})[0-9]{8}$/)) { // 25 digits, 15 digits inside a barcode
		best = ['www.dpd.com'];
		newNo = no.substring(3,17);
	}

	if (best) results.best = best;
	if (more) results.more = more;
	results.no = (newNo) ? newNo : no;
	return results;

}

function checkFedEx12 (no) {
	// Expects a string
	var checkDigit = ((no[0]*3)+(no[1]*1)+
		(no[2]*7)+(no[3]*3)+(no[4]*1)+
		(no[5]*7)+(no[6]*3)+(no[7]*1)+
		(no[8]*7)+(no[9]*3)+(no[10]*1)) % 11;
	if (checkDigit == 10) checkDigit = 0;
	return (checkDigit == no[11]) ? true : false;
}

function checkFedEx15 (no) {
	// Expects a string
	var checkDigit = 10 - ((((
		parseInt(no[13])+parseInt(no[11])+
		parseInt(no[9])+parseInt(no[7])+
		parseInt(no[5])+parseInt(no[3])+parseInt(no[1])) * 3)+
		(parseInt(no[12])+parseInt(no[10])+
		parseInt(no[8])+parseInt(no[6])+
		parseInt(no[4])+parseInt(no[2])+parseInt(no[0]))) % 10);
	if (checkDigit == 10) checkDigit = 0;
	return (checkDigit == no[14]) ? true : false;
}

function checkUSPS (no) {
	// Expects a string
	var checkDigit = 0;
	var digits = no.split('').reverse();
	for (var x = 1; x < digits.length; x+=2) checkDigit += parseInt(digits[x]);
	checkDigit = checkDigit * 3;
	for (var x = 2; x < digits.length; x+=2) checkDigit += parseInt(digits[x]);
	checkDigit = checkDigit % 10;
	if (checkDigit > 0) checkDigit = 10 - checkDigit;
	return (checkDigit == digits[0]) ? true : false;
}

function getUSPSSTC (no) {
	var matches = no.match(/^(420[0-9]{5,9})?92([0-9]{3})/);
	return (matches) ? parseInt(matches[2]) : 0;
}

function serviceFromString (string,substring) {
	var from = false;
	if (string) string = string.toLowerCase();
	if (substring) substring = substring.toLowerCase();
	switch (string) {
		case 'dhl paket':
			from = 'nolp.dhl.de';
			break;
		case 'fedex':
		case 'smartpost':
			from = 'www.fedex.com';
			break;
		case 'lasership':
			from = 'www.lasership.com';
			break;
		case 'ontrac':
			from = 'www.ontrac.com';
			break;
		case 'ups':
			from = 'www.ups.com';
			break;
		case 'usps':
			from = 'www.usps.com';
			if (substring == 'usps_sp_nat_std' || substring == 'usps_sp_std_pr') from = 'www.fedex.com';
			break;
		default:
			if (substring == 'usps_sp_nat_std' || substring == 'usps_sp_std_pr') from = 'www.fedex.com';
			break;
	}
	return from;
}

function getData (url) {
	var parts = url.substr(url.indexOf('?')+1);
	parts = parts.replace(/&amp;/g,'&');
	parts = parts.split('&');
	var data = new Array();
	for (x in parts) {
		part = parts[x].split('=');
		if (part[1]) data[fullUnescape(part[0])] = fullUnescape(part[1]);
	}
	return data;
}

function fullUnescape (string) {
	if (typeof string == 'string') {
		string = string.replace(/\+/g,' ');
		string = decodeURIComponent(string);
	}
	return string;
}