﻿// HttpRequest object & the desired callback
var	g_XmlRequester, g_XmlRequestCallback, g_XmlRequestUrl, g_XmlRequestStatus, g_XmlRequestDefaultError;
// See StartSynchronousDownload
var	g_XmlRequestCompleteCalled;

var	g_CurrencySymbol = "£";
var   g_VatAmount = 0.175;

/*
 *
 * Data downloading.
 *
 */

// Create the request object used to retrieve data from server
function	CreateRequester() {
	if( typeof(XMLHttpRequest) != 'undefined') {
		return new XMLHttpRequest();
	}
	try {
		return new ActiveXObject("Msxml2.XMLHTTP");
	}
	catch(e) {
		try {
			return new ActiveXObject("Microsoft.XMLHTTP");
		}
		catch(e) {
		}
	}
	alert("This browser does not support a required JavaScript component, this page will not function correctly");
	return null;
}

// Callback for XMLHttpRequest. We don't use the supplied callback as the direct callback
// as we have other house keeping to do.
function	XmlRequestComplete() {
	// 4 == Request has finished loading
	g_XmlRequestStatus = false;
	g_XmlRequestCompleteCalled = true;

	if( g_XmlRequester.readyState == 4 )
		switch( g_XmlRequester.status ) {

		case 200:
			try {
				if( g_XmlRequestCallback )
					g_XmlRequestCallback();
				g_XmlRequestStatus = true;
			}
			catch(e) {
				alert("Exception thrown from XHTTPRequest Handler: " + e);
			}
		break;

		case 400:
			var str = g_XmlRequester.getResponseHeader("SessionInfo");

			if( str && str == "TimedOut" )
			{
				alert("Your session has timed out.\nYour basket has been lost.");
				window.location = "default.aspx";
				return;
			}

			str = g_XmlRequester.getResponseHeader("DatabaseError");

			if( str && str == "true" )
				alert("Your request raised a Database exception");
		break;

		default:
			if( g_XmlRequestDefaultError )
				alert("A data request to the WebApplication server failed:\nRequest Url: " + g_XmlRequestUrl
					+ "\nStatus Code: " + g_XmlRequester.status
					+ "\nResponse Text: " + g_XmlRequester.responseText);
			g_XmlRequestStatus = false;
		break;
		}
}

// Get data from the server.
function	StartDownload(url, callback) {
//	XmlRequester.
	if( !g_XmlRequester )
		g_XmlRequester = CreateRequester();

	g_XmlRequestDefaultError = true;

	// store the callback and the url for if things go wrong
	g_XmlRequestCallback = callback;
	g_XmlRequestUrl = url;

	g_XmlRequester.open("GET", url, true);
	g_XmlRequester.onreadystatechange = XmlRequestComplete;
	g_XmlRequester.send(null);
}

function	StartSynchronousDownload(url, callback, defaultError) {
	g_XmlRequestCallback = callback;
	g_XmlRequestUrl = url;
	g_XmlRequestDefaultError = defaultError;
	g_XmlRequestCompleteCalled = false;

	// okies, various browsers handle Synchronous request differently.
	// standard says NOT to call the onreadystatechange callback, but older
	// browsers do.
	g_XmlRequester.open("GET", url, false);
	g_XmlRequester.onreadystatechange = XmlRequestComplete;
	g_XmlRequester.send(null);

	// so if the browser didn't call the callback, fire it ourselves.
	if(g_XmlRequestCompleteCalled == false)
		XmlRequestComplete();

	return g_XmlRequestStatus;
}

// Get the string from a Xml doc that contains a single string result
function GetXmlStringResult(doc) {
	var strNode = doc.lastChild;

	if( "textContent" in strNode )
		return	strNode.textContent;
	return strNode.text;
}

// strip white space nodes out of the child of an xml node, mozilla leaves them
// in which makes life a pita
function StripWhiteSpaceNodes(docElement) {

	for( var i = docElement.childNodes.length - 1; i >= 0; --i )
		if( docElement.childNodes[ i ].nodeName == "#text" )
			docElement.removeChild( docElement.childNodes[ i ] );
}


function IsMoz() {
	return	window.navigator.appName.lastIndexOf( "Netscape" ) != -1;
}

/*
 * Classes.
 * WARNING:
 *		The JavaScript class member names MUST match their counter parts in the WebSerivces code.
 */
