function setService (service,from,no) {

	if (!service) service = { from:from, no:no, extra:'' }
	service.fromClass = 'Other';
	service.domain = service.from;
	service.url = '';
	service.mobileURL = '';
	service.encoding = null;
	service.extraRequired = false;
	service.passRequired = false;
	service.cookiesRequired = false;
	service.noCache = false;
	service.postType = null;
	service.mobileUserAgent = false;
	service.desktopUserAgent = false;
	service.oneByOne = false;
	service.ignoreText = false;
	service.countToShip = false;
	service.neverUpdate = false;
	service.useItemCache = false;

	switch (service.from) {
		case 'www.adobe.com':
			service.fromClass = 'Adobe';
			service.url = 'https://accounts.adobe.com/orders';
			break;
		case 'www.amazon.com':
		case 'www.amazon.ca':
		case 'www.amazon.co.uk':
		case 'www.amazon.co.jp':
		case 'www.amazon.de':
		case 'www.amazon.at':
		case 'www.amazon.es':
		case 'www.amazon.fr':
		case 'www.amazon.in':
		case 'www.amazon.it':
			if (service.domain == 'www.amazon.at') service.domain = 'www.amazon.de';
			service.fromClass = 'Amazon';
			service.url = 'https://'+service.domain+'/';
			service.extraRequired = true;
			service.desktopUserAgent = true;
			service.cookiesRequired = true;
			service.countToShip = true;
			service.oneByOne = true;
			service.useItemCache = true;
			break;
		case 'www.amazon.com/url':
			var result = service.extra.match(/^https:\/\/((?:www|smile)\.amazon\.(?:co\.)?[a-z]+)\//i);
			service.domain = (result) ? result[1] : 'www.amazon.com';
			service.fromClass = 'Amazon';
			service.url = 'https://'+service.domain+'/';
			service.extraRequired = true;
			service.desktopUserAgent = true;
			service.cookiesRequired = true;
			service.countToShip = true;
			service.oneByOne = true;
			break;
		case 'www.apple.com':
		case 'www.apple.com/zip':
		case 'www.apple.com/japan':
		case 'www.apple.com/japanzip':
		case 'www.apple.com/hk':
			service.fromClass = 'Apple';
			switch (service.from) {
				case 'www.apple.com':
				case 'www.apple.com/zip':
					service.from = 'www.apple.com';
					service.url = 'https://store.apple.com/us/order/list';
					break;
				case 'www.apple.com/japan':
				case 'www.apple.com/japanzip':
					service.from = 'www.apple.com/japan';
					service.url = 'https://store.apple.com/jp/order/list';
					break;
				case 'www.apple.com/hk':
					service.url = 'https://store.apple.com/hk-zh/order/list';
					break;
			}
			service.desktopUserAgent = true;
			service.extraRequired = true;
			service.countToShip = true;
			break;
		case 'www.aramex.com':
			service.fromClass = 'Aramex';
			service.url = 'https://www.shopandship.com/';
			break;
		case 'auspost.com.au':
			service.fromClass = 'AusPost';
			service.url = 'https://auspost.com.au/parcels-mail/track.html';
			break;
		case 'em.canadapost.ca':
			service.fromClass = 'CanadaPost';
			service.url = 'https://www.canadapost.ca/trackweb/en#/home';
			break;
		case 'www.chronopost.fr':
			service.fromClass = 'Chronopost';
			service.url = 'https://www.chronopost.fr/';
			service.desktopUserAgent = true;
			break;
		case 'www.dhl.com':
		case 'track.dhl-usa.com':
		case 'www.dhl.co.uk':
			service.from = 'www.dhl.com';
			service.fromClass = 'DHL';
			service.url = 'http://www.dhl.com/en/express/tracking.html';
			break;
		case 'www.dhlglobalmail.com':
			service.fromClass = 'DHL';
			service.url = 'http://webtrack.dhlglobalmail.com/';
			break;
		case 'nolp.dhl.de':
		case 'nolp.dhl.de/int':
			service.from = 'nolp.dhl.de';
			service.fromClass = 'DHL';
			service.url = 'https://www.dhl.de/';
			service.desktopUserAgent = true;
			break;
		case 'www.dpd.com':
			service.fromClass = 'DPD';
			service.url = 'https://www.dpd.com/';
			break;
		case 'www.dpd.co.uk':
			service.fromClass = 'DPD';
			service.url = 'http://www.dpd.co.uk/content/how-can-we-help/index.jsp';
			service.neverUpdate = true;
			break;
		case 'www.fedex.com':
		case 'spportal.fedex.com':
			service.from = 'www.fedex.com';
			service.fromClass = 'FedEx';
			service.url = 'https://www.fedex.com/apps/fedextrack/?action=track';
			break;
		case 'www.gls-germany.com':
			service.fromClass = 'GLS';
			service.url = 'http://www.gls-group.eu/';
			break;
		case 'checkout.google.com':
			service.fromClass = 'Google';
			service.url = 'https://wallet.google.com/';
			service.countToShip = true;
			break;
		case 'www.hlg.de':
			service.fromClass = 'HLG';
			service.url = 'https://www.myhermes.de/empfangen/sendungsverfolgung/';
			break;
		case 'www.hongkongpost.com':
			service.fromClass = 'HongkongPost';
			service.url = 'http://www.hongkongpost.hk/en/mail_tracking/index.html';
			service.cookiesRequired = true;
			break;
		case 'www.japanpost.jp/ems':
		case 'www.japanpost.jp/m10':
		case 'www.japanpost.jp/reg':
		case 'www.japanpost.jp/yupack':
			service.fromClass = 'JapanPost';
			var lang = (translate('xx') == 'ja') ? 'jp' : 'en';
			service.url = 'https://trackings.post.japanpost.jp/services/srv/search/input?locale='+lang;
			break;
		case 'www.laposte.fr':
			service.fromClass = 'LaPoste';
			service.url = 'https://www.laposte.fr/particulier/outils/suivre-vos-envois';
			break;
		case 'www.lasership.com':
			service.fromClass = 'LaserShip';
			service.url = 'http://www.lasership.com/';
			break;
		case 'www.ontrac.com':
			service.fromClass = 'OnTrac';
			service.url = 'https://www.ontrac.com/tracking.asp';
			break;
		case 'www.parcelforce.com':
			service.fromClass = 'Parcelforce';
			service.url = 'https://www.parcelforce.com/track-trace';
			break;
		case 'www.post.at':
			service.fromClass = 'Post-at';
			service.url = (translate('xx') == 'de') ? 'http://www.post.at/sendungsverfolgung.php' : 
				'http://www.post.at/en/track_trace.php?language=en';
			break;
		case 'www.postdanmark.dk': // www.postnord.dk
			service.fromClass = 'PostNord';
			service.url = 'http://www.postnord.dk/';
			break;
		case 'www.poste.it':
		case 'www.poste.it/posta1':
		case 'www.poste.it/pacco1':
		case 'www.poste.it/maxi':
		case 'www.poste.it/pacco':
		case 'www.poste.it/ems':
			service.fromClass = 'Poste-it';
			service.url = 'https://www.poste.it/cerca/index.html';
			service.postType = 'application/json';
			service.oneByOne = true;
			break;
		case 'www.posten.no':
			service.fromClass = 'Posten-no';
			service.url = 'https://sporing.posten.no/';
			break;
		case 'www.posten.se': // www.postnord.se
			service.fromClass = 'PostNord';
			service.url = 'http://www.postnord.se/';
			break;
		case 'www.postnl.nl':
		case 'www.tntpost.nl':
			service.fromClass = 'PostNL';
			service.url = 'http://www.postnl.nl/';
			service.extraRequired = true;
			break;
		case 'www.purolator.com':
			service.fromClass = 'Purolator';
			service.url = 'https://www.purolator.com/en/ship-track/tracking-summary.page';
			break;
		case 'www.royalmail.com':
			service.fromClass = 'RoyalMail';
			service.url = 'https://www.royalmail.com/track-your-item';
			break;
		case 'www.sagawa-exp.co.jp':
			service.fromClass = 'Sagawa';
			service.url = 'https://k2k.sagawa-exp.co.jp/';
			break;
		case 'www.post.ch':
			service.fromClass = 'SwissPost';
			service.url = 'https://www.post.ch/';
			service.encoding = 'iso-8859-1';
			break;
		case 'www.tnt.com':
		case 'www.tnt.com/ref':
			service.fromClass = 'TNT';
			service.url = 'https://www.tnt.com/express/en_us/site/home/applications/tracking.html';
			break;
		case 'www.tntexpress.com.au':
			service.fromClass = 'TNT';
			service.url = 'http://www.tntexpress.com.au/interaction/asps/trackdtl_tntau.asp';
			break;
		case 'www.ups.com':
		case 'wwwapps.ups.com':
			service.from = 'www.ups.com';
			service.fromClass = 'UPS';
			service.url = 'https://wwwapps.ups.com/WebTracking/processInputRequest?loc=en_US';
			service.desktopUserAgent = true;
			break;
		case 'www.ups-mi.net':
			service.fromClass = 'UPS';
			service.url = 'https://www.ups-mi.net/packageID/';
			break;
		case 'www.usps.com':
			service.fromClass = 'USPS';
			service.url = 'https://tools.usps.com/go/TrackConfirmAction';
			service.oneByOne = true;
			break;
		case 'www.kuronekoyamato.co.jp':
			service.fromClass = 'Yamato';
			service.url = 'http://toi.kuronekoyamato.co.jp/cgi-bin/tneko';
			break;
		case 'www.yodel.co.uk':
			service.fromClass = 'Yodel';
			service.url = 'https://www.yodel.co.uk/';
			break;
		case 'other':
			service.url = service.extra;
			break;
		default:
			service = false;
			break;
	}
	return service;

}

function startUpdate (service,childNo) {
	switch(service.from) {

		case 'www.adobe.com':

			var no = service.no.replace(/[^A-Za-z0-9]/g,'');
			service.url = 'https://accounts.adobe.com/orders/'+no;	
			service.notSupported = notSupported;
			service.notSupported();
			this.updateItems();
			break;

		case 'www.amazon.com':
		case 'www.amazon.ca':
		case 'www.amazon.co.uk':
		case 'www.amazon.co.jp':
		case 'www.amazon.de':
		case 'www.amazon.at':
		case 'www.amazon.es':
		case 'www.amazon.fr':
		case 'www.amazon.in':
		case 'www.amazon.it':

			var no = service.no.replace(/[^0-9]/g,'').replace(/([0-9]{3})([0-9]{7})([0-9]{7})/,'$1-$2-$3');
			if (!no.match(/[0-9]{3}-[0-9]{7}-[0-9]{7}/)) {
				service.count = 0;
				service.notFound(translate('You must enter an Amazon order number, not a tracking number.'));
				this.updateItems();
			} else {
				service.url = 'https://'+service.domain+'/gp/css/order-details?orderId='+encodeURIComponent(no);
				if (this.itemCache && this.loadItemCache) {
					this.loadItemCache(getTracker,'AmazonTracker');
				} else if (manualSignIn[service.from+'-'+service.extra]) {
					logMessage(1,'startUpdate skipping '+service.from);
					service.count = 0;
					service.notFound();
					this.updateItems();
				} else {
					logMessage(1,'Item cache unavailable '+no);
					this.loadCookiePage(service.url,getAmazon,false,false);
				}
			}
			break;

		case 'www.amazon.com/url':

			var urlRegEx = /^https:\/\/((?:www|smile)\.amazon\.(?:co\.)?[a-z]+)\/(progress-tracker\/package|gp\/your-account\/ship-track|gp\/css\/shiptrack|gp\/your-account\/order-history\/item)[\/?%]/i;
			var url = false;
			if (service.extra) {
				if (service.extra.match(urlRegEx)) {
					url = service.extra;
				} else {
					var data = getData(service.extra);
					var dataURL = data['U'];
					if (dataURL && dataURL.match(urlRegEx)) url = dataURL;
				}
				if (url && !url.match(/[&?]packageIndex=/i)) {
					url += '&packageIndex=';
				}
			}
			if (url) {
				service.url = url;
				var args = setArgs('AmazonTracker',childNo);
				this.loadCookiePage(service.url,getTracker,false,args);
			} else {
				service.count = 0;
				service.notFound(translate('You must enter a link to a “Track Package” page on Amazon.'));
				this.updateItems();
			}
			break;

		case 'www.apple.com':
		case 'www.apple.com/zip':

			service.url = 'https://secure.store.apple.com/us/order/guest/'+encodeURIComponent(service.no)+'/'+doublePlusEncode(service.extra);
			this.loadPage(service.url,getAppleJSON,false,false);
			break;

		case 'www.apple.com/japan':
		case 'www.apple.com/japanzip':

			service.url = 'https://secure.store.apple.com/jp/order/guest/'+encodeURIComponent(service.no)+'/'+doublePlusEncode(service.extra);
			this.loadPage(service.url,getApple,false,false);
			break;

		case 'www.apple.com/hk':

			service.url = 'https://secure.store.apple.com/hk-zh/order/guest/'+encodeURIComponent(service.no)+'/'+doublePlusEncode(service.extra);
			this.loadPage(service.url,getApple,false,false);
			break;

		case 'www.aramex.com':

			service.url = 'https://www.shopandship.com/en/tracking/'+encodeURIComponent(service.no);
			var url = 'https://www.shopandship.com/api/packages/'+encodeURIComponent(service.no)+'/singlePackage?awbs='+encodeURIComponent(service.no)+'&count=1&language=en&useXMLTracking=false';
			var args = setArgs('Aramex',childNo);

			if (hasOSFeatureLevel(5)) {
				this.loadPage(url,getJSON,false,args);
			} else {
				service.notSupported = notSupported;
				service.notSupported();
				this.updateItems();
			}
			break;

		case 'auspost.com.au':

			service.url = 'https://auspost.com.au/mypost/track/#/details/'+encodeURIComponent(service.no);
			service.notSupported = notSupported;
			service.notSupported();
			this.updateItems();
			break;
	
		case 'em.canadapost.ca':
	
			var args = setArgs('CanadaPost',childNo);
			args.lang = (translate('xx') == 'fr') ? 'fr' : 'en';
			service.url = 'https://www.canadapost.ca/trackweb/'+args.lang+'#/search?searchFor='+
				encodeURIComponent(service.no);
			var url = 'https://www.canadapost.ca/trackweb/rs/track/json/package/'+
				encodeURIComponent(service.no)+'/detail';

			if (hasOSFeatureLevel(5)) {
				this.loadPage(url,getJSON,false,args);
			} else {
				service.notSupported = notSupported;
				service.notSupported();
				this.updateItems();
			}			
			break;

		case 'www.chronopost.fr':
	
			var args = setArgs('Chronopost',childNo);
			var no = service.no.replace(/ /g,'');
			args.lang = (translate('xx') == 'fr') ? 'fr' : 'gb';
			service.url = 'https://www.chronopost.fr/fr/chrono_suivi_search?listeNumerosLT='+encodeURIComponent(no)+'&lang='+args.lang;
			args.referer = service.url;
			var url = 'https://www.chronopost.fr/tracking-no-drupal/suivi-colis?listeNumerosLT='+encodeURIComponent(no)+'&langue='+args.lang;
			this.loadPage(url,getHTMLJSON,false,args);
			break;
	
		case 'www.dhl.com':
		case 'track.dhl-usa.com':
		case 'www.dhl.co.uk':
	
			var args = setArgs('DHL',childNo);
			var no = encodeURIComponent(service.no.replace(/ /g,''));
			service.url = 'http://www.dhl.com/content/g0/en/express/tracking.shtml?brand=DHL&AWB='+no;
			var url = 'http://www.dhl.com/shipmentTracking?AWB='+no+'&countryCode=g0&languageCode=en';
			if (childNo !== false) {
				args.service = service;
				this.loadPage(url,getJSON,false,args);
			} else this.loadPage(url,getJSON,false,args);
			break;
	
		case 'www.dhlglobalmail.com':

			service.url = 'http://webtrack.dhlglobalmail.com/?mobile=false&trackingnumber='+
				encodeURIComponent(service.no)+'&locale='+translate('xx');
			service.notSupported = notSupported;
			service.notSupported();
			this.updateItems();
			break;
	
		case 'nolp.dhl.de':
		case 'nolp.dhl.de/int':

			var args = setArgs('DHL (Germany)',childNo);
			var no = encodeURIComponent(service.no.replace(/[ .]*/g,''));
			args.lang = (translate('xx') == 'de') ? 'de' : 'en';
			args.extra = (service.skipExtra) ? '' : service.extra;
			service.url = 'https://nolp.dhl.de/nextt-online-public/'+args.lang+'/search?piececode='+encodeURIComponent(no);
			if (args.extra) service.url += '&zip='+encodeURIComponent(args.extra);
			if (childNo !== false) {
				args.service = service;
				this.loadPage(service.url,getTracker,false,args);
			} else this.loadPage(service.url,getTracker,false,args);
			break;

		case 'www.dpd.com':
	
			var args = setArgs('DPD',childNo);
			var no = encodeURIComponent(service.no.replace(/ /g,''));
			var locale = preferredLocale('en','de');
			service.url = 'https://tracking.dpd.de/status/'+locale+'/parcel/'+encodeURIComponent(no);
			var url = 'https://tracking.dpd.de/rest/plc/'+locale+'/'+encodeURIComponent(no);
			this.loadPage(url,getJSON,false,args);
			break;

		case 'www.dpd.co.uk':
	
			service.url = 'http://www.dpd.co.uk/service/tracking?parcel='+encodeURIComponent(service.no);
			service.notSupported = notSupported;
			service.notSupported();
			this.updateItems();
			break;

		case 'www.fedex.com':

			var no = service.no.replace(/^([0-9]+)E$/,'$1');
			var locale = preferredLocale('en','de','fr','ja','it','sv','da','nl','no','nb');
			var args = setArgs('FedEx',childNo);
			args.service = service;
			args.locale = locale;
			service.url = 'https://www.fedex.com/apps/fedextrack/?action=track&trackingnumber='+encodeURIComponent(no)+'&locale='+locale;

			if (hasOSFeatureLevel(5)) {
				this.loadPage(service.url,getFedEx,false,args);
			} else {
				service.notSupported = notSupported;
				service.notSupported();
				this.updateItems();
			}
			break;

		case 'www.gls-germany.com':

			var args = setArgs('GLS',childNo);
			var no = service.no.replace(/ /g,'');
			switch(translate('xx')) {
				case 'de':
					args.lang = 'de';
					service.url = 'https://gls-group.eu/DE/de/paketverfolgung?txtAction=71000&match='+encodeURIComponent(no);
					break;
				case 'da':
					args.lang = 'da';
					service.url = 'http://www.gls-group.eu/276-I-PORTAL-WEB/content/GLS/DK01/DA/5004.htm?txtAction=71000&txtRefNo='+encodeURIComponent(no);
					break;
				case 'fr':
					args.lang = 'fr';
					service.url = 'http://www.gls-group.eu/276-I-PORTAL-WEB/content/GLS/FR01/FR/5004.htm?txtAction=71000&txtRefNo='+encodeURIComponent(no);
					break;
				default:
					args.lang = 'en';
					service.url = 'https://gls-group.eu/EU/en/parcel-tracking?txtAction=71000&match='+encodeURIComponent(no);
					break;
			}
			var url = 'https://gls-group.eu/app/service/open/rest/EU/'+args.lang+'/rstt001?match='+encodeURIComponent(no);
			this.loadPage(url,getJSON,false,args);
			break;

		case 'www.hlg.de':

			var args = setArgs('HLG',childNo);
			var no = encodeURIComponent(service.no.replace(/[^0-9]/g,''));
			service.url = 'https://www.myhermes.de/empfangen/sendungsverfolgung/sendungsinformation/#'+no;
			var url = 'https://www.myhermes.de/services/tracking/shipments?search='+no;
			// &plz=00000
			this.loadPage(url,getJSON,false,args);
			break;
	
		case 'www.japanpost.jp/ems':
		case 'www.japanpost.jp/m10':
		case 'www.japanpost.jp/reg':
		case 'www.japanpost.jp/yupack':
	
			var args = setArgs('JapanPost',childNo);
			args.lang = (translate('xx') == 'ja') ? 'jp' : 'en';
			service.url = 'https://trackings.post.japanpost.jp/services/srv/search/direct?locale='+args.lang+
				'&reqCodeNo1='+encodeURIComponent(service.no);
			this.loadPage(service.url,getTracker,false,args);
			break;
	
		case 'www.laposte.fr':

			var args = setArgs('LaPoste',childNo);
			var no = service.no.replace(/ /g,'');
			service.url = (translate('xx') == 'fr') ? 'https://www.laposte.fr/particulier/outils/suivre-vos-envois?code='+encodeURIComponent(no) : 'https://www.laposte.fr/particulier/outils/en/track-a-parcel?code='+encodeURIComponent(no);
			this.loadPage(service.url,getDetailPage,false,args);
			break;

		case 'www.lasership.com':
	
			var args = setArgs('LaserShip',childNo);
			service.url = 'http://www.lasership.com/track/'+encodeURIComponent(service.no);
			var url = 'http://www.lasership.com/track/'+encodeURIComponent(service.no)+'/json';
			this.loadPage(url,getJSON,false,args);
			break;
	
		case 'www.ontrac.com':
	
			var args = setArgs('OnTrac',childNo);
			service.url = 'https://www.ontrac.com/trackingres.asp?tracking_number='+encodeURIComponent(service.no);
			this.loadPage(service.url,getDetailPage,false,args);
			break;
	
		case 'www.parcelforce.com':

			var args = setArgs('Parcelforce',childNo);
			service.url = 'https://www.parcelforce.com/track-trace?trackNumber='+encodeURIComponent(service.no);
			var url = service.url+'&stop_mobi=yes';

			if (hasOSFeatureLevel(5)) {
				this.loadPage(url,getMultiple,false,args);
			} else {
				service.notSupported = notSupported;
				service.notSupported();
				this.updateItems();
			}
			break;
	
		case 'www.post.at':
	
			var args = setArgs('Post.at',childNo);
			service.url = (translate('xx') == 'de') ? 'https://www.post.at/sendungsverfolgung.php/details?pnum1='+encodeURIComponent(service.no) : 
				'https://www.post.at/en/track_trace.php/details?pnum1='+encodeURIComponent(service.no);
			this.loadPage(service.url,getMultiple,false,args);
			break;

		case 'www.postdanmark.dk': // www.postnord.dk
		case 'www.posten.se': // www.postnord.se

			var args = setArgs('PostNord',childNo);
			args.lang = preferredLanguage('en','sv','no','da','fi');
			if (service.from == 'www.posten.se') {
				var path = (args.lang == 'sv') ? 'online-verktyg/verktyg/sok/spara-brev-paket-och-pall' : 'en/online-tools/tools/track/track-and-trace';
				service.url = 'http://www.postnord.se/'+path+'#dynamicloading=true&shipmentid='+encodeURIComponent(service.no);
			} else {
				var path = (args.lang == 'da') ? 'track-trace' : 'en/track-and-trace';
				service.url = 'http://www.postnord.dk/'+path+'#dynamicloading=true&shipmentid='+encodeURIComponent(service.no);
			}
			var apiKey = extraInformationForService(service,'apiKey');
			if (apiKey) {
				var url = 'https://api2.postnord.com/rest/shipment/v1/trackandtrace/findByIdentifier.json?id='+encodeURIComponent(service.no)+'&locale='+args.lang+'&apikey='+encodeURIComponent(apiKey);
				this.loadCookiePage(url,getJSON,false,args);
			} else {
				service.notSupported = notSupported;
				service.notSupported();
				this.updateItems();
			}
			break;

		case 'www.poste.it':
		case 'www.poste.it/posta1':
		case 'www.poste.it/pacco1':
		case 'www.poste.it/maxi':
		case 'www.poste.it/pacco':
		case 'www.poste.it/ems':

			var no = service.no.replace(/[^A-Za-z0-9]/g,'');
			service.url = 'https://www.poste.it/cerca/index.html#/risultati-spedizioni/'+no;
			if (getCommonFilesVersion() >= 185 && hasOSFeatureLevel(5)) {
				var args = setArgs('Poste-it',childNo);
				var url = 'https://www.poste.it/online/dovequando/DQ-REST/ricercasemplice';
				var postData = JSON.stringify({"tipoRichiedente":"WEB","codiceSpedizione":no,"periodoRicerca":1});
				args.service = service;
				this.loadPage(url,getJSON,postData,args);
			} else {
				service.notSupported = notSupported;
				service.notSupported();
				this.updateItems();
			}
			break;

		case 'www.posten.no':
	
			var args = setArgs('Posten-no',childNo);
			args.lang =  (translate('xx') == 'no') ? 'no' : 'en';
			service.url = 'https://sporing.posten.no/sporing.html?layout=normal&lang='+args.lang+'&q='+encodeURIComponent(service.no);
			this.loadPage(service.url,getTracker,false,args);
			break;

		case 'www.postnl.nl':
		case 'www.tntpost.nl':

			var args = setArgs('PostNL',childNo);
			args.lang = 'nl'; // preferredLanguage('nl','en','de','es','fr','it','zh');
			var postalCode = service.extra;
			var countryCode = 'NL';
			if (postalCode) {
				var matches = null;
				if (matches = postalCode.match(/(.*)[\/, -]+([A-Z]{2})$/i)) {
					postalCode = trim(matches[1]);
					countryCode = trim(matches[2]);
				} else if (matches = postalCode.match(/^([A-Z]{2})[\/, -]+(.*)/i)) {
					postalCode = trim(matches[2]);
					countryCode = trim(matches[1]);
				} else if (matches = postalCode.match(/^([A-Z]{2})$/i)) {
					postalCode = '';
					countryCode = trim(matches[1]);
				}
			}
			service.url = 'https://www.internationalparceltracking.com/Main.aspx#/track/'+encodeURIComponent(service.no)+'/'+countryCode;
			var url = 'https://www.internationalparceltracking.com/api/shipment?language='+args.lang+'&barcode='+encodeURIComponent(service.no)+'&country='+countryCode;
			if (postalCode) {
				service.url += '/'+encodeURIComponent(postalCode);
				url += '&postalCode='+encodeURIComponent(postalCode);
			}
			this.loadCookiePage(url,getJSON,false,args);
			break;
	
		case 'www.purolator.com':
	
			var args = setArgs('Purolator',childNo);
			args.lang = (translate('xx') == 'fr') ? 'fr' : 'en';
			service.url = 'https://www.purolator.com/'+args.lang+'/ship-track/tracking-summary.page?pin='+encodeURIComponent(service.no);
			this.loadPage(service.url,getMultiple,false,args);
			break;

		case 'www.royalmail.com':

			service.url = 'https://www.royalmail.com/track-your-item#/tracking-results/'+encodeURIComponent(service.no);
			service.notSupported = notSupported;
			service.notSupported();
			this.updateItems();
			break;

		case 'www.sagawa-exp.co.jp':
	
			service.url = 'https://k2k.sagawa-exp.co.jp/p/web/okurijosearch.do?okurijoNo='+encodeURIComponent(service.no);
			var args = setArgs('Sagawa',childNo);
			this.loadPage(service.url,getTracker,false,args);
			break;
	
		case 'www.post.ch':

			service.url = 'https://service.post.ch/EasyTrack/submitParcelData.do?lang='+translate('xx')+'&formattedParcelCodes='+encodeURIComponent(service.no);
			var args = setArgs('SwissPost',childNo);
			this.loadPage(service.url,getTracker,false,args);
			break;
	
		case 'www.tnt.com':
		case 'www.tnt.com/ref':
	
			var mode = 'CON';
			var key = 'con';
			if (service.from == 'www.tnt.com/ref') {
				mode = 'REF';
				key = 'ref';
			}
			service.url = 'https://www.tnt.com/express/en_us/site/home/applications/tracking.html?searchType='+
				mode+'&cons='+encodeURIComponent(service.no);
			var url = 'https://www.tnt.com/api/v1/shipment?locale=en_US&searchType='+
				mode+'&'+key+'='+encodeURIComponent(service.no);
			var args = setArgs('TNT',childNo);
			this.loadPage(url,getJSON,false,args);
			break;

		case 'www.tntexpress.com.au':

			service.url = 'http://www.tntexpress.com.au/interaction/track.aspx?con='+encodeURIComponent(service.no);
			var url = 'http://www.tntexpress.com.au/interaction/ASPs/Trackcon_tntau.asp?id=TRACKCON.ASP&con='+encodeURIComponent(service.no);
			var args = setArgs('TNT Express',childNo);
			if (childNo !== false) args.service = service;
			this.loadPage(url,getTracker,postData,args);
			break;

		case 'www.ups.com':

			var no = encodeURIComponent(service.no.replace(/ /g,''));
			var url = (no.match(/^1Z/i)) ? 'https://wwwapps.ups.com/WebTracking/detail?loc=en_US&InquiryNumber1='+no : 'https://www.ups.com/WebTracking/processInputRequest?loc=en_US&tracknum='+no;
			service.url = 'https://www.ups.com/WebTracking/processInputRequest?loc=en_US&tracknum='+no;
			service.mobileURL = 'https://m.ups.com/mobile/track?t=t&loc=en_US&trackingNumber='+no;
			var args = setArgs('UPS',childNo);

			if (hasOSFeatureLevel(5)) {
				if (childNo !== false) {
					args.service = service;
					this.loadPage(url,getTracker,false,args);
				} else this.loadPage(url,getMultiple,false,args);
			} else {
				service.notSupported = notSupported;
				service.notSupported();
				this.updateItems();
			}
			break;

		case 'www.ups-mi.net':
	
			service.url = 'https://www.ups-mi.net/packageID/packageid.aspx?pid='+encodeURIComponent(service.no);
			var args = setArgs('UPS-MI',childNo);
			this.loadPage(service.url,getTracker,false,args);
			break;

		case 'www.usps.com':

			var no = (service.no.toUpperCase()).replace(/ /g,'');
			service.url = 'https://tools.usps.com/go/TrackConfirmAction.action?tLabels='+encodeURIComponent(no);
			var args = setArgs('USPS',childNo);
			var apiUserID = extraInformationForService(service,'apiUserID');

			if (apiUserID) {
				var clientIPAddress = extraInformationForService(service,'clientIPAddress');
				var extraData = (apiUserID && clientIPAddress) ? '<Revision>1</Revision><ClientIp>'+clientIPAddress+'</ClientIp><SourceId>Junecloud</SourceId>' : '';
				var url = 'https://secure.shippingapis.com/ShippingAPI.dll';
				var postData = 'API=TrackV2&XML='+encodeURIComponent('<TrackFieldRequest USERID="'+apiUserID+'">'+extraData+'<TrackID ID="'+no+'"></TrackID></TrackFieldRequest>');
				this.loadPage(url,getTracker,postData,args);
			} else if (hasOSFeatureLevel(5)) {
				this.loadPage(service.url,getTracker,false,args);
			} else {
				service.notSupported = notSupported;
				service.notSupported();
				this.updateItems();
			}
			break;
	
		case 'www.kuronekoyamato.co.jp':
	
			var url = 'http://toi.kuronekoyamato.co.jp/cgi-bin/tneko';
			postData = 'number00=1&number01='+encodeURIComponent(service.no);
			service.url = 'http://jizen.kuronekoyamato.co.jp/jizen/servlet/crjz.b.NQ0010?id='+encodeURIComponent(service.no.replace(/-/g,''));
			var args = setArgs('Yamato',childNo);
			this.loadPage(url,getMultiple,postData,args);
			break;

		case 'www.yodel.co.uk':

			var args = setArgs('Yodel',childNo);
			args.extra = (service.skipExtra) ? '' : service.extra;
			var no = (service.no.toUpperCase()).replace(/ /g,'');
			var extra = (args.extra.toUpperCase()).replace(/ /g,'');
			service.url = 'https://www.myyodel.co.uk/tracking/'+encodeURIComponent(no)+'/'+encodeURIComponent(extra);
			var url = service.url+'?desktop=true';
			this.loadPage(url,getTracker,false,args);
			break;

		case 'other':

			service.url = service.extra;
			if (typeof updateOther === 'function') updateOther(this);
			this.updateItems();
			break;

		case 'checkout.google.com':
		case 'www.hongkongpost.com':
		default:
	
			if (this == service) {
				service.notSupported = notSupported;
				service.notSupported();
				this.updateItems();
			}
			break;
	
	}
}

function getAmazon () {

	var delivery = this.delivery;
	var sections = false;
	var items = false;
	var result = false;
	var thisResult = false;
	var trackingUrl = false;
	var x = 0;
	var y = 0;
	var yy = 0;
	var z = 0;

	var output = '';
	if (this.responseText) output = stripWhiteSpace(this.responseText);
	var errorResult = output.match(/<div class="a-box a-alert a-alert-(?:info|warning)[^"]*"> ?<div class="a-box-inner a-alert-container"> ?<h4 class="a-alert-heading">([^<]*)/i);

	if (output && !errorResult) {

		var languageFrom = getLanguageFrom(delivery.from,output);

		// If it's the page showing all orders, try to find the order there
		if (output.match(/<div id="yourOrders"> ?<div id="yourOrdersContent">/i)) {
			var orders = overlappingMatches(output,'(?:<div class="a-box-group a-spacing-base order">)(.*?)(?:<div class="a-box-group a-spacing-base order">|<div class="a-row"> <div class="a-text-center pagination-full">|$)');
			for (var orderx in orders) {
				var order = orders[orderx];
				var orderno = order.match(/<span class="a-color-secondary value"> ?([0-9]{3}-[0-9]{7}-[0-9]{7}) ?<\/span>/i);
				if (orderno && orderno[1]) {
					if (orderno[1] == delivery.no) {
						output = order; 
						break;
					}
				}
			}
		}

		sections = overlappingMatches(output,'<div[^>]*class="a-box [^"]*shipment[^"]*">.*?(<div[^>]*class="a-box [^"]*shipment[^"]*">|<script type="text\/javascript">|$)');

		for (y in sections) {

			var sectionDeliverDate = false;
			var status = '';

			// Sometimes there are subsections with different dates, especially for pre-orders
			var subSections = overlappingMatches(sections[y],'<div class="a-row"> ?(?:<div class="a-section a-spacing-base [^"]*"> ?(<div>)? ?<span[^>]*>|<div> ?<span[^>]*>)(.*?)(<div class="a-row"> ?(?:<div class="a-section a-spacing-base [^"]*"> ?(<div>)? ?<span[^>]*>|<div> ?<span[^>]*>)|$)');
			if (!subSections) subSections = [sections[y]];
			for (yy in subSections) {

				// Get the status of this section, which may include the delivery date
				// Should this be subSections[yy]?
				result = sections[y].match(/(?:<span class="a-size-medium[^"]*">|<h3>)([^<]*)/i); // h3 not used anymore?
				if (result) {
					status = stripHTML(result[1]);
					if (result = status.match(/^ ?(?:Delivered|Zugestellt|Entregado|Livré|Consegnato|配達を完了しました|配達完了):?(.*?)$/i)) {
						status = 'Complete';
						if (result[1]) {
							sectionDeliverDate = cleanUpDate(result[1],false,languageFrom,false,false);
						}
					} else {
						result = status.match(/^ ?(?:Arriving|Expected(?: by)?|Now expected|Zustellung|Erwartete Zustellung|Voraussichtlich|Voraussichtliche Lieferung|Entrega(?: el)?|Entrega prevista|Livraison(?: prévue| estimée)?(?: le)?|Arriverà|In consegna):? (.*)/i);
						if (!result) result = status.match(/^ ?([0-9]+月[0-9]+日|[0-9\/]*)[^<]*に到着予定/i);
						if (result && result[1]) {
							var arrivingDate = endDateInRange(result[1]);
							sectionDeliverDate = cleanUpDate(arrivingDate,false,languageFrom,false,true);
							status = '';
						}
					}
				}

				var item = resetItem();
				item.status = status;

				if (sectionDeliverDate) item.deliverSaved = sectionDeliverDate;

				// Get the estimated delivery date: "Voraussichtlicher Liefertermin:", "Delivery estimate:", "Guaranteed delivery by:"
				// Or the actual delivered date: "Delivered on:"
				// And/or the ship date: "Shipping estimate:"
				result = subSections[yy].match(/<div[^>]*> ?<span class="a-color-success">([^<]*)(?:<span class="a-text-bold">([^<]*))?(?: ?<\/span>| ?<\/div>)*(?:<div[^>]*> ?<span class="a-color-success">([^<]*)(?:<span class="a-text-bold">([^<]*))?(?: ?<\/span>| ?<\/div>)*)?/i);
				if (!result) result = subSections[yy].match(/<div class="a-row"> ?(?:<div class="a-section a-spacing-base a-spacing-top-large"> ?)?<div> ?<span> ?([a-z\s]*): ?([^<]*)/i);
				if (result) {
					var thisLabel = trim(result[1]);
					var thisDate = false;
					if (result[2]) thisDate = cleanUpDate(endDateInRange(result[2]),false,languageFrom,false,true);
					if (thisDate) {
						if (result[4]) {
							item.shipSaved = thisDate;
							item.deliverSaved = cleanUpDate(endDateInRange(result[4]),false,languageFrom,false,true);
						} else {
							if (thisLabel && thisLabel.match(/^(Shipping estimate)/i)) {
								item.shipSaved = thisDate;
							} else item.deliverSaved = thisDate;
						}
					}
				}

				// Get the tracking link for shipped items
				if (result = subSections[yy].match(/<a href="(https:\/\/[^\/]*)?(\/(?:progress-tracker\/package|gp\/your-account\/ship-track|gp\/css\/shiptrack)[\/?%][^"]+)"[^>]*>/i)) {
					trackingUrl = result[2].replace(/&amp;/g,'&');
					if (result[1]) {
						trackingUrl = result[1]+trackingUrl;
					} else {
						trackingUrl = 'https://'+delivery.domain+trackingUrl;
					}
					var args = setArgs('AmazonTracker',x);
					delivery.loadCookiePage(trackingUrl,getTracker,false,args);
				}

				// Get the name of each item
				var items = subSections[yy].match(/(<div class="a-row">|<span class="a-size-base">) ?<a class="a-link-normal"[^>]*>([^<]*)/gi);
				var firstItem = true;
				for (z in items) {
					// Get the name of the item
					result = items[z].match(/<a class="a-link-normal"[^>]*>([^<]*)/i);
					if (result && result[1]) {
						result = trim(result[1]);
						result = result.replace('&iuml;&iquest;&frac12;',' '); // Clean up encoding errors
						if (firstItem) {
							firstItem = false;
							item.title = (delivery.name) ? htmlEntities(delivery.name)+": "+result : result;
						} else {
							item.title += ", "+result;
						}
					}
				}

				if (x == 0) delivery.items = [];
				delivery.items[x] = item;
				x++;

			}

		}

		delivery.count = x;
		if (!delivery.items[0] || delivery.items[0].title == translate('Unknown Item')) delivery.count = 0;

	}

	if (delivery.count == 0) {

		// Check for an error message or sign in form
		var showImmediately = false;
		var needsSignIn = output.match(/(?:<form[^>]*name=.?signIn.?[^>]*action="(?:[^"]*)[^>]*>|<div class="[^"]* auth-workflow">|<form id="auth-mfa-form"[^>]*>)/i);

		result = output.match(/<div class="a-alert-content"> ?<ul[^>]*> ?<li> ?<span[^>]*>([^<]*)<\/span> ?<\/li> ?<\/ul> ?<\/div>/i);
		if (!result) {
			// Cancelled, not found, problem loading
			result = errorResult;
			showImmediately = true;
		}
		if (!result) result = output.match(/(?:<div id="message_error"[^>]*>|<div class="message error"[^>]*>).*?(?:<h6>[^<]*<\/h6>)? ?<p>(.*?)<\/p> ?<\/div>/i);
		if (result) result = result[1];

		if (result || needsSignIn) {
			if (needsSignIn) {
				var message = 'getAmazon requires sign in: '+needsSignIn[0];
				if (result) message += ' error: '+result;
				logMessage(1,message);
				manualSignIn[delivery.from+'-'+delivery.extra] = true;
			}
			delivery.notFound(result,false,showImmediately);
		} else {
			// Note that this can happen for both server errors and incorrect order numbers:
			// <h4 class="a-alert-heading">There's a problem loading this order
			var serverError = (this.status == 500 || output.match(/(<title>500 Service Unavailable Error|<body> ?<div style="width: 90%; margin-left: 5%"> ?<h2>)/));
			if (serverError) delivery.count = -1;
			logRequest(1,this);
			delivery.notFound();
		}

	}

	delivery.reqComplete++;
	delivery.updateItems();

	return;

}

function getAppleJSON () {

	var delivery = this.delivery;
	var pattern = /<script id="init_data" type="application\/json">(.*?)<\/script>/;
	var object = parseJSONInHTML(this.responseText, pattern);
	var orderItems = (object && object.orderDetail) ? object.orderDetail.orderItems : null;
	var orderItemList = (orderItems) ? orderItems.c : null;

	if (orderItems && orderItemList) {

		for (var x in orderItemList) {

			var itemID = orderItemList[x];
			var orderItem = orderItems[itemID];
			var detailItem = orderItem.orderItemDetails.d;
			var statusItem = orderItem.orderItemStatusTracker.d;

			var name = detailItem.productName;
			var detailStatus = detailItem.deliveryDate;
			var status = getAppleState(statusItem.currentStatus, this.responseText) || detailStatus;
			var dates = getAppleDates(detailStatus);
			var futureDate = (status == 'Complete');
			var ship = cleanUpDate(dates[0], false, delivery.from, false, futureDate);
			var deliver = cleanUpDate(dates[1], false, delivery.from, false, futureDate);
			var urls = detailItem.trackingURLMap || [null];

			for (var i in urls) {

				var url = urls[i];
				var item = resetItem();
				item.title = name;
				item.status = status;
				item.detailsurl = url;
				item.shipSaved = ship;
				item.deliverSaved = deliver;
				delivery.items[x] = item;

				if (url) {
					var service = serviceFromURL(url);
					if (service) delivery.startUpdate(service, x);
				}

				x++;

			}
			delivery.count = delivery.items.length;

		}

	} else {
		logRequest(1,this);
		if (object) {
			delivery.count = 0;
		} else if (!delivery.count) {
			delivery.count = -1;
		}
		delivery.notFound();
	}

	delivery.reqComplete++;
	delivery.updateItems();
	return;

}