// class: PartType 
function	PartType( id, description ) {
	this.m_Id			= id;
	this.m_Description	= description;

	// Create a Part and set it's properties from the the AnyTypeElement Xml node argument
	this.ReadFromXmlAnyTypeNode = function( anyTypeElement ) {

		for( i = 0; i < anyTypeElement.childNodes.length; i++ ) {
			var	theProperty = anyTypeElement.childNodes[i];

			if( theProperty.nodeName in this ) {

				if( "textContent" in theProperty )
					item[ theProperty.nodeName ] = theProperty.textContent;
				else
					item[ theProperty.nodeName ] = theProperty.text;

				if( theProperty.nodeName == "m_Id" )
					this.m_Id = parseInt( this.m_Id );
			}
		}
		return item;
	}
}

// class: Part
function	Part() {
	this.m_PartCode	= "";
	this.m_Price		= 0;
	this.m_Description	= "";
	this.m_Url			= "";
	this.m_bEssential 	= false;

	// Create a Part and set it's properties from the the AnyTypeElement Xml node argument
	this.ReadFromXmlAnyTypeNode = function( anyTypeElement )
	{

		for( i = 0; i < anyTypeElement.childNodes.length; i++ )
		{
			var	theProperty = anyTypeElement.childNodes[i];

			if( theProperty.nodeName in this )
			{
				var theText;

				if( "textContent" in theProperty )
					theText = theProperty.textContent;
				else
					theText = theProperty.text;

				this[ theProperty.nodeName ] = theText;

				if( theProperty.nodeName == "m_Price" )
					this.m_Price = parseFloat( theText );

				if( theProperty.nodeName == "m_bEssential" )
					this.m_bEssential = this.m_bEssential == "true";
			}
		}
      this.m_Url = flipSlashes( this.m_Url );
	}
}

// class: BasketItem
function BasketItem() {
	this.m_PartCode		= "";
	this.m_Price			= 0;
	this.m_Quantity		= 0;

	this.m_Description	= "";
	this.m_Url				= "";
	this.m_Index 			= 0;
	this.m_bEssential		= false;

	// Create a Part and set it's properties from the the AnyTypeElement Xml node argument
	this.ReadFromXmlAnyTypeNode = function( anyTypeElement ) {

		for( i = 0; i < anyTypeElement.childNodes.length; i++ ) {
			var	theProperty = anyTypeElement.childNodes[i];

			if( theProperty.nodeName in this ) {
				var theText;

				if( "textContent" in theProperty )
					theText = theProperty.textContent;
				else
					theText = theProperty.text;

				this[ theProperty.nodeName ] = theText;

				if( theProperty.nodeName == "m_Price" )
					this.m_Price = parseFloat( theText );

				if( theProperty.nodeName == "m_Quantity" )
					this.m_Quantity = parseFloat( theText );

				if( theProperty.nodeName == "m_Index" )
					this.m_Index = parseInt( theText );

				if( theProperty.nodeName == "m_bEssential" )
					this.m_bEssential = this.m_bEssential == "true";
			}
		}
	}
}

// class: BasketSummary
function BasketSummary() {
	this.m_Count = 0;
	this.m_Total = 0;

	this.ReadFromXml = function( element ) {
		for( i = 0; i < element.childNodes.length; i++ ) {
			var	theProperty = element.childNodes[i];

			if( theProperty.nodeName in this ) {
				var	theText;

				if( "textContent" in theProperty )
					theText = theProperty.textContent;
				else
					theText = theProperty.text;

				this[ theProperty.nodeName ] = theText;

				if( theProperty.nodeName == "m_Count" )
					this.m_Count = parseInt( theText );

				if( theProperty.nodeName == "m_Total" )
					this.m_Total = parseFloat( theText );
			}
		}
	}
}

// class: DiagramParams
function DiagramParams() {
	this.m_ModelCode = "";
	this.m_DiagramId = 0;
	this.m_DiagramSetId = 0;
	this.m_DiagramSetMainDiagramId = 0;
	this.m_InitialPartType = 0;

	this.m_RangeId = null;
	this.m_SectionType = null;
	this.m_MountType = null;

	this.ReadFromXml = function( element ) {
		if( element.nodeName != "DiagramParams" )
			throw	"DiagramParams::ReadFromXml, passed Xml element is not DiagramParams node";

		for( i = 0; i < element.childNodes.length; i++ ) {
			var	theProperty = element.childNodes[i];

			if( theProperty.nodeName in this ) {
				var	theText;

				if( "textContent" in theProperty )
					theText = theProperty.textContent;
				else
					theText = theProperty.text;

				this[ theProperty.nodeName ] = theText;

				if( theProperty.nodeName == "m_DiagramId" )
					this.m_DiagramId = parseInt( theText );

				if( theProperty.nodeName == "m_DiagramSetId" )
					this.m_DiagramSetId = parseInt( theText );

				if( theProperty.nodeName == "m_DiagramSetMainDiagramId" )
					this.m_DiagramSetMainDiagramId = parseInt( theText );

				if( theProperty.nodeName == "m_InitialPartType" )
					this.m_InitialPartType = parseInt( theText );


			}
		}
	}
}

/*
 * Diagram class, maps clicks on the overlay regions to a part type id
 * and possbily a different diagram. When an overlay region is clicked,
 * we switch to a different diagram if required & download a list of matching parts.
 */
function	RgnPartTypeMapEntry( rgn_id, part_type_id, diagram_id ) {
	this.m_RgnId		= rgn_id;
	this.m_DiagramId	= diagram_id;
	this.m_OverlayImg	= null;
	this.m_PartTypeId	= part_type_id;
	this.m_PartType		= null;

	this.Resolve = function() {
		this.m_OverlayImg	= document.getElementById( "rgn_" + this.m_RgnId );
		this.m_PartType		= FindPartTypeById( g_PartTypesArray, this.m_PartTypeId );
	};

	this.ReadFromXmlAnyTypeNode = function( anyTypeElement ) {
		for( i = 0; i < anyTypeElement.childNodes.length; i++ ) {
			var	theProperty = anyTypeElement.childNodes[i];

			if( theProperty.nodeName in this ) {
				var theText;

				if( "textContent" in theProperty )
					theText = theProperty.textContent;
				else
					theText = theProperty.text;

				this[ theProperty.nodeName ] = parseInt(theText);
			}
		}
	}
}

// class: ModelDims
function	ModelDims() {

	this.ReadFromXml = function(element) {
		for( var i = 0; i < element.childNodes.length; ++i ) {
			var	theProperty = element.childNodes[i];

			if( theProperty.nodeName.substr(0, 2) == "m_" ) {
				var theText;

				if( "textContent" in theProperty )
					theText = theProperty.textContent;
				else
					theText = theProperty.text;

				this[ theProperty.nodeName ] = parseInt(theText);
			}
		}
	}
}

// class: DiagramInfo
function	DiagramInfo() {

	this.m_Id = 0;
	this.m_Title = null;

	this.ReadFromXmlAnyTypeNode = function( anyTypeElement ) {
		for( i = 0; i < anyTypeElement.childNodes.length; i++ ) {
			var	theProperty = anyTypeElement.childNodes[i];

			if( theProperty.nodeName in this ) {
				var theText;

				if( "textContent" in theProperty )
					theText = theProperty.textContent;
				else
					theText = theProperty.text;

				if( theProperty.nodeName == "m_Id" )
					this.m_Id = parseInt(theText);
				else
					this[ theProperty.nodeName ] = theText;
			}
		}
	}
}

// class: ModelInfo
function	ModelInfo() {
	this.m_Model = null;
	this.m_DiagramSetId = 0;

	this.ReadFromXmlAnyTypeNode = function( anyTypeElement ) {
		for( i = 0; i < anyTypeElement.childNodes.length; i++ ) {
			var	theProperty = anyTypeElement.childNodes[i];

			if( theProperty.nodeName in this ) {
				var theText;

				if( "textContent" in theProperty )
					theText = theProperty.textContent;
				else
					theText = theProperty.text;

				if( theProperty.nodeName == "m_DiagramSetId" )
					this.m_DiagramSetId = parseInt(theText);
				else
					this[ theProperty.nodeName ] = theText;
			}
		}
	}
}

/*
 *
 * string manipulation
 *
 */
  
 // Turn a word in capitals into a word with just a starting captial letter for improved readability.