function getAppleState (status, html) {
	switch (status) {
		case 'DELIVERED': return 'Complete';
		case 'DELIVERED_TO_YOU': return 'Complete';
		default:
			var result = html.match(/"stateText": ({[\s\S]*?})/);
			if (!result) return null;
			try {
				object = JSON.parse(result[1]);
			} catch (err) {
				return null;
			}
			return object[status];
	}
	return null;
}

function getApple () {

	var delivery = this.delivery;
	var result = false;
	var thisResult = false;
	var checkForError = false;
	var dateHints = [];
	var x = 0;

	if (this.status == 200 && this.responseText) {

		var output = stripWhiteSpace(this.responseText);
		output = output.replace(/[\u2028]+/g,' '); // For iOS 4, in 4.7 and earlier

		// Get each section of the order
		var sections = output.match(/<div class="shaded-box">(.*?)<\/div> ?<\/li> ?(?:<!-- \/\.product -->)? ?<\/ul> ?<\/div> ?<\/div> ?<\/div>/gi);

		for (var y in sections) {

			var sectionOutput = sections[y];
			sectionOutput = sectionOutput.replace(/(\s*<!--(.*?)-->\s*)+/g,' ');

			var sectionInfo = false;
			var sectionShipped = false;
			var sectionDelivered = false;
			var status = false;
			var trackingUrl = false;
			var item = resetItem();

			// Get the status of this section, otherwise assume it's the same as the previous item
			result = sectionOutput.match(/<h3[^>]*> ?<span[^>]*>([^<]*)/i);
			if (result && result[1]) status = trim(result[1]);
			if (status) {

				// Ignore dates for cancelled or picked up items
				// Not sure if "Replaced with New Product" is still used
				if (!status.match(/^(Cancelled|Canceled|Replaced with New Product|Returns Completed|Picked Up)$/i)) {

					result = sectionOutput.match(/<span class="h5">( ?on )?([^<|]*)((<\/span> ?)?<span[^>]*>( ?<br\/> ?)?([^<]*))?/i);
					if (result && result[2]) {
						result = getAppleDates(result[0]);
						if (result[0]) sectionShipped = result[0];
						sectionDelivered = result[1];
						dateHints = dateHints.concat(result[2]);
					}

					// Get the tracking number
					result = sectionOutput.match(/<(?:a|button)[^>]*id="[A-Z0-9]+(-tik)?-track-delivery-link-([A-Z0-9]+)"[^>]*>/i);
					if (result && result[2]) {
						var groupId = (result[1]) ? 'tikgroup' : 'group';
						var regex = new RegExp('"'+groupId+'":{"'+result[2]+'":{"[^"]+":{"TRACK_SHIPMENT":{"url":"([^"]+)"}}}');
						result = output.match(regex);
						if (result && result[1]) trackingUrl = result[1];
					}

					if (status.match(/^Delivered$/)) {
						status = 'Complete';
					}

				}

				// Get each item in the section
				var items = sectionOutput.match(/(<li class="delivery-group">|<div class="media-block product-item">)(.*?)<\/div> ?<\/li>/gi);
				for (var z in items) {

					item.status = status;
					item.details = {};
					// item.details.status = status;
					item.detailsurl = trackingUrl;

					// Get the name of the item
					result = items[z].match(/<h4 class="strong name">([^<]*)/i);
					if (result && result[1]) item.title = trim(result[1]);

					var dateInfo = cleanSpaces(items[z]);
					result = getAppleDates(dateInfo);
					item.shipSaved = (result[0]) ? result[0] : sectionShipped;
					item.deliverSaved = (result[1]) ? result[1] : sectionDelivered;
					dateHints = dateHints.concat(result[2]);

					if (z == 0) {
						if (!sectionShipped) sectionShipped = item.shipSaved;
						if (!sectionDelivered) sectionDelivered = item.deliverSaved;
					}

					var lastItem = false;
					if (x == 0) {
						delivery.items = [];
					} else lastItem = delivery.items[x-1];

					if (lastItem && 
						item.status == lastItem.status && 
						item.shipSaved == lastItem.shipSaved && 
						item.deliverSaved == lastItem.deliverSaved &&
						item.detailsurl == lastItem.detailsurl) {

						lastItem.title += ', '+item.title;

					} else {

						delivery.items[x] = item;	
						if (trackingUrl) {
							var args = setArgs(false,x);
							var result = trackingUrl.match(/https?:\/\/([^\/]*)/i);
							if (result && result[1]) {
								if (result[1].match(/^(secure[0-9]*\.)?store\.apple\.com$/)) {
									delivery.loadPage(trackingUrl,getAppleDetails,false,args);
								} else {
									logMessage(1,'Unsupported Apple details URL: '+trackingUrl);
								}
							}
						}
	
						item = resetItem();
						x++;

					}

				}

			}

		}

		delivery.count = delivery.items.length;
		if (!delivery.items[0] || delivery.items[0].title == translate('Unknown Item')) delivery.count = 0;
		if (delivery.name) {
			var entityName = htmlEntities(delivery.name);
			for (var y in delivery.items) delivery.items[y].title = 
				entityName+": "+delivery.items[y].title;
		}

		if (delivery.items.length > 0) {
			var reverseDates = false;
			var probablyInternational = false;
			var result;
			for (var y in dateHints) {
				var dateHint = dateHints[y];
				result = isInternationalDate(dateHint);
				if (result == -1) {
					reverseDates = false;
					probablyInternational = false;
					break;
				} else if (result == 1) {
					reverseDates = true;
					break;
				} else {
					// Zero padded numbers are probably international
					if (probablyInternational == false &&
						dateHint.match(/(0[0-9]\/[0-9]{2}|[0-9]{2}\/0[0-9])\/[0-9]{4}/)) {
						probablyInternational = true;
					}
				}
			}
			if (probablyInternational) {
				reverseDates = true;
			}
			for (var z in delivery.items) {
				var item = delivery.items[z];
				var statusComplete = (item.status == 'Complete');
				var shipSaved = cleanUpDate(item.shipSaved,false,delivery.from,false,!statusComplete);
				var deliverSaved = cleanUpDate(item.deliverSaved,false,delivery.from,false,!statusComplete);
				if (reverseDates) {
					shipSaved = flexibleInternationalDate(shipSaved);
					deliverSaved = flexibleInternationalDate(deliverSaved);
				}
				delivery.items[z].shipSaved = shipSaved;
				delivery.items[z].deliverSaved = deliverSaved;
			}
		}

		if (x == 0) checkForError = true;

	} else {
		checkForError = true;
	}

	if (checkForError) {
		logRequest(1,this);
		if (this.responseText && (output = stripWhiteSpace(this.responseText)) && output.match(/(We'll be back soon|Apple's Online and phone Order Status services are temporarily unavailable due to a scheduled upgrade to our systems|We are currently updating our systems)/i)) {
			if (!delivery.count) delivery.count = -1;
		} else delivery.count = 0;
		delivery.notFound();
	}

	delivery.reqComplete++;
	delivery.updateItems();
	return;

}

function getAppleDates ( html ) {

	if (!html) return [];

	var shipDate = false;
	var deliverDate = false;
	var dateHints = [];

	// New: Arrives|Delivered
	var result = html.match(/(?:Arrives|Delivered|Estimated Delivery|Delivers on|Delivers by|Delivers between|Delivers|Delivered on|配送日|お届け予定日|運送日期)[:：]? ?([^<]*)/);
	if (result && result[1]) {
		deliverDate = result[1].replace(/(via|by|：)[^:<]+$/i,'');
		var dates = datesInRange(deliverDate);
		deliverDate = dates[1];
		dateHints = dateHints.concat(dates);
	}

	result = html.match(/(?:Shipped|Available to ship|Estimated Shipping|Ships by|Estimated Ship|Ships|出荷日|已上市)[:：]? ?([^<]*)/);
	if (result && result[1]) {
		// Fix ugly stuff like Ships: Ships by October 12th
		shipDate = result[1].replace(/(?:Shipped|Available to ship|Estimated Shipping|Ships by|Estimated Ship|Ships|出荷日|已上市)[:：]?/,'');
		var dates = datesInRange(shipDate);
		shipDate = dates[1];
		dateHints = dateHints.concat(dates);
		result = shipDate.match(/(?:Arrives|Delivered|Estimated Delivery|Delivers on|Delivers by|Delivers between|Delivers|Delivered on|配送日|お届け予定日|運送日期)[:：]? ?([^<]*)/);
		if (result && result[1]) {
			if (!deliverDate) deliverDate = result[1];
			shipDate = shipDate.replace(result[0],'');
		}
		shipDate = (trim(shipDate)) ? shipDate : false;
	}

	if (!shipDate && !deliverDate) {
		html = html.replace(/<select[^>]*>.*?<\/select>/i,'');
		result = html.match(/(?:Pick up):? ?([^<]*)/);
		if (result && result[1]) {
			deliverDate = result[0];
		}
	}

	return [shipDate,deliverDate,dateHints];

}

function serviceFromURL (url) {

	var result = url.match(/https?:\/\/([^\/]*)/i);
	if (result && result[1]) {

		var domain = result[1];
		var service = null;

		switch (domain) {
			case 'www.dhl.com':
				var data = getData(url);
				var no = data['AWB'];
				if (no) service = setService(null,'www.dhl.com',no);
				break;
			case 'www.fedex.com':
				var data = getData(url);
				var no = data['tracknumbers'];
				if (no) service = setService(null,'www.fedex.com',no);
				break;
			case 'www.ontrac.com':
				var data = getData(url);
				var no = data['tracking'];
				if (!no) no = data['tracking_number'];
				if (no) service = setService(null,'www.ontrac.com',no);
				break;
			case 'www.ups.com':
			case 'wwwapps.ups.com':
				var data = getData(url);
				var no = data['InquiryNumber1'];
				if (!no) no = data['tracknum'];
				if (!no) no = data['trackNums'];
				if (!no) no = data['inquiry1'];
				if (!no) no = data['trackingNumber'];
				if (!no) no = data['InquiryNumber'];
				if (no) service = setService(null,'www.ups.com',no);
				break;
			case 'www.tnt.com':
				break;
			default:
				break;
		}

		return service;

	}
	return null;

}

function getAppleDetails () {

	var delivery = this.delivery;
	var childNo = this.args.childNo;

	if (this.responseText) {

		var output = stripWhiteSpace(this.responseText);
		if (result = output.match(/<a[^>]*data-action=\\"tracking-number\\"[^>]*href=\\"(.*?)\\"[^>]*>/i)) {

			var url = result[1];
			var service = serviceFromURL(url);

			if (service) {
				delivery.startUpdate(service,childNo);
			} else {
				delivery.reqCount++;
				this.args.site = 'AppleInline';
				this.getTracker();
			}

		} else {
			delivery.reqCount++;
			this.args.site = 'AppleInline';
			this.getTracker();
		}

	} else {
		logRequest(1,this);
		if (!delivery.count) delivery.count = -1;
		delivery.notFound();
	}

	delivery.reqComplete++;
	delivery.updateItems();
	return;

}

function getFedEx () {

	var delivery = this.delivery;
	var service = this.args.service;
	var locale = this.args.locale;

	if (this.status == 200 && this.responseText && service && service.no) {

		var no = service.no.replace(/^([0-9]+)E$/,'$1');
		var url = 'https://www.fedex.com/trackingCal/track';
		var jsonData = JSON.stringify({
			"TrackPackagesRequest":{
				"appType":"wtrk",
				"uniqueKey":"",
				"processingParameters":{"anonymousTransaction":true,"clientId":"WTRK","returnDetailedErrors":true,"returnLocalizedDateTime":false},
				"trackingInfoList":[{
					"trackNumberInfo":{"trackingNumber":no,"trackingQualifier":"","trackingCarrier":""}
				}]
			}
		});

		var postData = 'data='+jsonData+'&action=trackpackages&locale='+locale+'&format=json&version=99';
		delivery.loadPage(url,getJSON,postData,this.args);

	} else {
		logRequest(1,this);
		if (!delivery.count) delivery.count = -1;
		delivery.notFound();
	}

	delivery.reqComplete++;
	delivery.updateItems();
	return;

}

function getFedExMultiple () {

	var delivery = this.delivery;
	var result = false;

	var output = trim(this.responseText);
	if ((this.status == 200 || this.status == 0) && output) {

		try {
			output = output.replace(/\\x/g,'%');
			object = JSON.parse(output);
		} catch (err) {
			logMessage(1,err);
			logRequest(1,this);
			object = {};
			success = false;
		}

		if (object) object = object.AssociatedShipmentsResponse;
		if (object && object.successful && object.associatedShipmentList) {
			for (var x in object.associatedShipmentList) {

				var anObject = object.associatedShipmentList[x];
				if (anObject.isSuccessful) {
					var trackingNbr = decodeURIComponent(anObject.trackingNbr);
					if (delivery.items && delivery.items[0] && (
						decodeURIComponent(anObject.trackingQualifier) == delivery.items[0].matchNo ||
						trackingNbr == delivery.no.replace(/\s+/g,''))) {

						delivery.items[0].title = deliveryTitle(delivery.no,delivery.name,
							trackingNbr,object.associatedShipmentList.length);

					} else {

						var item = resetItem();

						item.title = deliveryTitle(delivery.no,delivery.name,
							trackingNbr,object.associatedShipmentList.length);

						var status = decodeURIComponent(anObject.keyStatus);
						if (anObject.delivered) status = 'Complete';
						if (status) item.status = status;
						var statusComplete = (status == 'Complete');

						var shipDate = decodeURIComponent(anObject.shipDt);
						var deliverDate = (anObject.actDeliveryDt) ? decodeURIComponent(anObject.actDeliveryDt) : 
							decodeURIComponent(anObject.estDeliveryDt);
						if (deliverDate == 'Pending') deliverDate = false;
						if (shipDate) item.shipSaved = cleanUpDate(shipDate,false,delivery.from,false,false);
						if (deliverDate) item.deliverSaved = cleanUpDate(deliverDate,false,delivery.from,false,(!statusComplete));

						var count = delivery.items.push(item);
						var service = setService(false,'www.fedex.com',trackingNbr);
						if (service) delivery.startUpdate(service,count-1);

					}
				}

			}
		}
		delivery.count = delivery.items.length;

	}

	delivery.reqComplete++;
	delivery.updateItems();
	return;

}

function getHongkong () {

	var delivery = this.delivery;
	var output = stripWhiteSpace(this.responseText);
	if (delivery.tries == 0 && (result = output.match(/--> ?<form id="form2" action="([^"]*)"[^>]*>/i))) {

		var service = (this.args && this.args.service) ? this.args.service : delivery;
		var url = result[1];
		if (!url.match(/https?:\/\//i)) url = 'http://app3.hongkongpost.hk/CGI/mt/'+url;
		var postData = 'tracknbr='+encodeURIComponent(service.no.replace(/ /g,''));

		delivery.tries++;
		delivery.loadCookiePage(url,getHongkong,postData,this.args);

	} else if (result = output.match(/<!-- #[a-z]* --> ?<tr> ?<td> ?<a href="([^"]*)"[^>]*>/i)) {

		delivery.url = result[1].replace(/&amp;/g,'&');
		this.args.type = 1;
		delivery.loadCookiePage(delivery.url,getTracker,false,this.args);

	} else {

		this.args = setArgs('HongkongPost',false);
		this.args.type = 0;
		delivery.reqCount++;
		this.getTracker();

	}

	delivery.reqComplete++;
	delivery.updateItems();
	return;

}

function getJSON () {

	var delivery = this.delivery;
	var result = false;
	var site = this.args.site;
	var type = this.args.type;
	var childNo = this.args.childNo;
	var isChild = this.args.isChild;
	var ignoreStatus = [ 'Aramex', 'GLS', 'LaserShip', 'PostNord', 'TNT' ];

	var output = trim(this.responseText);
	// Comes wrapped in a function, unwrap it
	if (site == 'DPD') output = output.replace(/^[a-z0-9_]*\(((.|[\r\n])*)\);?$/,'$1');

	if ((this.status == 200 || this.status == 0 || ignoreStatus.indexOf(site) != -1) && output) {

		var object;
		var from = (this.args.service) ? this.args.service.from : delivery.from;
		var deliveredStatus;
		var deliveredLocation;
		var deliveredString = /Delivered/i;
		var connect = true;
		var success = true;
		var status;
		var details = {};
		var shipDate; // Ship date
		var deliverDate; // Delivery date
		var matchNo;
		var errorText;
		var multipleItems;

		if (!isChild) {
			var item = resetItem();
			item.title = (delivery.name) ? htmlEntities(delivery.name) : htmlEntities(delivery.no);
			delivery.items = [];
			delivery.items[childNo] = item;
		}

		try {
			if (site == 'FedEx') output = output.replace(/\\x/g,'%');
			object = JSON.parse(output);
		} catch (err) {
			logMessage(1,err);
			logRequest(1,this);
			object = {};
			success = false;
		}

		if (site == 'Aramex') {

			object = object[0];
			if (!object || object.found == false) {
				success = false;
			} else {

				status = object.currentStatus;
				shipDate = object.dateShipped;
				deliverDate = object.deliveredDate;
				if (!deliverDate && object.transitMsg) {
					var result = object.transitMsg.match(/delivered by ([0-9\/]+)/i);
					if (result) deliverDate = result[1];
				}
				deliveredStatus = object.trackingProgress;
				deliveredString = /100%/i;

				var events = object.packageHistory;
				if (events && events[0]) {

					var event = events[0];
					details.date = event.actionDate;
					details.status = event.activity;
					details.location = event.location;					

					var route = [];
					for (var x = 0; x < events.length; x++) {
						var location = events[x].location;
						if (location) route.push(cleanLocation(location,from));
					}
					details.route = route;

				}
			}

		} else if (site == 'CanadaPost') {

			var lang = this.args.lang;

			if (object && object.status) {

				status = object.status;
				if (object.actualDlvryDate) {
					deliverDate = object.actualDlvryDate;
				} else if (object.expectedDlvryDateTime) {
					deliverDate = object.expectedDlvryDateTime.dlvryDate;
				}
				if (object.delivered == 'true') status = 'Delivered';

				var events = object.events;
				if (events && events[0]) {

					var event = events[0];
					details.date = event.datetime.date;
					details.status = (lang == 'fr') ? event.descFr : event.descEn;
					if (event.locationAddr) {
						var parts = event.locationAddr;
						var country = (lang == 'fr') ? parts.countryNmFr : parts.countryNmEn;
						var location = locationFromParts(parts.city,parts.regionCd,parts.postCd,country);
						if (location) details.location = location;
					}

					var route = [];
					for (var x = 1; x < events.length; x++) {
						var parts = events[x].locationAddr;
						var location = locationFromParts(parts.city,parts.regionCd,parts.postCd,parts.countryNmEn);
						if (location) route.push(cleanLocation(location,from));
					}
					details.route = route;

				}

			} else {
				success = false;
				if (object && object.error) {
					errorText = (lang == 'fr') ? object.error.descFr : object.error.descEn;
				}
			}

		} else if (site == 'DHL') {

			if (object && object.results) {
				object = object.results[0];
				if (object) {

					status = object.description;
					// if (object.signature) deliverDate = object.signature.description;
					if (object.edd) {
						deliverDate = object.edd.date;
						if (!deliverDate) deliverDate = object.edd.product;
					}

					if (object.delivery) deliveredStatus = object.delivery.status;
					if (object.destination) deliveredLocation = object.destination.value;
					deliveredString = /^delivered$/i;

					var events = object.checkpoints;
					if (events && events[0]) {

						var event = events[0];
						details.date = event.date;
						details.status = event.description;
						details.location = event.location;					

						var route = [];
						for (var x = 1; x < events.length; x++) {
							var location = events[x].location;
							if (location) route.push(cleanLocation(location,from));
						}
						details.route = route;

					}

				}
			} else {
				success = false;
				if (object && object.errors) {
					object = object.errors[0];
					if (object) errorText = object.message;
				}
			}

		} else if (site == 'DPD') {

			if (object.parcellifecycleResponse && object.parcellifecycleResponse.parcelLifeCycleData) {

				object = object.parcellifecycleResponse.parcelLifeCycleData;
				if (object.statusInfo) {
					var infos = object.statusInfo;
					for (var x = 0; x < infos.length; x++) {
						var info = infos[x];
						if (info.isCurrentStatus) {
							status = info.label;
							deliveredStatus = info.status;
						}
					}
				}

				var events = (object.scanInfo) ? object.scanInfo.scan : null;
				if (events && events.length > 0) {

					var event = events[events.length-1];
					details.date = event.date;
					details.location = (event.scanData) ? event.scanData.location : null;
					if (event.scanDescription && event.scanDescription.content) {
						details.status = event.scanDescription.content[0];
					}

					var route = [];
					for (var x = 0; x < events.length; x++) {
						var event = events[x];
						var location = (event.scanData) ? event.scanData.location : null;
						if (location) route.unshift(cleanLocation(location,from));
					}
					details.route = route;

				}

			} else {
				success = false;
				if (object.serviceMessage) {
					object = object.serviceMessage;
					if (object.validationError) {
						errorText = object.validationError.message;
					}
				}
			}
			deliveredString = /^DELIVERED$/i;

		} else if (site == 'FedEx') {

			if (object) object = object.TrackPackagesResponse;
			if (object && object.successful && object.packageList) {
				for (var x in object.packageList) {
					var anObject = object.packageList[x];
					if (anObject.isSuccessful) {

						status = anObject.mainStatus;
						if (anObject.isDelivered) status = 'Delivered';

						details = {};
						details.status = status;

						var events = anObject.scanEventList;
						var event = events[0];
						if (event) {
							if (event.status) details.status = decodeURIComponent(event.status);
							if (event.scanDetails) details.status += ': '+decodeURIComponent(event.scanDetails);
							details.date = decodeURIComponent(event.date);
							details.location = decodeURIComponent(event.scanLocation);
						}

						var route = [];
						for (var eventNo = 0; eventNo < events.length; eventNo++) {
							var location = events[eventNo].scanLocation;
							if (location) route.push(cleanLocation(decodeURIComponent(location),from));
						}
						details.route = route;

						shipDate = decodeURIComponent(anObject.shipDt);
						deliverDate = (anObject.actDeliveryDt) ? decodeURIComponent(anObject.actDeliveryDt) : 
							decodeURIComponent(anObject.estDeliveryDt);
						if (deliverDate == 'Pending') deliverDate = false;
						matchNo = decodeURIComponent(anObject.trackingQualifier);

						if (!isChild && anObject.totalPieces > 1 && anObject.associationInfoList && anObject.associationInfoList[0]) {
							var associatedObject = anObject.associationInfoList[0];
							var url = 'https://www.fedex.com/trackingCal/track';
							var locale = this.args.locale;
							var jsonData = JSON.stringify({
								"AssociatedShipmentRequest":{
									"appType":"WTRK",
									"uniqueKey":"",
									"processingParameters":{"anonymousTransaction":true,"clientId":"WTRK","returnDetailedErrors":true,"returnLocalizedDateTime":false},
									"masterTrackingNumberInfo":{
										"trackingNumberInfo":{
											"trackingNumber":decodeURIComponent(associatedObject.trackingNumberInfo.trackingNumber),
											"trackingQualifier":decodeURIComponent(associatedObject.trackingNumberInfo.trackingQualifier),
											"trackingCarrier":decodeURIComponent(associatedObject.trackingNumberInfo.trackingCarrier)
										},
										"associatedType":decodeURIComponent(associatedObject.associatedType)
									}
								}
							});
							var postData = 'data='+jsonData+'&action=getAssociatedShipments&locale='+locale+'&format=json&version=99';
							delivery.loadPage(url,getFedExMultiple,postData,false);
							break;
						}
						if (object.packageList.length > 1) {
							if (!multipleItems) multipleItems = [];
							multipleItems[x] = {};
							multipleItems[x].title = decodeURIComponent(anObject.trackingNbr);
							multipleItems[x].status = status;
							multipleItems[x].details = details;
							multipleItems[x].shipDate = shipDate;
							multipleItems[x].deliverDate = deliverDate;
						}

					} else {
						if (anObject.errorList && anObject.errorList[0]) errorText = decodeURIComponent(anObject.errorList[0].message);
						success = false;
					}
				}

			} else {
				connect = false;
				success = false;
			}

		} else if (site == 'GLS') {

			if (object && object.tuStatus) {

				object = object.tuStatus[0];
				if (object.progressBar.statusInfo) status = object.progressBar.statusInfo;

				var events = object.history;
				if (events && events[0]) {

					var event = events[0];
					details.date = event.date;
					details.status = event.evtDscr;
					if (event.address) {
						var location = locationFromParts(event.address.city,null,null,event.address.countryName);
						if (location) details.location = location;
					}

					var route = [];
					for (var x = 0; x < events.length; x++) {
						var event = events[x];
						if (event.address) {
							var location = locationFromParts(event.address.city,null,null,event.address.countryName);
							if (location) route.push(cleanLocation(location,from));
						}
					}
					details.route = route;

				}

			} else if (object.exceptionText) {
				errorText = clearEmptyString(stripHTML(object.exceptionText));
				success = false;
			} else {
				connect = false;
				success = false;
			}
			deliveredString = /^DELIVERED/i;

		} else if (site == 'HLG') {

			deliveredString = /^Delivered$/i;

			if (object && object.length > 0) {
				for (var x in object) {

					var shipment = object[x];
					status = shipment.lastStatus.description;
					deliverDate = shipment.expectedDate;
					if (shipment.statusImageID == 5) {
						status = 'Delivered';
					}

					details = {};
					details.status = status;

					var events = shipment.statusHistory;
					var event = events[0];
					if (event) {
						details.status = event.description;
						details.date = event.dateTime;
						details.location = locationFromStatus(event.description,from);
					}

					var route = [];
					for (var x = 0; x < events.length; x++) {
						var event = events[x];
						var location = locationFromStatus(event.description,from);
						if (location) route.push(cleanLocation(location,from));
					}
					details.route = route;

					if (!isChild && object.length > 1) {
						if (!multipleItems) multipleItems = [];
						multipleItems[x] = {};
						multipleItems[x].title = shipment.shipmentID;
						multipleItems[x].status = status;
						multipleItems[x].details = details;
						multipleItems[x].shipDate = shipDate;
						multipleItems[x].deliverDate = deliverDate;
					}

				}
			} else {
				success = false;
			}

		} else if (site == 'LaserShip') {

			var events = object.Events;
			if (events && events[0]) {

				var event = events[0];
				shipDate = object.ReceivedOn;
				deliverDate = object.EstimatedDeliveryDate;
				details.date = event.DateTime;
				details.status = event.EventShortText;
				if (event.Location) details.status += ': '+event.Location;
				var location = locationFromParts(event.City,event.State,event.PostalCode,event.Country);
				if (location) details.location = location;

				var route = [];
				for (var x = 0; x < events.length; x++) {
					var event = events[x];
					var location = locationFromParts(event.City,event.State,event.PostalCode,event.Country);
					if (location) route.push(cleanLocation(location,from));
				}
				details.route = route;

			} else {
				success = false;
				errorText = object.ErrorMessage;				
			}

		} else if (site == 'Poste-it') {

			status = object.sintesiStato;
			if (status) {

				var events = object.listaMovimenti;
				var event = events[events.length-1];
				if (event) {
					details.status = event.statoLavorazione;
					var statusDate = event.dataOra;
					if (statusDate) details.date = new Date(statusDate).toISOString();
					var location = event.luogo;
					if (location) details.status += ': '+location;
					details.location = location;
				}
				if (object.stato == '5') {
					status = 'Complete';
				}

				var route = [];
				for (var x = 0; x < events.length; x++) {
					var event = events[x];
					var location = event.luogo;
					if (location) route.push(cleanLocation(location,from));
				}
				details.route = route.reverse();

			} else {
				success = false;
			}

		} else if (site == 'PostNL') {

			deliveredString = /^Delivered$/i;

			status = object.shipmentPhase;
			if (status) {

				shipDate = object.sentDate;
				deliverDate = object.deliveredDate;
				if (!deliverDate && object.deliveryExpectation) {
					deliverDate = object.deliveryExpectation.deliveryDateUntil;
				}

				var events = object.trackingEvents;
				var event = events[0];
				if (event) {
					details.status = event.description;
					details.date = event.dateTime;
					var location = event.location;
					if (location) details.status += ': '+location;
					details.location = location;
				}

				var route = [];
				for (var x = 0; x < events.length; x++) {
					var event = events[x];
					var location = event.location;
					if (location) route.push(cleanLocation(location,from));
				}
				details.route = route.reverse();

			} else {
				success = false;
			}

		} else if (site == 'PostNord') {

			deliveredString = /^Delivered$/i;

			if (object) object = object.TrackingInformationResponse;
			if (object && object.shipments && object.shipments.length > 0) {
				for (var x in object.shipments) {

					var shipment = object.shipments[x];
					status = shipment.statusText.body;
					deliverDate = shipment.deliveryDate || shipment.estimatedTimeOfArrival;
					if (shipment.status == 'DELIVERED') {
						status = 'Delivered';
					}

					for (var x in shipment.items) {

						var shipmentItem = shipment.items[x];
						details = {};
						details.status = status;

						var events = shipmentItem.events;
						var event = events[events.length-1];
						if (event) {
							details.status = event.eventDescription;
							details.date = event.eventTime;
							var eventLoc = event.location;
							if (eventLoc) {
								var city = eventLoc.city;
								if (!city && eventLoc.displayName != eventLoc.country) city = eventLoc.displayName;
								var location = locationFromParts(city,null,eventLoc.postcode,eventLoc.country);
								details.location = location;
							}
						}

						var route = [];
						for (var eventNo = 0; eventNo < events.length; eventNo++) {
							var eventLoc = events[eventNo].location;
							if (eventLoc) {
								var city = eventLoc.city;
								if (!city && eventLoc.displayName != eventLoc.country) city = eventLoc.displayName;
								var location = locationFromParts(city,null,eventLoc.postcode,eventLoc.country);
								if (location) route.unshift(cleanLocation(location,from));
							}
						}
						details.route = route;

						if (!isChild && (object.shipments.length > 1 || shipment.items.length > 1)) {
							if (!multipleItems) multipleItems = [];
							multipleItems[x] = {};
							multipleItems[x].title = shipmentItem.itemId;
							multipleItems[x].status = status;
							multipleItems[x].details = details;
							multipleItems[x].shipDate = shipDate;
							multipleItems[x].deliverDate = deliverDate;
						}

					}

				}
			} else {
				success = false;
			}

		} else if (site == 'TNT') {

			deliveredString = /^Delivered$/i;

			if (object) object = object['tracker.output'];
			if (object && object.consignment) {
				for (var x in object.consignment) {

					var anObject = object.consignment[x];

					deliverDate = anObject.deliveryDueDate;
					details = {};
					details.status = status;

					var events = anObject.statusData;
					var event = events[0];
					if (event) {
						details.status = event.statusDescription;
						details.date = event.localEventDate;
						details.location = event.depot;
						if (event.groupCode == 'DELRED') {
							status = 'Delivered';
							if (anObject.deliveryTown) {
								details.location = anObject.deliveryTown;
								if (anObject.destinationCountry) details.location += ', '+anObject.destinationCountry;
							}
						}
					}

					var route = [];
					for (var eventNo = 0; eventNo < events.length; eventNo++) {
						var location = events[eventNo].depot;
						if (location) route.push(cleanLocation(location,from));
					}
					details.route = route;

					if (!isChild && object.consignment.length > 1) {
						if (!multipleItems) multipleItems = [];
						multipleItems[x] = {};
						multipleItems[x].title = anObject.consignmentNumber;
						multipleItems[x].status = status;
						multipleItems[x].details = details;
						multipleItems[x].shipDate = shipDate;
						multipleItems[x].deliverDate = deliverDate;
					}

				}
			} else {
				success = false;
				if (object && object.error) errorText = object.error.message;
			}

		}

		var itemCount = (!isChild && multipleItems) ? multipleItems.length : 1;
		for (var x = 0; x < itemCount; x++) {

			var itemNo = childNo;
			if (!isChild && multipleItems) {
				itemNo = x;
				if (!delivery.items[x]) delivery.items[x] = resetItem();
				var thisItem = multipleItems[x];
				var thisTitle = (thisItem) ? thisItem.title : '';
				delivery.items[x].title = deliveryTitle(delivery.no,delivery.name,thisTitle,multipleItems.length);
				status = (thisItem) ? thisItem.status : '';
				details = (thisItem) ? thisItem.details : '';
				shipDate = (thisItem) ? thisItem.shipDate : '';
				deliverDate = (thisItem) ? thisItem.deliverDate : '';
				deliveredStatus = (thisItem) ? thisItem.deliveredStatus : '';
				deliveredLocation = (thisItem) ? thisItem.deliveredLocation : '';
			}

			if (!status) status = details.status;
			if (!deliveredStatus) deliveredStatus = status;
			if (deliveredStatus && deliveredStatus.match(deliveredString)) status = 'Complete';
			var statusComplete = (status == 'Complete');
			if (statusComplete && !deliverDate) deliverDate = details.date;
			if (details.date) details.date = cleanUpDate(details.date,false,from,false,false);
			if (statusComplete && deliveredLocation) details.location = deliveredLocation;
			if (details.location == undefined) details.location = '';
			details.mapLocation = (details.location) ? cleanLocation(details.location,from) : '';

			if (success && delivery.items[itemNo]) {
				if (status) delivery.items[itemNo].status = status;
				if (details) delivery.items[itemNo].details = details;
				if (shipDate) delivery.items[itemNo].shipSaved = cleanUpDate(shipDate,false,from,false,false);
				if (deliverDate) delivery.items[itemNo].deliverSaved = cleanUpDate(deliverDate,false,from,false,(!statusComplete));
				if (matchNo) delivery.items[itemNo].matchNo = matchNo;
			}

		}

		if (isChild) {
			if (!connect) {
				if (delivery.previousItems) delivery.items = delivery.previousItems;
			}
		} else {
			if (!success) {
				logRequest(1,this);
				delivery.tries = 0;
				if (connect) {
					delivery.count = 0;
				} else if (delivery.count) {
					if (delivery.previousItems) delivery.items = delivery.previousItems;
				} else delivery.count = -1;
				delivery.notFound(errorText);
			} else delivery.count = itemCount;
		}

	} else if (isChild) {

		logRequest(1,this);
		if (delivery.previousItems) delivery.items = delivery.previousItems;
		delivery.notFound();

	} else {

		logRequest(1,this);
		if (!delivery.count) {
			if (site == 'PostNL' && (this.status == 400 || this.status == 404)) {
				delivery.count = 0;
			} else if (site == 'HLG' && this.status == 204) {
				delivery.count = 0;
			} else {
				delivery.count = -1;
			}
		}
		delivery.notFound();

	}

	delivery.reqComplete++;
	delivery.updateItems();
	return;

}

function getHTMLJSON () {

	var delivery = this.delivery;
	var args = this.args;
	var site = args.site;

	var output = trim(this.responseText);

	if ((this.status == 200 || this.status == 0) && output) {

		var html = '';

		try {
			object = JSON.parse(output);
		} catch (err) {
			logMessage(1,err);
			logRequest(1,this);
			object = {};
		}

		if (site == 'Chronopost') {
			if (object.top) html += object.top + ' '
			if (object.tab) html += object.tab
		}

		delivery.loadSection(html,args);

	} else {
		logRequest(1,this);
		if (!delivery.count) delivery.count = -1;
		delivery.notFound();
	}

	delivery.reqComplete++;
	delivery.updateItems();
	return;

}

function getMultiple () {

	var delivery = this.delivery;
	var sections = false;
	var site = this.args.site;
	var commonRegEx;
	var commonOutput;
	var packageRegEx;
	var packagePattern;
	var urlRegEx;
	var urlReplace;
	var rootUrl;
	var labelRegEx;

	if ((this.status == 200 || this.status == 0) && this.responseText) {

		var output = stripWhiteSpace(this.responseText);

		if (site == 'Parcelforce') {

			packageRegEx = /<tr[^>]*> ?<td> ?<a href="[^"]*"[^>]*>[^<]*<\/a> ?<\/td> ?<td>[^<]*<\/td> ?<td>[^<]*<\/td> ?<td>[^<]*<\/td> ?<td>[^<]*<\/td> ?<\/tr>/gi;
			urlRegEx = /<a href="([^"]*)"[^>]*>/i;
			labelRegEx = /<a href="[^"]*"[^>]*>([^<]*)<\/a>/i;
			rootUrl = 'https://www.parcelforce.com';

		} else if (site == 'Post.at') {

			packageRegEx = /<tr> ?<td[^>]*> ?<a href="[^"]+">[0-9]+<\/a> ?<\/td> ?<td[^>]*>[^<]*<\/td> ?<td[^>]*>[^<]*<\/td> ?<td[^>]*>[^<]*<\/td> ?<\/tr>/gi;
			urlRegEx = /<a href="([^"]+)">/i;
			labelRegEx = /<a href="[^"]+">([0-9]+)<\/a>/i;	
			rootUrl = 'https://www.post.at';		

		} else if (site == 'Purolator') {

			packageRegEx = /var jsSummaryTable = \[ \['([A-Z0-9]*)','([0-9-]+)','([A-Z0-9]+)'/gi;
			urlRegEx = /\['([A-Z0-9]*)','([0-9-]+)','([A-Z0-9]+)'/i;
			urlReplace = 'https://www.purolator.com/'+this.args.lang+'/ship-track/tracking-details.page?pin=$3&sdate=$2';

		} else if (site == 'UPS') {

			packageRegEx = /<!-- Begin Section Module --> ?<div class="secLvl[^"]*"> ?<fieldset> ?(.*?)<!-- End Sub Module --> ?<\/fieldset>/gi;
			labelRegEx = /<input[^>]*name="trackNums"[^>]*value="([^"]*)"[^>]*>/i;

		} else if (site == 'Yamato') {

			commonRegEx = /<table class="saisin">(.*?)<\/table>(.*?)<table class="meisaibase">(.*?)<\/table>/i;
			packagePattern = '<tr> ?<td[^>]* rowspan="2"> ?([0-9]+) ?<\/td>(.*?)(<tr> ?<td[^>]* rowspan="2">|<\/table>)';
			labelRegEx = /<tr> ?<td[^>]* rowspan="2"> ?([0-9]+)/i;

		}

		if (commonRegEx) {
			var result = output.match(commonRegEx);
			if (result) commonOutput = result[0];
		}

		var packages;
		if (packageRegEx) {
			packages = output.match(packageRegEx);
		} else if (packagePattern) {
			packages = overlappingMatches(output,packagePattern);
		}

		if (packages) {
			for (var y in packages) {

				var packageOutput = packages[y];
				var item = resetItem();
				if (y == 0) delivery.items = [];

				var label = false;
				if (labelRegEx) {
					var result = packageOutput.match(labelRegEx);
					if (result) label = trim(result[1]);
				}
				item.title = deliveryTitle(delivery.no,delivery.name,label,packages.length);
				delivery.items[y] = item;

				var args = setArgs(site,y);
				args.type = this.args.type;

				if (urlRegEx && (result = packageOutput.match(urlRegEx))) {
					var url;
					if (urlReplace) {
						url = result[0].replace(urlRegEx,urlReplace);
					} else {
						url = result[1];
					}
					url = url.replace(/&amp;/g,'&');
					if (rootUrl && !url.match(/^https?:\/\//i)) {
						if (url.charAt(0) != '/') url = '/'+url;
						url = rootUrl+url;
					}
					delivery.loadPage(url,getTracker,false,args);
				} else {
					if (commonOutput) packageOutput = commonOutput+' '+packageOutput;
					delivery.loadSection(packageOutput,args);
				}

			}
			delivery.count = delivery.items.length;
		} else {
			delivery.reqCount++;
			this.getTracker();
		}

	} else {
		delivery.reqCount++;
		this.getTracker();
	}

	delivery.reqComplete++;
	delivery.updateItems();
	return;

}