function	Decapitialise( str ) {
	return	str.replace( /\b([A-Z])([A-Z']+)/g,
			function( whole_match, first_letter, rest_of_word ) {
				return	first_letter + rest_of_word.toLowerCase();
			}
		);
}

function BaseName( str ) {
	var	indx = str.lastIndexOf('/');

	if( indx == -1 )
		return str;

	return str.substr( indx+1 );
}

function flipSlashes( str ) {
   return str.replace("\\", "/");
}

// Format a price. Specifically make sure 0 priced items are displayed as POA.
function	FormatPrice( price, currencySymbol ) {
	if( typeof(price) != "number" )
		return price;

	if( price == 0 )
		return	"POA";
   if( currencySymbol != null )
      return currencySymbol + price.toFixed(2);
	return	price.toFixed( 2 );
}

// list of tld's from http://data.iana.org/TLD/tlds-alpha-by-domain.txt
var twoLetterTldsRx = /AC|AD|AE|AF|AG|AI|AL|AM|AN|AO|AQ|AR|AS|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BJ|BM|BN|BO|BR|BS|BT|BV|BW|BY|BZ|CA|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|CR|CU|CV|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EDU|EE|EG|ER|ES|ET|EU|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|IO|IQ|IR|IS|IT|JE|JM|JO|JP|KE|KG|KH|KI|KM|KN|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|MG|MH|MK|ML|MM|MN|MO|MP|MQ|MR|MS|MT|MU|MV|MW|MX|MY|MZ|NA|NC|NE|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PS|PT|PW|PY|QA|RE|RO|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|ST|SU|SV|SY|SZ|TC|TD|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TP|TR|TT|TV|TW|TZ|UA|UG|UK|UM|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|YE|YT|YU|ZA|ZM|ZW/i;
var longTldsRx = /ASIA|ARPA|AERO|BIZ|CAT|COM|COOP|GOV|INFO|INT|JOBS|MIL|MOBI|MUSEUM|NAME|NET|ORG|PRO|TEL|TRAVEL/i
var emailRx = /^(?!\.)(?:\w|[\.-])+@(?!\.)(?:\w|[\.-](?!\.))+[a-z]+/i;

// Check to see if an email address is valid
function ValidateEmail( str ) {
	var	arry;

	arry = emailRx.exec( str );

	if( !arry || arry[ 0 ] != str )
		return false;

	var tld = str.substr( str.lastIndexOf(".") + 1 );

	arry = longTldsRx.exec( tld );

	if( arry && arry[ 0 ] == tld )
		return true;

	arry = twoLetterTldsRx.exec( tld );

	if( arry && arry[ 0 ] == tld )
		return true;

	return false;
}

/*
 *
 * table manipulation
 *
 */
// Create a table header cell, placing either text or a node into it.
function	CreateHeaderCell( obj ) {
	var	cell = document.createElement( "TH" );

	if( obj )
		if( typeof(obj) == "string" )
			cell.innerHTML = obj;
		else
			cell.appendChild( obj );

	return	cell;
}

// Create a form button
function CreateButton(displayText, callbackFunction) {
	var button = document.createElement( "INPUT" );

	button.type = "button";
	button.value = displayText;
	if( callbackFunction )
		button.onclick = callbackFunction;
	return button;
}

// Create a table cell, placing either text or a node into it.
function	CreateCell( obj ) {
	var	cell = document.createElement( "TD" );

	if( obj )
		if( typeof(obj) == "object" )
			cell.appendChild( obj );
		else
			cell.innerHTML = obj;

	return	cell;
}

// Delete the body of a table, assuming only one body per table
function	DeleteTableBody( table ) {
	for( var i = table.tBodies[ 0 ].rows.length - 1; i >= 0 ; --i )
		table.tBodies[ 0 ].deleteRow( i );
}

/*
 *
 * support broken browsers...
 *
 */
function	AddToSelectElement( select, option ) {
	try {
		select.add( option );
	} catch(e) {
		select.add( option, null );
	}
}

function getTextContent(ele) {
	if( "textContent" in ele )
		return ele.textContent;
	return ele.text;
}

// Calculate the row of an element. NOTE: element must be a child of a tabel cell.
function	CalcRowOfImage( ele, table ) {
	return	ele.parentNode.parentNode.rowIndex - table.tHead.rows.length;
}

function getEventSource( ev ) {
	if( ev.target )
		return ev.target;
	return ev.srcElement;
}

// Add an event callback function to an element
function 	AddEventListener( element, eventName, callback ) {
   if( element.addEventListener )
      element.addEventListener( eventName, callback, false );
   else
      element.attachEvent( "on" + eventName, callback );
}


function	GetBasketFromXmlDoc( XmlDoc ) {
	var basket = new Array();

	if( !XmlDoc || !XmlDoc.lastChild || !XmlDoc.childNodes )
		return basket;

	var nodeArray = XmlDoc.lastChild.childNodes;

	for(var i = 0; i < nodeArray.length; i++ ) {
		if( nodeArray[ i ].nodeName == "anyType" ) {
			var item = new BasketItem();
			item.ReadFromXmlAnyTypeNode( nodeArray[i] );
			basket.push( item );
		}
	}
	return basket;
}