function getDetailPage () {

	var delivery = this.delivery;
	var site = this.args.site;
	var isChild = this.args.isChild;

	if (this.responseText) {

		var detailPageRegEx;
		var baseURL;
		var useFallback = false;

		switch (site) {
			case 'LaPoste':
				detailPageRegEx = /<a[^>]*href="([^"]*)"[^>]*> ?Détails ?<\/a>/;
				break;
			case 'OnTrac':
				detailPageRegEx = /<a href="([^"]*)">DETAILS<\/a>/i;
				baseURL = 'https://www.ontrac.com/';
				break;
		}

		var output = stripWhiteSpace(this.responseText);
		if (detailPageRegEx && (result = output.match(detailPageRegEx))) {

			var url = result[1];
			if (baseURL && !url.match(/^https?:\/\//i)) url = baseURL+url;
			if (!isChild) delivery.url = url;

			if (site == 'LaPoste' && url.lastIndexOf('http://www.csuivi.courrier.laposte.fr/',0) !== 0) {
				useFallback = true;
			}

			if (useFallback && getCommonFilesVersion() < 173) {
				logMessage(1,'No fallback support, skipping details');
				delivery.reqCount++;
				this.args.site += '-Fallback';
				this.getTracker();
			} else {
				if (useFallback) {
					this.args.fallbackStatus = this.status;
					this.args.fallbackText = this.responseText;
				}
				delivery.loadPage(url,getTracker,false,this.args);
			}

		} else {
			delivery.reqCount++;
			this.getTracker();
		}

	} else {
		logRequest(1,this);
		if (!delivery.count) delivery.count = -1;
		delivery.notFound();
	}

	delivery.reqComplete++;
	delivery.updateItems();
	return;

}

function getTracker () {

	var delivery = this.delivery;
	var result = false;
	var site = this.args.site;
	var type = this.args.type;
	var childNo = this.args.childNo;
	var isChild = this.args.isChild;
	var ignore404 = [];

	if ((this.status == 200 || this.status == 0 || 
		(this.status == 404 && ignore404.indexOf(site) != -1)) && this.responseText) {

		var from = (this.args.service) ? this.args.service.from : delivery.from;
		var languageFrom = from;
		var statusRegEx;
		var ignoreStatusRegEx;
		var statusNo = 1;
		var detailSection;
		var detailItemRegEx;
		var detailRegEx;
		var detailDateNo;
		var detailTimeNo;
		var detailLocationNo;
		var detailLocationNoExtra = -1;
		var detailStatusNo;
		var detailStatusNoExtra = -1;
		var detailDateRegEx;
		var detailLocRegEx;
		var ignoreDetailRegEx;
		var latestFirst = true;
		var sortItemsByDate = true;
		var shipDateRegEx;
		var deliverDateRegEx;
		var altDeliverDateRegEx;
		var splitDateRanges = false;
		var deliverUpdatedRegEx; // Used if there's a second, preferred delivery date
		var deliverLocRegEx;
		var deliverLocExtraRegEx;
		var deliveredRegEx;
		var deliveredTodayString;
		var deliveredString = /Delivered/i;
		var errorRegEx;
		var connectRegEx;
		// var captchaRegEx;
		var checkForNo = false;
		var foundStatus = false;
		var status;
		var details;
		var shipDate;
		var deliverDate;
		var errorText;
		var useDetailDateWhenDelivered = false;
		var skipExtraOnError = false;
		var retryOnError = false;
		var notSupportedOnError = false;
		var output = stripWhiteSpace(this.responseText);

		if (site == 'AmazonTracker') {

			var languageFrom = getLanguageFrom(from,output);

			statusRegEx = /(?:<span class="[^"]*delivery-date">|<span class="a-size-large a-color-[a-z]+ a-text-bold">)([^<]*)/i;
			if (output.match(statusRegEx)) {
				detailItemRegEx = /(?:<div class="a-box[^"]*"><div[^>]*> ?<span[^>]*>(?:[^:]+:)? ?([^<]*)<\/span> ?<\/div> ?<\/div> ?)?<div[^>]*> ?<div[^>]*> ?<div[^>]*> ?<div[^>]*>([^<]*)<\/div> ?<div[^>]*> ?<span>([^<]*)<\/span> ?(?:<br> ?<span[^>]*>([^<]*)<\/span>)?/gi;
				detailRegEx = /(?:<div class="a-box[^"]*"><div[^>]*> ?<span[^>]*>(?:[^:]+:)? ?([^<]*)<\/span> ?<\/div> ?<\/div> ?)?<div[^>]*> ?<div[^>]*> ?<div[^>]*> ?<div[^>]*>([^<]*)<\/div> ?<div[^>]*> ?<span>([^<]*)<\/span> ?(?:<br> ?<span[^>]*>([^<]*)<\/span>)?/i;
				detailDateNo = 1;
				detailLocationNo = 4;
				detailStatusNo = 3;
				// shipDateRegEx = /(?:<div class="will-ship-mini-date">|<span class="shipped-date">)([^<]*)/i;
				deliverDateRegEx = /(?:<span class="[^"]*delivery-date">|<span class="a-size-large a-color-[a-z]+ a-text-bold">) ?(?:Arriving|Expected(?: by)?|Now expected|Zustellung|Erwartete Zustellung|Voraussichtlich|Voraussichtliche Lieferung|Entrega(?: el)?|Entrega prevista|Livraison(?: prévue| estimée)?(?: le)?|En cours d'acheminement|Arriverà|In consegna|Delivered|Zugestellt|Entregado|Livré|Consegnato|配達を完了しました|配達完了):? ?([^<]*)/i;
				altDeliverDateRegEx = /(?:<span class="[^"]*delivery-date">|<span class="a-size-[a-z]+ a-color-[a-z]+ a-text-bold">) ? ?([0-9]+月[0-9]+日|[0-9\/]*)[^<]*に到着予定/i;
				deliveredRegEx = /<div class="package-status on-time" data-width="([^"]*)"[^>]*>/i;
				deliveredString = /^100$/i;
			} else {
				statusRegEx = /<span id="primaryStatus"[^>]*>(.*?)(?:<\/span> ?<span id="secondaryStatus">|<\/div>)/i;
				detailItemRegEx = /(?:<span class="tracking-event-date">([^<]*)<\/span> ?<\/div> ?)?<div[^>]*> ?<div[^>]*> ?<span class="tracking-event-time">([^<]*)<\/span> ?<span[^>]*><\/span> ?<\/div> ?<div[^>]*> ?<div[^>]*> ?<span class="tracking-event-message">([^<]*)<\/span> ?<\/div> ?<div[^>]*> ?<span class="tracking-event-location">([^<]*)<\/span> ?<\/div>/gi;
				detailRegEx = /(?:<span class="tracking-event-date">([^<]*)<\/span> ?<\/div> ?)?<div[^>]*> ?<div[^>]*> ?<span class="tracking-event-time">([^<]*)<\/span> ?<span[^>]*><\/span> ?<\/div> ?<div[^>]*> ?<div[^>]*> ?<span class="tracking-event-message">([^<]*)<\/span> ?<\/div> ?<div[^>]*> ?<span class="tracking-event-location">([^<]*)<\/span> ?<\/div>/i;				
				detailDateNo = 1;
				detailLocationNo = 4;
				detailStatusNo = 3;
				// shipDateRegEx = /<span class="milestone-primaryMessage[^"]*"> ?(?:Expected to ship|Shipped) (?:<span [^>]*>)?([^<]*)/i;
				deliverDateRegEx = /(?:<span id="primaryStatus"[^>]*>|<span class="milestone-primaryMessage[^"]*">) ?(?:Arriving|Expected(?: by)?|Now expected|Zustellung|Erwartete Zustellung|Voraussichtlich|Voraussichtliche Lieferung|Entrega(?: el)?|Entrega prevista|Livraison(?: prévue| estimée)?(?: le)?|En cours d'acheminement|Arriverà|In consegna|Delivered|Zugestellt|Entregado|Livré|Consegnato|配達を完了しました|配達完了):? ?(.*?)(?:<\/span> ?<span id="secondaryStatus">|<\/div>)/i;
				altDeliverDateRegEx = /(?:<span id="primaryStatus"[^>]*>|<span class="milestone-primaryMessage[^"]*">) ? ?([0-9]+月[0-9]+日|[0-9\/]*)[^<]*に到着予定/i;
				deliveredRegEx = /"lastReachedMilestone" ?: ?"([^"]*)"/i;
				deliveredString = /^DELIVERED$/i;
			}
			ignoreStatusRegEx = /^ ?(?:Arriving|Expected(?: by)?|Now expected|Zustellung|Erwartete Zustellung|Voraussichtlich|Voraussichtliche Lieferung|Entrega(?: el)?|Entrega prevista|Livraison(?: prévue| estimée)?(?: le)?|Arriverà|In consegna|Delivered|Zugestellt|Entregado|Livré|Consegnato|配達を完了しました|配達完了)/i;
			splitDateRanges = true;
			useDetailDateWhenDelivered = true;
			sortItemsByDate = false;

		} else if (site == 'AppleInline') {

			detailRegEx = /<td[^>]*class=\\"tdata current-status status-notification\\">([^<]*)<\\\/td> ?(<td[^>]*> ?<\\\/td> ?)?<td class=\\"tdata current-status date\\">([^<]*)<\\\/td>/i;
			detailDateNo = 3;
			detailStatusNo = 1;
			deliverDateRegEx = /<th scope=\\"row\\" class=\\"text-alt tdata\\">(?:Est. Delivery|配送の目安)<\\\/th> ?<td colspan=\\"3\\" class=\\"tdata current-status date\\">([^<]*)<\\\/td>/i;
			deliveredString = /^(Delivered|配送済み)$/i;
			useDetailDateWhenDelivered = true;

		} else if (site == 'Chronopost') {

			statusRegEx = /<div class="ch-colis-information"> ?<img [^>]*>([^<]*)/i;
			detailItemRegEx = /<tr class="toggleElmt"> ?<td>([^<]*)<br[^>]*>([^<]*)<\/td> ?<td>([^<]*)<br[^>]*>([^<]*)<\/td> ?<td[^>]*>/gi;
			detailRegEx = /<tr class="toggleElmt"> ?<td>([^<]*)<br[^>]*>([^<]*)<\/td> ?<td>([^<]*)<br[^>]*>([^<]*)<\/td> ?<td[^>]*>/i;
			detailDateNo = 1;
			detailTimeNo = 2;
			detailLocationNo = 3;
			detailStatusNo = 4;
			deliveredString = /Delivered|Livraison effectuée|Livraison effectu&eacute;e/i;
			errorRegEx = /<div class="ch-colis-information"> ?<img [^>]*>([^<]*)/i;

		} else if (site == 'DHL (Germany)') {

			statusRegEx = /<dd>Status: ?([^<]*)/i;
			detailItemRegEx = /<dt data-label-date="[^"]*" data-label-location="[^"]*">[A-Z,]+ ([0-9.]+ [0-9:]+) ([^<]*)<\/dt> ?<dd data-label-status="[^"]*">(.*?)<\/dd>/gi;
			detailRegEx = /<dt data-label-date="[^"]*" data-label-location="[^"]*">[A-Z,]+ ([0-9.]+ [0-9:]+) ([^<]*)<\/dt> ?<dd data-label-status="[^"]*">(.*?)<\/dd>/i;
			detailDateNo = 1;
			detailLocationNo = 2;
			detailStatusNo = 3;
			deliverDateRegEx = /<div class="mm_deliveryTime"> ?<span class="highlighted">([^<]*)/i;
			deliveredRegEx = /<div class="mm_deliveryStep mm_deliveryStep([^"]*)"><\/div> ?<\/div>/i;
			deliveredString = /^-done$/i;
			// skipExtraOnError = true;
			connectRegEx = /(<div class="error"> ?<div class="red"> ?Der Service ist kurzzeitig nicht verfügbar\. Bitte versuchen Sie es später erneut\.|<h3>Das System ist zur Zeit sehr ausgelastet!)/i;

		} else if (site == 'GLS') {

			statusRegEx = detailRegEx = /<tr class="resulthead">.*?<tr[^>]*> ?<td>([^<]*)<\/td> ?<td> ?(?:[a-z]{2} [0-9]{3} )?(?:, )?([^<]*)<\/td> ?<td>[^<]*<\/td> ?<td>([^<]*)/i;
			statusNo = 3;
			detailDateNo = 1;
			detailLocationNo = 2;
			detailStatusNo = 3;
			shipDateRegEx = /(?:Pick-Up Date|Abhol-Datum|Date enlèvement|Afhentningsdato): ?<\/td> ?<td class="value"> ?([^<]*)/i;
			deliverDateRegEx = /(?:Delivery Date|Zustell-Datum|Date livraison|Leveringsdato): ?<\/td> ?<td class="value"> ?([^<]*)/i;
			deliveredString = /^(Delivered|Zugestellt|LIVRE|Forsendelsen er leveret)/i;
			connectRegEx = /<h1> ?Temporarily out of order ?<\/h1>/i;

		} else if (site == 'HongkongPost') {

			if (type == 1) {

				detailItemRegEx = /<tr> ?<td>([^<]*)<\/td> ?<td> ?([^0-9][^<]*)<\/td> ?<td> ?([^<]*)<\/td> ?<\/tr>/gi;
				detailRegEx = /<tr> ?<td>([^<]*)<\/td> ?<td> ?([^0-9][^<]*)<\/td> ?<td> ?([^<]*)<\/td> ?<\/tr>/i;
				detailDateNo = 1;
				detailStatusNo = 3;
				detailLocationNo = 2;
				latestFirst = false;

			} else {

				detailRegEx = /<!-- #[a-z]* --> ?(<P[^>]*> ?<IMG[^>]*> ?<a[^>]*>[^<]*<\/a> ?<\/P> ?)?(<p>)?(<span class="textNormalBlack">[^<]*<\/span>[^<]*<\/p>)?(.*?)<br \/>/i;
				statusNo = 4;
				detailStatusNo = 4;
				// Detail date: /<!-- #[a-z]* --> ?<p>.*on ([0-9]{1,2}-[a-z]+-[0-9]{4})/i;
				deliverDateRegEx = /was delivered on ([^<]*). ?<br \/>/i;

			}
			connectRegEx = /<p> ?The Track and Trace System is (temporarily out of service|now busy), please try later\./i;

		} else if (site == 'JapanPost') {

			detailItemRegEx = /<tr> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<\/tr> ?<tr> ?<td[^>]*>([^<]*)<\/td> ?<\/tr>/gi;
			detailRegEx = /<tr> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<\/tr> ?<tr> ?<td[^>]*>([^<]*)<\/td> ?<\/tr>/i;
			detailDateNo = 1;
			detailLocationNo = 4;
			detailLocationNoExtra = 5;
			detailStatusNo = 2;
			detailStatusNoExtra = 3;
			deliverDateRegEx = /(?:Scheduled delivery date:|配達予定日：) ?([^<]*)/i;
			deliveredString = /(Final delivery|お届け済み|お届け先にお届け済み|窓口でお渡し)/i;
			errorRegEx = /<td colspan="5">([^<]*)<\/td>/i;
			latestFirst = false;
			sortItemsByDate = false;

		} else if (site == 'LaPoste') {

			detailRegEx = /<li class="step"> ?<div[^>]*> ?<span>([^<]*)<\/span> ?(<span>([^<]*)<\/span>)? ?<\/div> ?<div[^>]*><div[^>]*> ?<\/div> ?<div[^>]*> ?<\/div> ?<\/div> ?<div[^>]*> ?<span[^>]*>([^<]*)<\/span> <a[^>]*>([^<]*)<\/a> ?<\/div> ?<\/li>/i;
			if (output.match(detailRegEx)) {
				// m.laposte.fr
				detailItemRegEx = /<li class="step"> ?<div[^>]*> ?<span>([^<]*)<\/span> ?(<span>([^<]*)<\/span>)? ?<\/div> ?<div[^>]*><div[^>]*> ?<\/div> ?<div[^>]*> ?<\/div> ?<\/div> ?<div[^>]*> ?<span[^>]*>([^<]*)<\/span> <a[^>]*>([^<]*)<\/a> ?<\/div> ?<\/li>/gi;
				detailDateNo = 1;
				detailStatusNo = 4;
				detailLocationNo = 5;
				deliverLocRegEx = /<p[^>]*> ?Destination ?: ?([^<]*)/i;
			} else {
				detailRegEx = /<tr> ?<td[^>]*> ?<p[^>]*>([^<]*)<\/p> ?<\/td> ?<td[^>]*> ?<p[^>]*>([^<]*)<\/p> ?<\/td> ?<td[^>]*> ?<p[^>]*>([^<]*)<\/p> ?<\/td> ?<\/tr>/i;
				if (output.match(detailRegEx)) {
					// www.laposte.fr
					detailItemRegEx = /<tr> ?<td[^>]*> ?<p[^>]*>([^<]*)<\/p> ?<\/td> ?<td[^>]*> ?<p[^>]*>([^<]*)<\/p> ?<\/td> ?<td[^>]*> ?<p[^>]*>([^<]*)<\/p> ?<\/td> ?<\/tr>/gi;
					detailDateNo = 1;
					detailStatusNo = 2;
					detailLocationNo = 3;
					deliverLocRegEx = /<li> ?<p> ?Destination ?: ?([^<]*)/i;
				} else {
					// www.csuivi.courrier.laposte.fr
					statusRegEx = /<tr> ?<td[^>]*>([^<]*)<img[^>]*> ?<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?(<td>([^<]*)<\/td> ?)?<td>([^<]*)<\/td> ?<\/tr>/i;
					detailRegEx = /<div class="ach_courrier_box"> ?<span[^>]*>([^<]*)<\/span> ?<span[^>]*>([^<]*)<\/span> ?<span[^>]*>([^<]*)/i;
					statusNo = 6;
					detailDateNo = 1;
					detailStatusNo = 2;
					detailLocationNo = 3;
				}
			}
			deliveredString = /^(Votre colis est livré|Votre colis a été livré|Livraison effectuée|Votre colis a été remis au gardien ou à l'accueil|Distribué|Delivered|Your parcel was delivered|Your parcel has been delivered)/i;
			errorRegEx = /<div[^>]*> ?<span class="icon-exclamation"> ?<\/span> ?<\/div> ?<div[^>]*> ?<p>([^<]*)<\/p> ?<\/div>/i;
			connectRegEx = /Nous rencontrons actuellement des problèmes techniques et vous prions de nous en excuser/i;

		} else if (site == 'LaPoste-Fallback') {

			detailRegEx = /<div[^>]*> ?<p> ?<b>[^<]*<br> ?<\/b> ?Date ?:([^<]*)<br>([^<]*)<br> ?<a[^>]*> ?Détails ?<\/a> ?<\/p> ?<\/div>/i;
			if (output.match(detailRegEx)) {
				detailDateNo = 1;
				detailStatusNo = 2;
			} else {
				// Formatting is different after delivery?
				detailRegEx = /<ul[^>]*> ?<li[^>]*> ?<ul[^>]*> ?<li> ?<p[^>]*> ?Date ?<\/p> ?<p>([^<]*)<\/p> ?<\/li> ?<li> ?<p[^>]*> ?Statut ?<\/p> ?<p[^>]*>([^<]*)<\/p> ?<\/li> ?<li> ?<p[^>]*> ?Localisation ?<\/p> ?<p>([^<]*)<\/p> ?<\/li> ?<\/ul>/i;
				detailDateNo = 1;
				detailStatusNo = 2;
				detailLocationNo = 3;
			}
			deliveredString = /Votre colis est livré|Votre colis a été livré|Livraison effectuée|Votre colis a été remis au gardien ou à l'accueil|Distribué/i;

		} else if (site == 'OnTrac') {

			statusRegEx = /Delivery Status:<\/b> ?<\/TD> ?<TD[^>]*>([^<]*)/i;
			detailItemRegEx = /<tr> ?<td[^<>]*>? ?<p>([^<]*)(<br \/> ?<\/p>)? ?<\/td> ?<td[^<>]*>? ?<p>([^<]*)(<br \/> ?<\/p>)? ?<\/td> ?<td[^<>]*>? ?<p>([^<]*)/gi;
			detailRegEx = /<tr> ?<td[^<>]*>? ?<p>([^<]*)(?:<br \/> ?<\/p>)? ?<\/td> ?<td[^<>]*>? ?<p>([^<]*)(?:<br \/> ?<\/p>)? ?<\/td> ?<td[^<>]*>? ?<p>([^<]*)/i;
			detailDateNo = 2;
			detailLocationNo = 3;
			detailStatusNo = 1;
			shipDateRegEx = /Ship Date:<\/b> ?<\/TD> ?<TD[^>]*>([^<]*)/i;
			deliverDateRegEx = /(?:Estimated Delivery Time|Service Commitment Time):<\/b> ?<\/TD> ?<TD[^>]*> ?([0-9\/]+)/i;
			deliverLocRegEx = /Deliver To:<\/b> ?<\/td> ?<td[^>]*>([^<]*)/i;
			deliverUpdatedRegEx = /<b>Delivery Time:<\/b> ?<\/TD> ?<TD[^>]*>(?! ?n\/a)([^<]*)/i;
			latestFirst = false;

		} else if (site == 'Parcelforce') {

			detailRegEx = /<tr[^>]*> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>(.*?)<\/td> ?<\/tr>/i;
			if (output.match(detailRegEx)) {
				detailItemRegEx = /<tr[^>]*> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>(.*?)<\/td> ?<\/tr>/gi;
				statusRegEx = /<dt[^>]*> ?Tracking status: ?<\/dt> ?<dd>([^<]*)<\/dd>/i;
				detailDateNo = 1;
				detailTimeNo = 2;
				detailLocationNo = 3;
				detailStatusNo = 4;
				deliverDateRegEx = /<dt[^>]*> ?Delivered on: ?<\/dt><dd>([^<]*)<\/dd>/i;
			} else {
				statusRegEx = /<h3 class="track-trace-rml-messages">([^<]*)/i;
				detailRegEx = /<h3 class="track-trace-rml-messages">[^<]*<\/h3> ?<p>([^<]*)/i;
				deliverDateRegEx = /was delivered [a-z\s]* on ([0-9/]*)/i;
				detailStatusNo = 1;
			}
			errorRegEx = /<h2> ?Please review the information below ?<\/h2> ?<ul> ?<li>([^<]*)/i;
			connectRegEx = /<p> ?(Our website is temporarily unavailable.|Because of very heavy site traffic, this page is currently unavailable.)/i;

		} else if (site == 'Post.at') {

			statusRegEx = /<h4 class="sendungsstatus-header">(.*?)<\/h4>/i;
			if (output.match(statusRegEx)) {
				detailItemRegEx = /<div class="media-body"> ?<em> ?<b> ?[A-Z:]* ?([0-9.]+)&nbsp;([0-9:]*) ?<\/b> ?<\/em> ?<br> ?<b>([^<]*)<\/b>/gi;
				detailRegEx = /<div class="media-body"> ?<em> ?<b> ?[A-Z:]* ?([0-9.]+)&nbsp;([0-9:]*) ?<\/b> ?<\/em> ?<br> ?<b>([^<]*)<\/b>/i;
				detailDateNo = 1;
				detailTimeNo = 2;
				detailStatusNo = 3;
				detailLocRegEx = /; ?([A-Z]* ?[0-9]+(; ?[A-Z]*)?) ?$/i;
				deliverDateRegEx = /<div[^>]*> ?(?:Voraussichtliche Zustellung|Voraussichtliche&nbsp;&shy;Zustellung|Expected delivery day): ?<\/div> ?<div[^>]*> ?(?:zugestellt am|delivered on)? ?[a-z\s]*, ([0-9.\/]*)/i;
				deliveredRegEx = /<li class="sendungsstatus-progress-item ([^"]*)"> ?<span[^>]*> ?<\/span> ?<span class="pi pi-icon_005[^"]*"[^>]*>/i;
				deliveredString = /^(active|complete)$/i;
			} else {
				detailRegEx = /(?:Sendungsstatus|Delivery status): ?([^<;]*)<br> ?(?:Datum|Date): ?([^<;]*) ?; ?(?:PLZ|Postcode): ?([^<;]*)/i;
				detailDateNo = 2;
				detailStatusNo = 1;
				detailLocationNo = 3;
				deliveredString = /^(übergeben|delivered)$/i;
			}
			connectRegEx = /<h4> ?Die Seite kann nicht angezeigt werden|<h3> ?DB Error/i; // This has probably changed

		} else if (site == 'Posten-no') {

			statusRegEx = /<div class="sporing-sendingandkolli-latestevent-text"> ?<strong>(.*?)<\/strong>/i;
			detailItemRegEx = /<tr> ?<td> ?<div class="sporing-sendingandkolli-latestevent-text">(.*?)<\/div>(.*?)<\/td> ?<td>([^<]*)<\/td> ?<td>([^<]*)<\/td> ?<\/tr>/gi;
			detailRegEx = /<tr> ?<td> ?<div class="sporing-sendingandkolli-latestevent-text">(.*?)<\/div>(.*?)<\/td> ?<td>([^<]*)<\/td> ?<td>([^<]*)<\/td> ?<\/tr>/i;
			detailDateNo = 3;
			detailLocationNo = 4;
			detailStatusNo = 1;
			deliverDateRegEx = /<span id="sporing-sendingandkolli--latestevent-deliverydate"> ?<strong>([^<]*)/i;
			deliveredString = /(Package has been delivered|The shipment has been delivered|Sendingen er utlevert|Sendingen er utlevert)/i;
			errorRegEx = /<span class="sporing-windowsize-warning-link">([^<]+)<\/span>/
			connectRegEx = /(<div class="sporing-error-text">|<span class="sporing-windowsize-warning-link">) ?(The service is too busy at the moment.|An error has occurred.|Tjenesten er ikke tilgjengelig for øyeblikket.|En feil har oppstått.|The result may be incomplete because one of our systems is temporarily unavailable.|Resultatet kan være ufullstendig siden et av våre systemer er utilgjengelig for øyeblikket.)/i;

		} else if (site == 'Purolator') {

			statusRegEx = /"status" ?: ?'([^']*)'/i;
			detailItemRegEx = /\[ ?'([^']*)', ?'([^']*)', ?'([^']*)', ?'([^']*)' ?\]/gi;
			detailRegEx = /\[ ?'([^']*)', ?'([^']*)', ?'([^']*)', ?'([^']*)' ?\]/i;
			detailDateNo = 1;
			detailLocationNo = 3;
			detailStatusNo = 4;
			deliverDateRegEx = /"deliverBy" ?: ?"([^"]*)"/i;
			deliveredString = /^Delivered/;
			connectRegEx = /addFeedbackError\("#feedbackTrackDetails","error\.service\.1031990052"\)/i;

		} else if (site == 'Sagawa') {

			output = decodeDecimalEntities(output);

			detailItemRegEx = /<tr> ?<td> ?[⇒↓]([^<]*)<\/td> ?<td>([^<]*)<\/td> ?<td>([^<]*)<\/td> ?<\/tr>/gi;
			detailRegEx = /<tr> ?<td> ?[⇒↓]([^<]*)<\/td> ?<td>([^<]*)<\/td> ?<td>([^<]*)<\/td> ?<\/tr>/i;
			detailDateNo = 2;
			detailStatusNo = 1;
			detailLocationNo = 3;
			deliverDateRegEx = /<span[^>]*> ?(?:配達完了日|お届け予定) ?<\/span> ?<\/th> ?<th[^>]*>[^<]*<\/th> ?<\/tr> ?<tr> ?<td[^>]*>([^<]*)<\/td>/i;
			deliverUpdatedRegEx = /<span[^>]*>[^<]*<\/span> ?<\/th> ?<th[^>]*> ?(?:再配達希望日時) ?<\/th> ?<\/tr> ?<tr> ?<td[^>]*>[^<]*(?:<div[^>]*> ?― ?<\/div>)?[^<]*<\/td> ?<td[^>]*>([^<]*)<\/td>/i;
			deliveredString = /(配達完了)/i; // 配達は終了致しました。|配達終了
			errorRegEx = /<tr> ?<th>詳細表示<\/th> ?<td>([^<]*)<\/td> ?<\/tr>/i;
			latestFirst = false;

		} else if (site == 'SwissPost') {

			detailItemRegEx = /<tr[^>]*> ?<td[^>]*>(<span[^>]*>[^<]*<\/span>)?([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td class="event_event"> ?(<span[^>]*>[^<]*<\/span>)?(.*?) ?(<img[^>]*> ?)?<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<\/tr>/gi;
			detailRegEx = /<tr[^>]*> ?<td[^>]*>(<span[^>]*>[^<]*<\/span>)?([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td class="event_event"> ?(<span[^>]*>[^<]*<\/span>)?(.*?) ?(<img[^>]*> ?)?<\/td> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*>([^<]*)<\/td> ?<\/tr>/i;
			statusNo = 5;
			detailDateNo = 2;
			detailLocationNo = 7;
			detailLocationNoExtra = 8;
			detailStatusNo = 5;
			deliverDateRegEx = /<dt>(?:Predicted delivery date|Voraussichtlicher Zustelltag|Jour de distribution prévu|Giorno di recapito previsto):<\/dt> ?<dd>([^<]*)<\/dd>/i;
			deliveredString = /^(Delivered|Zugestellt|Distribué|Distribuito|Recapitato)/i;
			latestFirst = false;

		} else if (site == 'TNT Express') {

			detailItemRegEx = /<tr[^>]*> ?<td>([^<]*)<\/td> ?<td>([^<]*)<\/td> ?<td>([^<]*)<\/td> ?<td>([^<]*)<\/td>/gi;
			detailRegEx = /<tr[^>]*> ?<td>([^<]*)<\/td> ?<td>([^<]*)<\/td> ?<td>([^<]*)<\/td> ?<td>([^<]*)<\/td>/i;
			detailDateNo = 2;
			// detailTimeNo = 3; // Sometimes times are missing so don't use them for sorting
			detailLocationNo = 4;
			detailStatusNo = 1;
			deliverDateRegEx = /<td class='strong'>Estimated Delivery by:([^<]*)/i;
			deliveredString = /(^Delivered|^Dlvrd|^We've delivered your shipment|^Proof of Delivery Image$|Agent Collected \(Full\)$)/i;
			ignoreDetailRegEx = /^Closed for Holidays$/i;
			latestFirst = false;

		} else if (site == 'USPS') {

			detailRegEx = /<(?:TrackSummary|TrackDetail)>(<EventTime>([^<]*)<\/EventTime>|<EventTime\/>)(<EventDate>([^<]*)<\/EventDate>|<EventDate\/>)<Event>([^<]*)<\/Event>((?:<EventCity>|<EventCity\/>).*?(?:<EventCountry\/>|<\/EventCountry>))/i;
			if (output.match(detailRegEx)) {
				detailItemRegEx = /<(TrackSummary|TrackDetail)>(.*?)<\/\1>/gi;
				detailDateNo = 4;
				detailLocationNo = 6;
				detailStatusNo = 5;
				deliverDateRegEx = /<(?:DeliveryNotificationDate|ExpectedDeliveryDate|GuaranteedDeliveryDate)>([^<]*)/i;
				deliverUpdatedRegEx = /<(?:PredictedDeliveryDate)>([^<]*)/i;
				deliveredRegEx = /<StatusCategory>([^<]*)/i;
				if (output.match(deliveredRegEx)) {
					deliveredString = /^(Delivered|Delivered to Agent)$/i;
				} else {
					// For older clients
					deliveredRegEx = null;
					deliveredString = /^Delivered/i;
				}
				errorRegEx = /<Error><Number>[^<]*<\/Number>(?:<Source>[^<]*<\/Source>)?<Description>([^<]*)<\/Description>/i;
				sortItemsByDate = false;
			} else {
				detailRegEx = /<span> ?<strong>([^<]*)<\/strong> ?<\/span> ?<br \/> ?(?:<span>)?([^<]*)<\/span> ?<br \/> ?(?:(?:<span>)?([^<]*)<\/span> ?<br[^>]*>)? ?(?:<span>([^<]*)<\/span> ?<br[^>]*>)? ?<hr[^>]*>/i;
				detailDateNo = 1;
				detailLocationNo = 3;
				detailStatusNo = 2;
				deliverDateRegEx = /<span class="eta_wrap"> ?<span[^>]*> <em[^>]*>[^<]*<\/em> (<strong[^>]*>[^<]*<\/strong> <span[^>]*> <span>[^<]*<\/span> [^<]*)/i;
				deliveredRegEx = /<div class="product_summary delivery_([a-z]*)">/i;
				errorRegEx = /(?:<h2> <strong>[^<]*<\/strong> <p[^>]*>|<Error><Number>[^<]*<\/Number>(?:<Source>[^<]*<\/Source>)?<Description>)([^<]*)/i;
				connectRegEx = /(To learn about integrating the free Postal Service&reg; Address and Tracking API's into your application, please visit|This service is currently unavailable|USPS.com is temporarily unavailable.|Sorry, the tracking system is having technical difficulties. Please try your search again later.)/i;
			}

		} else if (site == 'UPS') {

			statusRegEx = /<div id="(?:ttc_tt_spStatus|ttc_tt_pgfStatus)"[^>]*> ?(<!--.*?-->)? ?<h3>([^<]*)/i;
			// The ending </tr> is important to make sure flight information isn't matched
			// The optional </td> is for UPS Mail Innovations
			detailItemRegEx = /<tr[^>]*> ?<td class="nowrap">(.*?)<\/tr>/gi;
			// This checks for a date to ignore Flight Information on Freight shipments
			detailRegEx = /<tr[^>]*> ?<td[^>]*>([^<]*)<\/td> ?<td[^>]*> ?([0-9]{2}\/[0-9]{2}\/[0-9]{4}) ?<\/td> ?(?:<td[^>]*>([^<]*)<\/td> ?)?<td[^<]*>(.*?)(<\/td>)? ?(<td[^<]*>[^<]*<\/td>)? ?<\/tr>/i;
			statusNo = 2;
			detailDateNo = 2;
			detailLocationNo = 1;
			detailStatusNo = 4;
			shipDateRegEx = /Shipped&#047;Billed On: ?<\/strong> ?<\/div> ?<div[^>]*>([^<]*)/i;
			deliverDateRegEx = /(?:Scheduled Delivery|Schedule Delivery|Scheduled Delivery Updated To|Schedule Delivery Updated To|Scheduled For Early Delivery On|Delivered On|Next Delivery Attempt|Estimated Delivery|Estimated Arrival): ?<\/strong> ?<\/p> ?<p[^>]*> ?(?:[a-z]*, ?&nbsp; ?)?([0-9\/]+)/i;
			deliverLocRegEx = /To: ?<\/strong> ?<\/p> ?<div[^>]*> ?<address> ?<p>([^<]*)/i;
			deliverLocExtraRegEx = /Left At: ?<\/strong> ?<\/p> ?<p[^>]*> ?([^<]*)/i;
			deliveredTodayString = /^(On Vehicle for Delivery Today)$/i;
			deliveredString = /(^Delivered$|^(Package )?delivered by (local )?post office$|^Delivered to UPS Access Point)/i;
			connectRegEx = /(<span class="error">Not Available<\/span>|<p class="error">&gt;&gt;&gt;&nbsp;Not all tracking information is available at this time\. Please try again later\.<br><\/p>|<!-- SearchBodyStart --> ?<!-- SearchBodyEnd -->)/i;
			sortItemsByDate = false;

		} else if (site == 'UPS-MI') {

			detailRegEx = /<tr> ?<td class="LeftD_BottomL"[^>]*>(.*?)<\/td> ?<td class="LeftL_Bottom[^"]*"[^>]*>(.*?)<\/td> ?<td class="LeftL_Bottom[^"]*"[^>]*>(.*?)<\/td> ?<\/tr>/i;
			detailItemRegEx = /<tr> ?<td class="LeftD_BottomL"[^>]*>(.*?)<\/td> ?<td class="LeftL_Bottom[^"]*"[^>]*>(.*?)<\/td> ?<td class="LeftL_Bottom[^"]*"[^>]*>(.*?)<\/td> ?<\/tr>/g;
			statusNo = 2;
			detailDateNo = 1;
			detailLocationNo = 3;
			detailStatusNo = 2;
			shipDateRegEx = /<tr> ?<td[^>]*>[^<]*<\/td> ?<td[^>]*>(Mail Retrieved From Shipper|Package received for processing)/i;
			deliverDateRegEx = /<span id="[a-z0-9_]*ProjDelDate">([^<]*)/i;

		} else if (site == 'Yamato') {

			statusRegEx = /<table class="saisin">.*?<td class="font14"> ?(?!伝票番号未登録)([^<]*)/i;
			detailItemRegEx = /<tr[^>]*> ?(<td[^>]* rowspan="2"> ?[0-9]+ ?<\/td>)? ?<td[^>]*> ?<(img|image)[^>]*> ?<\/td> ?<td[^>]*>([^<]*)(<br>)?<\/td> ?<td[^>]*>([^<]*)(<br>)?<\/td> ?<td[^>]*>([^<]*)(<br>)?<\/td> ?<td[^>]*>(<a[^>]*>)?([^<]*)(<\/a>)?(<br>)?<\/td> ?<td[^>]*>([^<]*)(<br>)?<\/td> ?(<td[^>]*>(<br>)?<\/td>)? ?(<td[^>]* rowspan="2">[^<]*<\/td>)? ?<\/tr>/gi;
			detailRegEx = /<tr[^>]*> ?(<td[^>]* rowspan="2"> ?[0-9]+ ?<\/td>)? ?<td[^>]*> ?<(img|image)[^>]*> ?<\/td> ?<td[^>]*>([^<]*)(<br>)?<\/td> ?<td[^>]*>([^<]*)(<br>)?<\/td> ?<td[^>]*>([^<]*)(<br>)?<\/td> ?<td[^>]*>(<a[^>]*>)?([^<]*)(<\/a>)?(<br>)?<\/td> ?<td[^>]*>([^<]*)(<br>)?<\/td> ?(<td[^>]*>(<br>)?<\/td>)? ?(<td[^>]* rowspan="2">[^<]*<\/td>)? ?<\/tr>/i;
			detailDateNo = 5;
			detailLocationNo = 10;
			detailStatusNo = 3;
			deliverDateRegEx = /(?:お届け予定日時 ?<\/th> ?(?:<th[^>]*> ?お届け希望日時 ?<\/th> ?)?<\/tr> ?<tr> ?<td[^>]*>[^<]*<br> ?<\/td> ?<td[^>]*>|<tr> ?<td>[^<]*<\/td> ?<td>[^<]*(?:<br>)?<\/td> ?<td>[^<]*(?:<br>)?<\/td> ?<td>(?:<a[^>]*>)?[^<]*(?:<\/a>)?<\/td> ?<td>[^<]*<\/td> ?<td>)([^<]*)/i;
			deliveredString = /^(配達完了|投函完了)/i;
			errorRegEx = /<table class="saisin">.*?<td class="font14">([^<]*)/i;
			latestFirst = false;
			useDetailDateWhenDelivered = true;

		} else if (site == 'Yodel') {

			statusRegEx = /<h3 class="sub-header"> ?Status:([^<]*)/i;
			detailItemRegEx = /<p class="tracking_event"> ?<span[^>]*> ?([0-9\/]*) ?([0-9:]*) ?<\/span> ?<span[^>]*>([^<]*)<\/span> ?<span[^>]*> ?\(?([^<\)]*)\)? ?<\/span> ?<\/p>/gi;
			detailRegEx = /<p class="tracking_event"> ?<span[^>]*> ?([0-9\/]*) ?([0-9:]*) ?<\/span> ?<span[^>]*>([^<]*)<\/span> ?<span[^>]*> ?\(?([^<\)]*)\)? ?<\/span> ?<\/p>/i;
			detailDateNo = 1;
			detailTimeNo = 2;
			detailLocationNo = 4;
			detailStatusNo = 3;
			deliverDateRegEx = /<h3[^>]*> ?Delivery Estimate ?<\/h3> ?<p>([^<]*)/i;
			errorRegEx = /<div class="error-msg error-msg--general">([^<]*)/i;
			useDetailDateWhenDelivered = true;

		}

		if (!isChild) {
			var item = resetItem();
			if (delivery.name) {
				item.title = htmlEntities(delivery.name);
			} else if (delivery.no) {
				item.title = htmlEntities(delivery.no);
			} else if (delivery.domain) {
				var title = delivery.domain.replace(/^(www|m)\./i,'');
				item.title = title;
			}
			delivery.items = [];
			delivery.items[childNo] = item;
		}

		// If checkForNo is set, and the details are found, but not the number, force an error
		if (checkForNo && output.match(detailRegEx)) {
			var noMatch = new RegExp(delivery.no.replace(/ /g,' ?'),'i');
			if (!output.match(noMatch)) output = '';
		}

		// Get the ship date
		if (shipDateRegEx) {
			result = output.match(shipDateRegEx);
			if (result && result[1]) shipDate = result[1];
			if (shipDate == '--') shipDate = false;
		}

		// Get the delivery date  
		result = output.match(deliverDateRegEx);
		if (!result && altDeliverDateRegEx) {
			result = output.match(altDeliverDateRegEx);
		}
		if (deliverUpdatedRegEx) {
			thisResult = output.match(deliverUpdatedRegEx);
			if (thisResult) result = thisResult;
		}
		if (result && result[1]) {
			deliverDate = stripHTML(result[1]);
			if (deliverDate == '--') deliverDate = false;
			if (deliverDate && splitDateRanges) deliverDate = endDateInRange(deliverDate);
		}

		function getDetails (input) {
			var detailItem = false;
			var result;
			if (input) result = input.match(detailRegEx);
			if (result) {
				detailItem = {};
				var detailResult = result[detailStatusNo];
				if (site == 'Posten-no') detailResult = detailResult.replace(/\&lt;/g,'<');
				detailItem.status = clearEmptyString(cleanUpStatus(detailResult,languageFrom));

				if (detailStatusNoExtra >= 0) {
					var extra = trim(result[detailStatusNoExtra]);
					if (extra) detailItem.status += ': '+result[detailStatusNoExtra];
				}
				if (detailDateNo) {
					detailItem.date = result[detailDateNo];
					if (detailTimeNo && result[detailTimeNo]) detailItem.date += ' '+result[detailTimeNo];
				} else if (detailDateRegEx && (dateResult = detailItem.status.match(detailDateRegEx))) {
					detailItem.date = dateResult[1];
				}

				if (detailLocationNo) {
					detailItem.location = result[detailLocationNo];
					if (detailLocationNoExtra >= 0 && result[detailLocationNoExtra]) {
						detailItem.location = (detailItem.location) ? 
							detailItem.location+' '+result[detailLocationNoExtra] : result[detailLocationNoExtra];
					}
				} else if (detailLocRegEx && (locationResult = detailItem.status.match(detailLocRegEx))) {
					detailItem.location = locationResult[1];
				}
				detailItem.location = clearEmptyString(detailItem.location); // Also takes care of undefined
				if (ignoreDetailRegEx && detailItem.status.match(ignoreDetailRegEx)) detailItem = false;
			}
			return detailItem;
		}

		// Find all the items in the details list, or just get the current details
		if (detailItemRegEx && (result = output.match(detailItemRegEx))) {
			var detailItems = [];
			for (x in result) {
				var detailItem = getDetails(result[x]);
				if (detailItem) {
					var dateResult = dateFromString(cleanUpDate(detailItem.date,true,languageFrom,false,false));
					if (dateResult) detailItem.dateStamp = dateResult.getTime();
					detailItem.index = x;
					detailItems.push(detailItem);
				}
			}
			if (sortItemsByDate || !latestFirst) {
				detailItems.sort(function(a,b){
					// Most recent first, or preserve the order if the dates match
					var difference = (sortItemsByDate) ? b.dateStamp - a.dateStamp : 0;
					if (difference == 0) difference = (latestFirst) ? a.index - b.index : b.index - a.index;
					return difference;
				});
			}
			var route = [];
			for (var x = 0; x < detailItems.length; x++) {
				var location = detailItems[x].location;
				if (location) route.push(cleanLocation(location,languageFrom));
			}
			details = detailItems[0];
			details.route = route;
		} else {
			details = (detailSection) ? getDetails(detailSection) : getDetails(output);
		}

		// Get the current status if it's separate, or use the detail status
		if (statusRegEx) {
			if (result = output.match(statusRegEx)) {
				if (result[statusNo]) {
					foundStatus = true;
					status = result[statusNo];
				}			
				status = clearEmptyString(cleanUpStatus(status,languageFrom));
				if (ignoreStatusRegEx && status.match(ignoreStatusRegEx)) status = null;
			}
		} else if (details) {
			status = details.status;
		}

		if (!status && details) status = details.status;
		if (deliveredRegEx) {
			thisResult = output.match(deliveredRegEx);
			if (thisResult && thisResult[1] && thisResult[1].match(deliveredString)) status = 'Complete';
		} else if (status && status.match(deliveredString)) {
			status = 'Complete';
		}
		var statusComplete = (status == 'Complete');

		if (deliveredTodayString && status && status.match(deliveredTodayString)) {
			var date = new Date();
			deliverDate = (date.getMonth()+1)+'/'+(date.getDate())+'/'+(date.getFullYear());
		}

		if (details) {
			if (statusComplete) {
				if (!deliverDate || useDetailDateWhenDelivered) deliverDate = details.date;
				if (deliverLocRegEx) {
					result = output.match(deliverLocRegEx);
					if (result && result[1]) details.location = result[1];
				}
				// If there's additional location info (front door), add it to the status
				if (deliverLocExtraRegEx) {
					result = output.match(deliverLocExtraRegEx);
					if (result && result[1]) details.status += ': '+result[1];
				}
			}
		}

		if (details.date) details.date = cleanUpDate(details.date,false,languageFrom,false,false);
		details.mapLocation = (details.location) ? cleanLocation(details.location,languageFrom) : '';

		if (delivery.items[childNo]) {
			if (status) delivery.items[childNo].status = status;
			if (details) delivery.items[childNo].details = details;
			if (shipDate) delivery.items[childNo].shipSaved = cleanUpDate(shipDate,false,languageFrom,false,false);
			if (deliverDate) delivery.items[childNo].deliverSaved = cleanUpDate(deliverDate,false,languageFrom,false,(!statusComplete));
			if (delivery.useItemCache) delivery.items[childNo].url = this.url;
		}

		if (isChild) {
			if (delivery.usedItemCache && !foundStatus && !shipDate && !deliverDate && !details) {
				logRequest(1,this);
				logMessage(1,'Cached URL failed to load: '+this.url);
				delivery.clearItemCache = true;
			} else if (delivery.usedItemCache && details.status && !this.url.match(/packageIndex=[0-9]+/)) {
				logMessage(1,'Cached URL needs to be updated: '+this.url);
				delivery.clearItemCache = true;
			} else if (retryOnError && !delivery.retry && this.args.service) {
				this.args.service.retry = true;
				delivery.startUpdate(this.args.service,childNo);
			} else if (connectRegEx && output.match(connectRegEx)) {
				if (delivery.previousItems) delivery.items = delivery.previousItems;
				status = details = shipDate = deliverDate = false;
				delivery.notFound();
			}
		} else {
			if (!foundStatus && !shipDate && !deliverDate && !details) {
				logRequest(1,this);
				if ((skipExtraOnError && this.args.extra) || (retryOnError && !delivery.retry)) {
					if (retryOnError) delivery.retry = true;
					if (skipExtraOnError) delivery.skipExtra = true;
					delivery.startUpdate(delivery,false);
				} else if ((connectRegEx && output.match(connectRegEx)) || (checkForNo && !output)) {
					if (!delivery.count) {
						delivery.count = -1;
					} else delivery.items = delivery.previousItems;
					delivery.notFound();
				/* } else if (captchaRegEx && output.match(captchaRegEx)) {
					manualSignIn[delivery.from+'-'+delivery.extra] = true;
					delivery.count = 0;
					delivery.notFound(); */
				} else if (notSupportedOnError) {
					delivery.notSupported = notSupported;
					delivery.notSupported();
				} else {
					delivery.tries = 0;
					delivery.count = 0;
					if (errorRegEx) {
						result = output.match(errorRegEx);
						if (result) errorText = cleanUpStatus(result[1],languageFrom);
					}
					delivery.notFound(errorText);
				}
			} else delivery.count = 1;
		}

	} else if (isChild) {

		logRequest(1,this);
		if (delivery.usedItemCache) {
			logMessage(1,'Cached URL failed to load');
			delivery.clearItemCache = true;
		} if (!this.responseText) {
			if (delivery.previousItems) delivery.items = delivery.previousItems;
			delivery.notFound();
		}

	} else {

		logRequest(1,this);
		var ignore500 = false;
		var connectRegEx = '';
		switch (site) {
			case 'UPS':
				ignore500 = true; // Also used for invalid tracking numbers
				connectRegEx = /(<span class="error">Not Available<\/span>|<p class="error">&gt;&gt;&gt;&nbsp;Not all tracking information is available at this time\. Please try again later\.<br><\/p>|<!-- SearchBodyStart --> ?<!-- SearchBodyEnd -->)/i;
				break;
			case 'Posten-no':
				connectRegEx = /<h1>Feilmelding: Feil i underliggende system<\/h1>/i;
				break;
		}
		if (this.status >= 500 && !ignore500) {
			if (!delivery.count) delivery.count = -1;
		} else if (connectRegEx && this.responseText) {
			output = stripWhiteSpace(this.responseText);
			if (output.match(connectRegEx)) {
				if (!delivery.count) delivery.count = -1;
			} else delivery.count = 0;
		} else if (!delivery.count) {
			delivery.count = -1;
		}
		delivery.notFound();

	}

	delivery.reqComplete++;
	delivery.updateItems();
	return;

}

function retryTracker () {
	var delivery = this.delivery;
	var onComplete = getTracker;
	delivery.loadPage(delivery.url,onComplete,false,this.args);
	delivery.reqComplete++;
	delivery.updateItems();
	return;
}

function preferredLocale() {
	var lang = preferredLanguage.apply(this,arguments);
	var locale = 'en_US';
	switch (lang) {
		case 'en': locale = 'en_US'; break;
		case 'da': locale = 'da_DK'; break;
		case 'de': locale = 'de_DE'; break;
		case 'fr': locale = 'fr_FR'; break;
		case 'it': locale = 'it_IT'; break;
		case 'ja': locale = 'ja_JP'; break;
		case 'nb':
		case 'no': locale = 'no_NO'; break;
		case 'nl': locale = 'nl_NL'; break;
		case 'sv': locale = 'sv_SE'; break;
	}
	return locale;
}

function getLanguageFrom (defaultFrom, output) {
	var languageFrom = defaultFrom;
	result = output.match(/<header class='[^']*nav-lang-([a-z]*)[^']*'>/i);
	if (result) {
		switch (result[1]) {
			case 'en': languageFrom = 'www.amazon.com'; break;
			case 'ja': languageFrom = 'www.amazon.co.jp'; break;
			case 'de': languageFrom = 'www.amazon.de'; break;
			case 'es': languageFrom = 'www.amazon.es'; break;
			case 'fr': languageFrom = 'www.amazon.fr'; break;
			case 'it': languageFrom = 'www.amazon.it'; break;
		}
		if (defaultFrom != languageFrom) logMessage(1,'Language detected: '+result[1]);
	}
	return languageFrom;
}

function cleanUpStatus (string,from) {
	if (typeof string == 'string') {
		switch (from) {
			case 'www.amazon.com':
			case 'www.amazon.com/url':
			case 'www.amazon.ca':
			case 'www.amazon.co.uk':
			case 'www.amazon.co.jp':
			case 'www.amazon.de':
			case 'www.amazon.at':
			case 'www.amazon.es':
			case 'www.amazon.fr':
			case 'www.amazon.in':
			case 'www.amazon.it':
				string = string.replace(/\(\)\s*$/,'');
				break;
			case 'www.usps.com':
				string = removeHtmlEntities(string);
				break;
		}
		string = stripHTML(string);
	}
	return string;
}

function cleanUpDate (input,includeTime,from,referenceDate,probablyFuture) {

	// Note: referenceDate is not currently used

	var output;
	if (input) {

		input = input.toString();

		// ISO 8601 dates pass through
		var regEx = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(:[0-9]{2})?([.,][0-9]+)?(Z|\+[0-9]{2}:[0-9]{2})?$/;
		if (regEx.test(input)) return input;

		if (!from) from = this.from;
		var year = new Date().getFullYear();

		// Either clean up the time or remove it
		regEx = /(, |,? by |,? bis |,? avant )?([0-9]{2}:[0-9]{2}:[0-9]{2}|[0-9]{1,2}:[0-9]{2} ?(a\.m\.\/p\.m\.|A\.?M\.?|P\.?M\.?|Uhr|Hours|h)?|[0-9]{1,2} ?(am|pm)|[0-9]{1,2}h(?:[0-9]{2})?|[0-9]{1,2}時[0-9]{1,2}分)/i;
		if (includeTime) {
			input = input.replace(regEx,' $2');
			input = input.replace(/([0-9]{1,2}:[0-9]{2})([a-z])/i,'$1 $2');
			input = input.replace(/([0-9]{1,2}:[0-9]{2}) ?(a\.m\.\/p\.m\.|Uhr|Hours|h)/i,'$1');
			input = input.replace(/([0-9]{1,2})時([0-9]{1,2})分/i,'$1:$2');
		} else {
			input = input.replace(regEx,'');
		}

		while (input.indexOf('&nbsp;') != -1) input = input.replace('&nbsp;',' ');
		input = trim(input);
		output = input;

		// Add the year if it's missing, needed for Apple store shipping estimates
		var regEx = /^[a-z]* [0-9]{1,2}$/i;
		if (regEx.test(output)) output = appendYearToDate(output+', ',probablyFuture);

		// Fix international dates (YYYY-MM-DD)
		output = output.replace(/([0-9]{4})-([0-9]{2})-([0-9]{2})/i,'$2/$3/$1');

		switch (from) {
			case 'www.amazon.com':
			case 'www.amazon.com/url':
			case 'www.amazon.ca':
			case 'www.amazon.co.uk':
			case 'www.amazon.in':
				output = output.replace(/, On time ?$/i,'');
				var regEx = /^[a-z]*, ([a-z]+ [0-9]+)$/i;
				if (regEx.test(output)) { // Remove day of week, add year
					output = output.replace(regEx,'$1, ');
					output = appendYearToDate(output,probablyFuture);
				} else {
					var regEx = /^([a-z]*,)? ?([0-9]+ [a-z]+)$/i;
					if (regEx.test(output)) { // Remove day of week if present, add year for Amazon.co.uk and Amazon.in
						output = output.replace(regEx,'$2, ');
						output = appendYearToDate(output,probablyFuture);
					}
				}
				if (possibleWeekDay(output)) output = dateFromWeekDay(output,'en',referenceDate);
				break;
			case 'www.amazon.co.jp':
			case 'www.sagawa-exp.co.jp':
				// Note that these all remove the time
				var regEx = /^([0-9]+)月([0-9]+)日(.*)$/i;
				if (regEx.test(output)) { // Add year
					output = output.replace(regEx,'$1/$2/');
					output = appendYearToDate(output,probablyFuture);
				} else {
					var regEx = /^([0-9]+)\/([0-9]+)(\s.*|$)/i;
					if (regEx.test(output)) { // Add year
						output = output.replace(regEx,'$1/$2/');
						output = appendYearToDate(output,probablyFuture);
					} else {
						output = output.replace(/([0-9]+)年([0-9]+)月([0-9]+)日(.*)/i,'$2/$3/$1');
					}
				}
				break;
			case 'www.amazon.de':
			case 'www.amazon.at':
				output = flexibleInternationalDate(output);
				output = cleanGermanDate(output);
				output = output.replace(/[a-z]*, ([0-9]+\.? [a-z]+ [0-9]+)/i,'$1'); // Remove day of week
				var regEx = /([a-z]*\.?,)? ?([0-9]+\.? [a-z]+\.?) ?$/i;
				if (regEx.test(output)) { // Remove day of week if present, add year
					output = output.replace(regEx,'$2, ');
					output = appendYearToDate(output,probablyFuture);
				}
				if (possibleWeekDay(output)) output = dateFromWeekDay(output,'de',referenceDate);
				break;
			case 'www.amazon.es':
				output = flexibleInternationalDate(output);
				output = cleanSpanishDate(output);
				var regEx = /^([0-9]+ [a-z]+)$/i;
				if (regEx.test(output)) { // Add year
					output = output.replace(regEx,'$1, ');
					output = appendYearToDate(output,probablyFuture);
				}
				if (possibleWeekDay(output)) output = dateFromWeekDay(output,'es',referenceDate);
				break;
			case 'www.amazon.fr':
				output = cleanFrenchDate(output);
				var regEx = /^([a-z]*\.?)? ?([0-9]+ [a-z]+) ?$/i;
				if (regEx.test(output)) { // Remove day of week if present, add year
					output = output.replace(regEx,'$2, ');
					output = appendYearToDate(output,probablyFuture);
				}
				if (possibleWeekDay(output)) output = dateFromWeekDay(output,'fr',referenceDate);
				break;
			case 'www.amazon.it':
				output = cleanItalianDate(output);
				output = flexibleInternationalDate(output);
				var regEx = /^([a-z\u00E0-\u00FC']*\.?)? ?([0-9]+ [a-z]+) ?$/i;
				if (regEx.test(output)) { // Remove day of week if present, add year
					output = output.replace(regEx,'$2, ');
					output = appendYearToDate(output,probablyFuture);
				}
				if (possibleWeekDay(output)) output = dateFromWeekDay(output,'it',referenceDate);
				break;
			case 'www.apple.com':
			case 'www.apple.com/japan':
			case 'www.apple.com/hk':
				var replaced = null;
				if (replaced = replacedMatch(output,/^(Sun|Mon|Tue|Wed|Thu|Fri|Sat) ([a-z]*) ([0-9]{1,2})/i,'$1 $2 $3, ')) {
					output = appendYearToDate(replaced,probablyFuture);
				} else if (replaced = replacedMatch(output,/([a-z]+) ([0-9]{1,2}) by [0-9]+ Business Days/i,'$1 $2, ')) {
					output = appendYearToDate(replaced,probablyFuture);
				} else if (replaced = replacedMatch(output,/^(?:by )?([a-z]+) ([0-9]{1,2})[a-z]{2}$/i,'$1 $2, ')) {
					output = appendYearToDate(replaced,probablyFuture);
				} else if (replaced = replacedMatch(output,/^([0-9]{1,2}) ([a-z]+)$/i,'$2 $1, ')) {
					output = appendYearToDate(replaced,probablyFuture);
				} else if (replaced = replacedMatch(output,/^([0-9]{1,2})\/([0-9]{1,2})$/i,'$1/$2/')) {
					output = appendYearToDate(replaced,probablyFuture);
				} else if (output.match(/^(January|February|March|April|May|June|July|August|September|October|November|December)$/i)) {
					var thisDate = new Date(output+'1, '+year);
					thisDate = new Date(thisDate.getFullYear(),thisDate.getMonth()+1,0);
					output = thisDate.toString();
				}
				if (from == 'www.apple.com/hk') output = output.replace(/^([0-9]+)年([0-9]+)月([0-9]+)日$/i,'$2/$3/$1');
				break;
			case 'www.dpd.com':
			case 'www.hlg.de':
				output = flexibleInternationalDate(output);
				break;
			case 'www.chronopost.fr':
				if (translate('xx') == 'fr') output = flexibleInternationalDate(output); // French desktop
				var regEx = /^[a-z]* ([0-9]{1,2} [a-z]*)$/i;
				if (regEx.test(output)) { // French mobile
					output = output.replace(regEx,'$1,'+year);
					output = cleanFrenchDate(output);
				} else { // English mobile
					output = output.replace(/^[a-z]*, ([a-z]* [0-9]{1,2})$/i,'$1,'+year);
				}
				break;
			case 'www.laposte.fr':
				if (translate('xx') == 'fr') output = flexibleInternationalDate(output);
				if (output.match(/^[0-9]{1,2} [a-zéû]+$/i)) {
					output = cleanFrenchDate(output);
					output = appendYearToDate(output,probablyFuture);
				}
				break;
			case 'www.tntexpress.com.au':
				output = flexibleInternationalDate(output);
				output = output.replace(/([0-9]{1,2})[a-z]+ ([a-z]+)/i,'$2 $1, '+year);
				break;
			case 'www.dhl.com':
				if (output.match(/^Today/i)) output = new Date().toDateString();
				break;
			case 'nolp.dhl.de':
				output = trim(stripHTML(output));
				output = output.replace(/^([a-z]+\.,)/i,'');
				if (output.match(/^[a-zä]+ [0-9]{1,2}$/i)) {
					output = cleanGermanDate(output)+' '+year;
				} else output = flexibleInternationalDate(output);
				break;
			case 'www.fedex.com':
				// Note: this is sometimes garbled by the time cleanup above, and/or fix international dates: 
				// 2017-07-18T00:00:00-06:00 becomes 2017-07-18T-06:00 or 07/18/2017T-06:00
				output = output.replace(/^([0-9]{4})-([0-9]{2})-([0-9]{2})(T.*)?$/,'$2/$3/$1');
				output = output.replace(/^([0-9]{2})\/([0-9]{2})\/([0-9]{4})(T.*)?$/,'$1/$2/$3');
				break;
			case 'www.royalmail.com':
			case 'www.parcelforce.com':
			case 'www.posten.no':
				// Could be DD/MM/YY (RoyalMail, Parcelforce), DD.MM.YYYY (Posten), or YYYY-MM-DD. The latter was fixed above.
				if (input == output) output = flexibleInternationalDate(output);
				break;
			case 'www.japanpost.jp/ems':
			case 'www.japanpost.jp/m10':
			case 'www.japanpost.jp/reg':
			case 'www.japanpost.jp/yupack':
				output = output.replace(/([0-9]{1,2})月([0-9]{1,2})日/i,'$1/$2/'+year);
				break;
			case 'www.post.at':
				output = flexibleInternationalDate(output);
				break;
			case 'www.post.ch':
				output = output.replace(/^&ndash;$/,'');
				output = output.replace(/([0-9]{2})\.([0-9]{2})\.([0-9]{4})/i,'$2/$1/$3');
				break;
			case 'www.kuronekoyamato.co.jp':
				if (output == '-') output = '';
				output = output.replace(/^([0-9]{1,2}\/[0-9]{1,2})(\s.*|$)/i,'$1/'+year);
				break;
			case 'www.yodel.co.uk':
				if (output.match(/^(Delivered|Unavailable)$/i)) output = '';
				output = output.replace(/^On or before /i,'');
				if (output.match(/^[a-z]+$/i)) {
					output = dateFromWeekDay(output,'en',false);
				} else if (output.match(/^[0-9]+ [a-z]+$/i)) {
					output = output += ' '+year;
				} else {
					output = flexibleInternationalDate(output);
				}
				break;
		}

	}
	return output;

}

function appendYearToDate (input, probablyFuture) {
	var currentDate = new Date();
	var currentYear = currentDate.getFullYear();
	currentDate = new Date(currentYear,currentDate.getMonth(),currentDate.getDate()); // Remove h/m/s
	var oneWeekAgo = new Date(currentYear,currentDate.getMonth(),currentDate.getDate() - 7);
	// If using the previous year puts it in the past week, assume that's correct
	var output = input + (currentYear - 1);
	if (new Date(output) < oneWeekAgo) output = input + currentYear;
	// If it's probably in the future and it's more than a week ago, add a year
	if (probablyFuture) {
		var thisDate = new Date(output);
		if (thisDate < oneWeekAgo) output = input + (currentYear+1);
	}
	return output;
}

function replacedMatch (string, pattern, replacement) {
	if (pattern.test(string)) {
		string = string.replace(pattern,replacement);
		return string;
	}
	return null;
}

function endDateInRange (string, flexible) {
	var result = datesInRange(string,flexible);
	return result[1];
}

function datesInRange (string, flexible) {
	string = trim(string);
	if (flexible && (result = string.match(/(.*) - (.*)/))) return [result[1],result[2]]; // Fri, Feb 20 - Wed, Feb 25 by 8pm
	if (result = string.match(/(.{2,}) - ([^(]+)/i)) return [result[1],result[2]]; // March 12 - March 22, 17 Dec - 21 Dec
	if (result = string.match(/([a-z]+) ([0-9]{1,2})-([0-9]{1,2})/i)) return [result[1]+' '+result[2],result[1]+' '+result[3]]; // March 16-22
	if (result = string.match(/([0-9]{1,2}\/[0-9]{1,2})-([0-9]{1,2}\/[0-9]{1,2})/i)) return [result[1],result[2]]; // 3/16-3/22
	if (result = string.match(/(?:entre )?(le [0-9]+ [a-z]+) et (le [0-9]+ [a-z]+)\.?/i)) return [result[1],result[2]]; // entre le 20 janv et le 25 janv.
	return ['',string];
}

function possibleWeekDay ( string ) {
	var regEx = /^[a-z\u00E0-\u00FC']+$/i;
	return regEx.test(string);
}

function dateFromWeekDay ( string, language, referenceDate ) {
	var output = false;
	if (typeof string == 'string') {
		var dayOffset = false;
		var dayNo = false;
		var lowercaseString = string.toLowerCase();
		switch (language) {
			case 'de':
				switch (lowercaseString) {
					case 'heute':		dayOffset = 0; break;
					case 'morgen':		dayOffset = 1; break;
					case 'gestern':		dayOffset = -1; break;
					case 'sonntag':		dayNo = 0; break;
					case 'montag':		dayNo = 1; break;
					case 'dienstag':	dayNo = 2; break;
					case 'mittwoch':	dayNo = 3; break;
					case 'donnerstag':	dayNo = 4; break;
					case 'freitag':		dayNo = 5; break;
					case 'sonnabend':	dayNo = 6; break;
				}
				break;

			case 'es':
				switch (lowercaseString) {
					case 'hoy':			dayOffset = 0; break;
					case 'mañana':		dayOffset = 1; break;
					case 'ayer':		dayOffset = -1; break;
					case 'domingo':		dayNo = 0; break;
					case 'lunes':		dayNo = 1; break;
					case 'martes':		dayNo = 2; break;
					case 'miércoles':	dayNo = 3; break;
					case 'jueves':		dayNo = 4; break;
					case 'viernes':		dayNo = 5; break;
					case 'sábado':		dayNo = 6; break;
				}
				break;

			case 'fr':
				switch (lowercaseString) {
					case 'aujourd\'hui':dayOffset = 0; break;
					case 'demain':		dayOffset = 1; break;
					case 'hier':		dayOffset = -1; break;
					case 'dimanche':	dayNo = 0; break;
					case 'lundi':		dayNo = 1; break;
					case 'mardi':		dayNo = 2; break;
					case 'mercredi':	dayNo = 3; break;
					case 'jeudi':		dayNo = 4; break;
					case 'vendredi':	dayNo = 5; break;
					case 'samedi':		dayNo = 6; break;
				}
				break;
			case 'it':
				switch (lowercaseString) {
					case 'oggi':		dayOffset = 0; break;
					case 'domani':		dayOffset = 1; break;
					case 'ieri':		dayOffset = -1; break;
					case 'domenica':	dayNo = 0; break;
					case 'lunedì':		dayNo = 1; break;
					case 'martedì':		dayNo = 2; break;
					case 'mercoledì':	dayNo = 3; break;
					case 'giovedì':		dayNo = 4; break;
					case 'venerdì':		dayNo = 5; break;
					case 'sabato':		dayNo = 6; break;
				}
				break;
			case 'en':
			default:
				switch (lowercaseString) {
					case 'today':		dayOffset = 0; break;
					case 'tomorrow':	dayOffset = 1; break;
					case 'yesterday':	dayOffset = -1; break;
					case 'sunday':		dayNo = 0; break;
					case 'monday':		dayNo = 1; break;
					case 'tuesday':		dayNo = 2; break;
					case 'wednesday':	dayNo = 3; break;
					case 'thursday':	dayNo = 4; break;
					case 'friday':		dayNo = 5; break;
					case 'saturday':	dayNo = 6; break;
				}
				break;
		}
		if (dayOffset !== false) {
			var now = (referenceDate) ? new Date(referenceDate) : new Date();
			now.setDate(now.getDate() + dayOffset);
			output = now.toDateString();
		} else if (dayNo !== false) {
			var now = (referenceDate) ? new Date(referenceDate) : new Date();
			var currentNumber = now.getDay();
			if (dayNo <= currentNumber) dayNo += 7;
			dayNo -= currentNumber;
			now.setDate(now.getDate() + dayNo);
			output = now.toDateString();
		}
	}
	return (output) ? output : string;
}

function isInternationalDate (input) {
	// 0 is unknown, 1 is true, -1 is false
	if (!input) return 0;
	var result;
	if (result = input.match(/^([0-9]{1,2})\/([0-9]{1,2})/i)) {
		if (result[1] > 12) return 1;
		if (result[2] > 12) return -1;
	}
	return 0;
}

function reorderInternationalDate (input) {
	return (input) ? input.replace(/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/i,'$2/$1/$3') : '';
}

function flexibleInternationalDate (input) {
	// Might be safe to replace reorderInternationalDate with this?
	return (input) ? input.replace(/([0-9]{1,2})(?:\/|-|\.)([0-9]{1,2})(?:\/|-|\.)([0-9]{2,4})/i,'$2/$1/$3') : '';
}

function cleanLocation (string,from) {
	if (string) {
		if (!from) from = this.from; // Remove once 4.6 is dropped
		switch (from) {
			case 'www.amazon.com':
			case 'www.amazon.com/url':
				string = defaultCleanLocation(string);
				string = string.replace(/^US$/i,'United States');
				string = string.replace('Unknown','');
				string = string.replace(/^March Air Reserve Ba,/,'March Air Reserve Base,');
				// China Post
				string = string.replace(/^United States Of America Us([0-9]+)$/i,'$1, United States');
				string = string.replace(/^United States Of America Usus([A-Z]{3})[a-z]$/i,'$1, United States');
				string = string.replace('GuangDongShengShenZhenShi BTYY','ShenZhen, GuangDongSheng');
				// USPS
				string = string.replace(/ ((International |Network )?Distribution Center( Annex)?), /i,', ');
				break;
			case 'www.aramex.com':
				string = defaultCleanLocation(string);
				string = string.replace(/ (Branch|Office|Express Hub),/i,',');
				break;
			case 'em.canadapost.ca':
				string = defaultCleanLocation(string);
				string = string.replace(/^US([A-Z]{3})[A-Z],? ?(USA)?$/i,'$1, United States');
				string = string.replace(/[0-9]+,(Spain|China)/i,'$1');
				string = string.replace('VILLE ST-LAURENT, QC','Saint-Laurent, Quebec');
				break;
			case 'www.chronopost.fr':
				string = defaultCleanLocation(string);
				string = string.replace(/^\.+$/,'');
				string = string.replace(/^Chronopost France$/,'');
				string = string.replace(/^VALENCE CHRONOPOST$/i,'Valence, FR');
				string = string.replace(/^REUNION CEI$/,'La Réunion, FR');
				string = string.replace(/^NESPRESSO FRANCE SAS$/,''); // Initial location may be a business
				string = string.replace(/(-|,)? (CHRONOPOST|FLOREVEUR)/i,'');
				break;				
			case 'www.dhl.com':
				string = defaultCleanLocation(string);
				string = string.replace(/ (HUB|GATEWAY),/i,'');
				string = string.replace(/^(.*?) - \1$/,'$1');
				string = string.replace(/, THE$/i,'');
				string = string.replace(/ - /i,', ');
				string = string.replace(/CHINA, PEOPLES REPUBLIC$/i,'CHINA');
				string = string.replace('ONTARIO SERVICE AREA, ON, CANADA','ONTARIO, CANADA');
				break;
			case 'nolp.dhl.de':
				string = defaultCleanLocation(string);
				string = string.replace(/^HUB /i,'');
				string = string.replace(/^Rüdersdorf, (Germany|Deutschland)$/i,'Rüdersdorf bei Berlin, $1');
				break;
			case 'www.dpd.com':
				string = defaultCleanLocation(string);
				string = string.replace(/^(DPD Datenzentrum|DPD data centre)$/i,'');
				break;
			case 'www.fedex.com':
				string = defaultCleanLocation(string);
				string = string.replace(/^FEDEX SMARTPOST /i,'');
				string = string.replace(/(^|\s)(.*?), \2$/i,'$1$2');
				string = string.replace('SUN VALLEY, CA','SUN VALLEY, CA, US');
				break;
			case 'www.kuronekoyamato.co.jp':
				string = defaultCleanLocation(string);
				string = string.replace(/支店(（[^）]+）)?$/i,'$1');
				string = string.replace(/(センター|物流システム|ＡＦＣ|ベース店?)$/i,'');
				break;
			case 'www.laposte.fr':
				string = defaultCleanLocation(string);
				string = string.replace(/(Plate-forme Centre|Centre Courrier [0-9]+|Centre platform|Sorting office [0-9]+)/i,'');
				string = string.replace(/ ([0-9]+ ?PPDC|PDC[0-9])? ?(\([0-9]+\))?$/i,'');
				string = string.replace(/(^Agence |^Plate-forme |^Bureau de Poste | agency$| platform$|^Post office | PPDC$)/i,'');
				string = string.replace(/(^|\s)(Nord|Sud|Est|Ouest)$/i,'');
				string = string.replace(/^PACA$/,'Provence-Alpes-Côte d\'Azur');
				string = string.replace(/^www.colissimo.fr\/.*/,'');
				break;
			case 'www.lasership.com':
				string = defaultCleanLocation(string);
				string = string.replace(/^US$/i,'United States');
				string = string.replace('Unknown','');
				break;
			case 'www.ontrac.com':
				string = defaultCleanLocation(string);
				string = string.replace(/([a-z])([A-Z])/,'$1 $2');
				string = string.replace(/^([A-Z]+), CA$/i,'$1, CA, US');
				string = string.replace(/^SALTLAKE$/i,'SALT LAKE');
				break;
			case 'www.parcelforce.com':
				string = defaultCleanLocation(string);
				string = string.replace(/^Delivery Agent - /i,'');
				string = string.replace(/ DEFAULT$/i,'');
				string = string.replace(/^(National|International)$/i,''); // Hub already removed
				break;
			case 'www.post.at':
				string = defaultCleanLocation(string);
				string = string.replace(/^ATALLA\b/i,'Allhaming');
				string = string.replace(/^ATVIEA\b/i,'Wien Flughafen');
				string = string.replace(/^ATBIEA\b/i,'Biedermannsdorf');
				string = string.replace(/^ATLNZD\b/i,'Linz');
				string = string.replace(/^ATLUTA\b/i,'Lauterach');
				string = string.replace(/^ATVIEG\b/i,'Wien');
				string = string.replace(/^ATVIEB\b/i,'Wien');
				string = string.replace(/^ATWNFA\b/i,'Wiener Neudorf');
				string = string.replace(/^ATWNFZ\b/i,'Wiener Neudorf');
				string = string.replace(/^ATVIEC\b/i,'Wien');
				string = string.replace(/^ATSZGA\b/i,'Salzburg');
				string = string.replace(/^ATHALA\b/i,'Hall in Tirol');
				string = string.replace(/^ATWLRA\b/i,'Wolfurt');
				if (matches = string.match(/^(Postcode|PLZ) (.*)$/i)) {
					string = matches[2];
					if (string.match(/^([1-9][0-9]{3})$/)) {
						if (parseInt(string) <= 1901) string = 'Vienna';
						string += ', Austria';
					}
				}
				break;
			case 'www.post.ch':
				string = defaultCleanLocation(string);
				string = string.replace(/^(Swiss Post|[0-9]+) /i,'');
				string = string.replace(/ (Zustellung|Filiale di distribuzione)$/i,'');
				string = string.replace('SE','Sweden');
				string = string.replace('Asendia USA','USA');
				break;
			case 'www.poste.it':
				string = defaultCleanLocation(string);
				string = string.replace(/^([A-Z]{2})$/,'$1, Italia');	
				string = string.replace(/Centro Scambi Internazionale/i,'Linate, Milano, Italia');		
				break;
			case 'www.posten.no':
				string = defaultCleanLocation(string);
				string = string.replace(/^[0-9]+ /,'');
				break;
			case 'www.postdanmark.dk': // www.postnord.dk
			case 'www.posten.se': // www.postnord.se
				string = defaultCleanLocation(string);
				// Sweden
				string = string.replace(/^(PostNord,?|Posten ?Företag|Posten)\b ?/,'');
				// Denmark
				string = string.replace(/^(.*?) (Distributionscenter|Pakkecenter - [A-Z]+),/,'$1,');
				string = string.replace(/^Vestegnens Pakkedist, Denmark$/,'Priorparken 385, 2605 Brøndby, Denmark');
				string = string.replace(/^(Amazon EU SARL|ASOS C\/O P2P Mailing)$/,'');
				break;
			case 'www.postnl.nl':
			case 'www.tntpost.nl':
				string = defaultCleanLocation(string);
				string = string.replace(/^([0-9]{4} ?[A-Z]{2})( .*)$/,'$1');
				break;
			case 'www.purolator.com':
				string = defaultCleanLocation(string);
				string = string.replace(/ Sort Ctr\/ctr Tri/i,'');
				string = string.replace(/^Purolator$/,'');
				break;
			case 'www.sagawa-exp.co.jp':
				string = defaultCleanLocation(string);
				string = string.replace(/中継センター$/,'');
				break;
			case 'www.tnt.com':
			case 'www.tnt.com/ref':
				string = defaultCleanLocation(string);
				string = string.replace(/^HUB RHEIN MAIN DFT$/i,'Rhein-Main');
				string = string.replace(/^AL BATHA BORDER CUSTOMS$/i,''); // Unknown to MapKit
				string = string.replace('DHAHRAN CAUSEWAY','Dhahran');
				string = string.replace(/ (Air|Border Customs|Control Tower|Road)$/i,''); // X Road Hub or X Air Hub, Hub was already removed
				string = string.replace('GLOBAL TRANSIT','TNT Global Transit Hub, Netherlands'); // GLOBAL TRANSIT HUB
				break;
			case 'www.tntexpress.com.au':
				string = defaultCleanLocation(string);
				string = string.replace(/ (Priority|Riteway)$/i,'');
				break;
			case 'www.ups.com':
				string = defaultCleanLocation(string);
				string = string.replace(/\b([A-Z]{2}) US\b/,'$1, United States');
				string = string.replace(/\bHodgkins, IN\b/i,'Hammond, IN');
				string = string.replace(/Herne-Boernig, Germany/i,'Herne-Börnig, Germany');
				string = string.replace(/\bKorea, Republic of\b/i,'Republic of Korea');
				break;
			case 'www.usps.com':
				var city = substringWithPattern(string,'<EventCity>([^<]*)</EventCity>');
				city = city.replace(/^ISC /i,'');
				city = city.replace(/ ?\(USPS\)$/i,'');
				city = city.replace(/ (INT|INT CONTAINER [0-9]+)$/i,'');
				city = city.replace(/ ((INTERNATIONAL |NETWORK )?DISTRIBUTION CENTER( ANNEX)?) ?$/i,'');
				city = city.replace('LANGLEY HWDC','Heathrow World Distribution Centre');
				var state = substringWithPattern(string,'<EventState>([^<]*)</EventState>');
				var zip = substringWithPattern(string,'<EventZIPCode>([^<]*)</EventZIPCode>');
				var country = substringWithPattern(string,'<EventCountry>([^<]*)</EventCountry>');
				string = locationFromParts(city,state,zip,country) || string;
				string = removeHtmlEntities(string);
				string = string.replace(/^ON ITS WAY TO .+$/i,'');
				string = string.replace(/^NTX P&DC$/i,'Coppell, TX 75099'); // Or remove P&DC
				string = string.replace(/^NORTH METRO, GA 30026$/i,'Duluth, GA 30026');
				string = string.replace(/ EMS CHINA$/i,' CHINA');
				string = string.replace(/^TOKYO INT( CONTAINER 1)? JAPAN$/i,'Tokyo, Japan');
				string = string.replace('SOUTHERN MD FACILITY, MD 20797','US Post Office, Laurel, MD'); // PO zip code
				break;
			case 'www.yodel.co.uk':
				string = defaultCleanLocation(string);
				string = string.replace(/(Customer| service centre| hub| depot| van| home delivery)$/i,'');
				break;
			default:
				string = defaultCleanLocation(string);
				break;
		}
	}
	return string;
}

function defaultCleanLocation(string) {
	if (string) {
		string = stripHTML(string);
		string = string.replace(/ (Hub|Depot|Distribution|Distributionsbasis|Paketzentrum|Centre Colis|Base de distribution)$/i,'');
	}
	return string;
}

function substringWithPattern(string, pattern) {
	var result = '';
	if (string) {
		var regex = new RegExp(pattern);
		var matches = string.match(regex);
		if (matches) result = matches[1];
	}
	return result;
}

function locationFromParts ( city, state, zip, country ) {
	var location = '';
	if (city) location += city;
	if (state) {
		if (location) location += ', ';
		location += state;
	}
	if (zip) {
		if (location) location += ' ';
		location += zip;
	}
	if (country) {
		if (location) location += ', ';
		location += country;
	}
	return location;
}

function locationFromStatus (status, from) {
	if (!status) return '';
	var pattern = /Die Sendung ist im Hermes Verteilzentrum (.*?) eingetroffen/i;
	var location = status.match(pattern);
	if (!location) return '';
	return location[1];
}

// Required for commonFilesVersion < 204, sync version < 8.1

if (typeof hasOSFeatureLevel !== 'function') {
	function hasOSFeatureLevel (requiredLevel) {
		var level = 0;
		if (typeof osFeatureLevel !== 'undefined') {
			level = osFeatureLevel;
		} else if (dashboardWidget && navigator.userAgent) {
			var a = navigator.userAgent;
			var v = a.match(/Mac OS X ([0-9]+)_([0-9]+)(?:_([0-9]+))?/);
			if (v && (v[1] > 10 || v[1] == 10 && v[2] >= 9)) level = 5;
		} else {
			level = 5;
		}
		return (level >= requiredLevel);
	}
}

if (typeof preferredLanguage !== 'function') {
	function preferredLanguage () {
		var userLang = translate('xx');
		for (var i = 0; i < arguments.length; i++) {
			if (arguments[i] == userLang) return userLang;
		}
		return arguments[0];
	}
}

if (typeof parseJSONInHTML !== 'function') {
	function parseJSONInHTML (html, pattern) {
		if (!html) return null;
		var result = html.match(pattern);
		if (!result) return null;
		return parseJSON(result[1]);
	}
}

if (typeof parseJSON !== 'function') {
	function parseJSON (json) {
		var object = null;
		try {
			object = JSON.parse(json);
		} catch (err) {
			logMessage(1,err);
		}
		return object;
	}
}

// Once sync version 7.1 is required, commonFilesVersion can be used directly

function getCommonFilesVersion () {
	if (typeof commonFilesVersion === 'undefined') return 0;
	return commonFilesVersion;
}
