/* author: Don Kent, eShopHost Ltd */
/* Copyright (C) 2003-10, eShopHost Ltd. */

/*
HISTORY:
	1.0 18/09/03
	1.1 to 1.26 - see archived version 1.26 for details
	1.27 02/01/08 3.2, A.5: New Feature: Product Discount Code - uses 'text' option field & Otherdb
	Upgrade for 2nd Cart: PayPal & Dynamic eCatalogue
	2.0  04/03/08 3.2, A.6: Add 2nd cart: PayPal
	2.1  20/05/08 D.Kent 1st integration with new eCatalogue & PayPal PSG: A.6.2.PayPal CheckOut may also be a form, id = CheckOutForm; A. Skip CatItem Form initialisation if no form on page & move other initialisation to new B; B. now reads value for Cart from external var. Item Submit now refers to cart set by B; B. return paths as .asp for eCatalogue
	2.2  30/05/08 D.Kent B.2.2 Setting summary (Dansie) failed for 'old' web
	2.3  03/06/08 D.Kent A.7 item submittal prior to initialisation will cause missing options: hide 'add to basket' buttons until initialised
	2.4. 16/06/08 D.Kent B.3 New: Change search form action depending on folder location of page
	2.5  17/09/08 D.Kent
		3.1.3 add missing option text & correct selection text
		D add new mode 5 to return index of selection
	2.6  03/10/08 D.Kent D. Fix: no value returned for input type checkbox when not checked
	2.7  03/10/08 D.Kent A. CN103 Add DeleteDefaultTextFromTextOnFocusUntilChanged.
	2.8  04/10/08 D.Kent
		2. BR103 Consistent <pre> for Option text for popup types 1,3
		BR104 choice image names
	2.9  04/10/08 D.Kent C. BR105 corrected display property if image element
	2.10 03/11/08 D.Kent 2. CN104 eCommerceWeb: PopUpsPath can be specified in OtherDb
	2.11 04/11/08 D.Kent A.4 BR104 eCatalogue: Fix early exit if main Photo not found
	2.12 20/01/09 D.Kent 2.1.3.3 BR106 needs to remove any trailing price information for popup image name
	2.13 28/01/09 D.Kent New Feature CN107: A, 1, F, G, H, I. Reveal & calculate total price if required
	2.14 29/01/09 D.Kent To make compatible with added modules like CalcFoam:
		A. Changed name of function Initialise to CatItemInitialise
		1. Changed method of adding to window onload event
	2.15 30/01/09 D.Kent D. Add new mode 6 to Set value of specified option
	2.16 02/02/09 D.Kent D. BR107 Fix error - do not use 'Option' parameter in mode 0 (return length)
	2.17 02/02/09 D.Kent A.3b BR108 Fix error - do not simulate a Buyer Option Change if it is already hidden.
		C. Fix display property if image: was changing to inline even if the call was to hide!
	2.18 16/02/09 D.Kent 3.2 Add form validation check for default hiddens in case generated item page is wrong.
	2.19 03/03/09 D.Kent CN115 3.2.cart.2 Add support for new eshop variable returnURL
	2.20 09/03/09 D.Kent BR111 D ItemInput(..) gave wrong answers for radio group of length 1
	2.21 12/01/10 D.Kent 3.1 CN120 Respond to special instruction Quantity(Decimal) for quantity validator
	2.22 15/03/10 D.Kent B.2 BR124 Error for PayPal cart - referencing paymentfail - should be .html
	2.23 25/03/10 D.Kent 3.2.PayPal.2 BR126 - decode returnURL for PayPal!
	2.24 14/09/10 D.Kent 3.2.PayPal.3, B2.1 CN124 - permit merchant to be a whole email address to bypass construction
	2.25 06/10/10 D.Kent 3.2.Dansie.3 BR128 - Change recognition of 'no shipping' from "0.0" to 0

PURPOSE & USAGE
	1. Sets up Catalogue item page:
		- showing/hiding Buyer Options & Total Price as required, etc.
	2. Changes Catalogue item page on submit (add to basket):
		- checkout & basket summary suitable for chosen cart
		- search form action if eCatalogue
	3. Auto-calculation of Total Price for display, if required - on the fly

FUTURE
	1. A.4. 'Flash' Overlay Rendition: rhythmically: show/ hide, slide, change colour, filter (shadow, revealTrans)

INSTRUCTIONS
	1. Include as external Javascript on:
			cat/CatItem.asp - for Dynamic eCatalogue
			cat/cat010100x-item_0.html - etc. for eCommerce Stacked Data Pages
			all other pages that have checkout or basket summary
	2. Include Generic return pages paymentfail.html & paymentsuccess.html required for specific carts: PayPal
	3. Requires eshophj1.js
	4. Total Price feature requires:
		TotalPrice(£) - keyword & currency symbol parameter in Otherdb (use Special button on FE)
		Row container with id TotalPrice, with display: none
		Text box with id TotalPriceBox, with attribute: readonly

CONTENTS:
 - Public functions:
	1. OptionChange - Changes display of all options depending upon value of an option selected
	2. PopUpInfo - causes pop-up window to be created with action 0 to 5
	3. Validate - does any final checking of fields entered and then creates the variables needed by the shopping cart & submits

 - Internal functions:
	A. CatItemInitialise - Sets up Catalogue item page
		- showing/hiding as required:
			- Buyer Options (empty/void & dependent)
			- Unit Price - price with selected options (if OptionDB keyword is present)
		- event handlers for Text Box Buyer Options
		- Flash overlay processing
		- Product Discount Code
		- Checkout, Summary & Add to Basket
	B. InitialiseOther - changes checkout & basket summary suitable for chosen cart.
		- Needed on all other pages that have checkout or summary
	C. ChangeVisibility - For an element's id, changes visibility as requested.
	D. ItemInput (OptionId, Mode, Option) - returns the value selected
	E. W3CDOMfield(name, value) - Create W3C DOM hidden input element & append
	F. textChange(), textFocus(), textDefocus() - DeleteDefaultTextFromTextOnFocusUntilChanged
	G. setTotalPrice () - set total price using selected options
	H. calculateUnitPrice () - for total price using selected options
	I. quantityChange () - event handler for quantity

 - External functions used in eshophj1.js:
	- Check (FieldId, CheckType)
	- FieldValue (FieldId)
	- MakeHiddenInput (Fieldname, Value)

 - Immediate code:
	To call CatItemInitialise() - at end of module
*/


/* Global Variables */
var OptionIdStart = 1;
var OptionIdMax = 10;
var OptionIdStep = 1; // Test value 5 - just do options 1 and 6 - set to 1 for real
var OptionQty = 10; // In the depend 'array variable' from the dB
var ProductDiscount = {};
var TextObjects = {};
var TotalPriceRequired = false;
var CurrencySymbol;
var HiddenBuyerOptions = new Array(10);


/* Public Functions */
function OptionChange(OptionId) { // 1.
	/*
	Depending upon value of an option selected:
	- Changes display of all options 
	- Auto-calculates TotalPrice if required
	*/
	// 1.0 Total Price
	if (TotalPriceRequired) {
		setTotalPrice (); // (See G.)
	}
	
	// 1.1. Get the Depend string
	var DependObject = getObj("Option" + OptionId + "depend");
	var DependString = DependObject.value;
	var Action;

	// 1.2. Do we need to do anything at all?
	if (DependString.length == 0) {
		return; // No options depend on selection - do nothing
	}
	// alert ("Non-null DependString: " + DependString); // Debug

	// 1.3. What is the value of the selected option?
	var Selection = ItemInput (OptionId, 4, 0);	// 4 = Value of selected Option
 
	/* Format = ;SelectedOptionValue,Option1Change..OptionnChange - repeated for each Option value that something other than nothing is required
	e.g. ;1,-101010101;red,-101010101;etc. where '-' = do nothing; 0 = hide; 1 = show */
	// alert ("Selected Index = " + si + ". Selection: " + Selection); // Debug

	// 1.4. if the selected option value is NOT in the Depend String, do nothing
	var pos = DependString.indexOf(";" + Selection + ",");
	if (pos == -1) { // Not found
		return;
	}
	// alert ("String found: " + ";" + Selection + ", At position: " + pos); // Debug

	// 1.5. if the selected option value IS in the Depend String, perform the changes
	var j = OptionIdStart - 1;
	for (k=pos + Selection.length + 2; k<pos + Selection.length + 2 + OptionQty; k++) {
		j++;
		Action = DependString.charAt(k);
		// alert ("Action value: " + Action + ". At position " + i + " using: pos = " + pos + ", length = " + Selection.length); // Debug
		if (Action == "-") { // Do nothing
			continue;
		}
		if (Action == 0) { // Hide
			HiddenBuyerOptions[j] = true;
			Action = "none"; // display property for hide & remove space occupied
		}
		else { // Show - Action = 1
			HiddenBuyerOptions[j] = false;
			Action = "block"; // display property for normal display (show)
		}
		ChangeVisibility ("Option" + j + "descrcell", Action);
		ChangeVisibility ("Option" + j + "dbcell", Action);
		ChangeVisibility ("Option" + j + "textcell", Action);
	}
}


function PopUpInfo (OptionId, Action) { // 2.
	/* Causes pop-up window for Buyer Option (Input Type e.g. Combo, etc.) to be created
With Action 0 to 5:
	0 - No action
	1 - Display text for just the choice selected
	2 - Display text for the option as a whole
	3 - Display image & any text for just the choice selected
	4 - Display image for the option as a whole + any text (as action 2)
	5 - Display a set of images (& any text) corresponding to each of the choices available
	 in the Input Type and include a Select link to set the choice in the Input Type.
SUMMARY:
	Each Buyer Option has an Input Type (Combo box, Radio, Checkbox, Text, Multiline) with one or more Choices
	Action	TEXT	IMAGE	ONE for Option	ONE per Choice		SET for all choices
	0		-		-		-				-					-
	2		Text	-		Per Option		-					-
	1		Text	-		-				Per Choice			-
	4		(Text)	Image	Per Option		-					-
	3		(Text)	Image	-				Per Choice			-
	5		(Text)	Images	-				-					Complete Set
	*/

	// 2.1. Determine Action required
	var InfoContent = "";
	var FormElementObject1;
	var Filename;
	var FileExt;
	var Selection;
	var TextValue;
	
	// PopUps Image Path
	var Otherdb = FieldValue ("Otherdb"); // eShopHost field containing keywords
	var PopUpsPathRegEx = /PopUpsPath\(([^\)]*)\)/; // get all between ()
	var PopUpsPath = "../assets/images/"; // default
	if (Otherdb.search (PopUpsPathRegEx) != -1) { // Path defined
		PopUpsPath = RegExp.$1;
	}

	// Common text field
	// - Just 1 piece of text is the header. More pipe delimited text is for selections
	var FormElementObject = getObj ("Option" + OptionId + "addtext");
	var TextContent = FormElementObject.value;
	var TextArray;
	var TAL = 0; // length of TextArray
	if (TextContent != "") { // Some text found
		TextArray = TextContent.split(/\|/g); // to sub strings, pipe delimited
		TAL = TextArray.length;
	}
	//' Per Action:
	switch (Action) {
		case 0: {
			// No action
			return;
		}
		case 1:
			// 2.1.1 Display text for choice selected from the Input Type
			// Run straight into action 3
		case 3: { //' Also handles Action = 1
			// 2.1.3 Display image, & any text, for choice selected
			// 2.1.3.1 Option General Text
			if (TAL > 0) {
				InfoContent += "<pre>" + TextArray[0] + "</pre>";
			}
			
			// 2.1.3.2 Get value of selected choice from Input Type
			Selection = ItemInput (OptionId, 4, 0);	// 4 = Value of selected Option
			// - Selection Heading
			InfoContent += "<h5>" + Selection + "</h5>\n";

			if (Action == 3) {
				// 2.1.3.3 image for Selected Choice
				// - Remove any trailing price e.g. £2.01, -$3.49, +£1
				Selection = Selection.replace(/\s[+-]?\W[\d.]*\d$/, "");
				// - Remove characters that are illegal in filenames from Selection
				Selection = Selection.replace(/\W+/g, "");
				
				// - What is the base image name? 
				FormElementObject = getObj ("Option" + OptionId + "addimage");
				
				// - Construct individual image name (Base name + Selection value + Base ext)
				// -- Separate file extension & name from base image name
				if (FormElementObject.value.length == 0) {
					Filename = "";
					FileExt = ".jpg";
				} else {
					// Filename can also include a sub-path e.g. cat/popup/colours.jpg
					Filename = FormElementObject.value.substring(0, FormElementObject.value.length - 4);
					FileExt = FormElementObject.value.substring(FormElementObject.value.length - 4);
				}
				InfoContent += "<img src='" + PopUpsPath + Filename + Selection + FileExt + "'>";
			}
			// 2.1.3.4 Text for selected choice
			var si = ItemInput (OptionId, 5, 0);	// 5 = index of selected choice
			if (TAL > si + 1) { // Any text? - use <p to allow flow
				InfoContent += "<p>" + TextArray[si + 1] + "</p>";
			}
			break;
		}
		case 4: {
			// 2.1.4 Display image for the Option as a whole + any text (as action 2)
			FormElementObject = getObj ("Option" + OptionId + "addimage");
			InfoContent += "<img src='" + PopUpsPath + FormElementObject.value + "'>";
			// Run straight into action 2
		}
		case 2: { //' Also handles Action = 4
			// 2.1.2 Display text for the Option as a whole
			InfoContent += "<pre>" + TextContent + "</pre>"; // use <pre so pseudo table can be presented
			break;
		}
		case 5: {
			// 2.1.5 Display a set of images, & any text, corresponding to each of the choices available in the Input Type and include a Select link to set the choice in the Input Type
			// - Read choices available from the combo box, etc.
			// -- Use ItemInput(OptionId, Mode, Option) for value, text & length
			
			// 2.1.5.2 Build content
			// 2.1.5.2.1 text describing option as a whole
			if (TAL) { // At least one piece of text - put at head - preformatted for pseudo tables
				InfoContent += "<pre>" + TextArray[0] + "</pre>";
			}
			// 2.1.5.2.2 image for option, text of option value, clickable image
			InfoContent += "<table cellpadding='3'>\n";
			var Length = ItemInput(OptionId, 0, 0);
			for (i = 0; i < Length; i++) { // for each selection
				// Divider
				InfoContent += "<tr><td colspan='2'><hr /></td></tr>\n";
				// Selection Title
				InfoContent += "<tr><td>";
				var Text = ItemInput(OptionId, 2, i); // Text of selection
				InfoContent += "<p>" + Text + "</p>";
				InfoContent += "</td><td>";
				// Selection Click
				InfoContent += "<p><a href='javascript:SetOptionValue(" + OptionId +
					", " + i + ")'>CLICK to select this option</a></p>";
				InfoContent += "</td></tr>\n";

				// Picture
				InfoContent += "<tr><td colspan='2'>";
				// 2.1.5.2.1 Remove characters from selection value that are:
					// - not alphanumeric in filenames from Selection
					// - any trailing price e.g. £62.91
				Selection = ItemInput(OptionId, 1, i);
				Selection = Selection.replace(/\s[+-]?\W[\d.]*\d$/, ""); // remove trailing price
				Selection = Selection.replace(/\W+/g, "");
			
				// 2.1.5.2.2 What is the base image name? 
				FormElementObject1 = getObj ("Option" + OptionId + "addimage");
			
				// 2.1.5.2.3 Construct individual image name (Base name + Selection value + Base ext)
				// - Separate file extension & name from base image name
				if (FormElementObject1.value.length == 0) {
					// No image name provided - use default
					Filename = "";
					FileExt = ".jpg";
				} else {
					// image name provided - assume Filename.FileExt (of 3 characters)
					Filename = FormElementObject1.value.substring(0, FormElementObject1.value.length - 4);
					FileExt = FormElementObject1.value.substring(FormElementObject1.value.length - 4);
				}
				InfoContent += "<img src='" + PopUpsPath + Filename + Selection + FileExt + "'>";
				// alert ("Debug: Final Image Name = " + Filename + Selection + FileExt); // Debug

				InfoContent += "</td></tr>\n";

				// Description
				if (TAL > 1 && TAL > i + 1 && TextArray[i + 1] != " ") { // Any text? - use <p for freeflow
					InfoContent += "<tr><td colspan='2'><p>";
					InfoContent += TextArray[i + 1];
					InfoContent += "</p></td></tr>";
				}
				
			}
			InfoContent += "</table>\n";
			break;
		}
		default: {
			// 2.1.7 Any other action
			alert ("There is a problem with the web page. The requested action (" + Action + ") for the Information button has not been understood. Try refreshing the web page. If the problem persists PLEASE let us know so we can fix it! - See Contact Us page - Tell us which product and which option (e.g. colour) you had the problem with. Thanks!");
			return;
		}
	}

	/* 2.2. Create Pop-up */
	var NewWin = window.open("", "NewWin", "height=550,width=400,scrollbars,resizable,dependent");
	NewWin.document.open();
	var Content = '<html>\n<';
	Content += 'header>\n';
	Content += '<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">\n';
	Content += '<title>Information</title>\n';
	Content += '<LINK REL=STYLESHEET TYPE="text/css" HREF="../html/style.css">\n';
	Content += '<LINK REL=STYLESHEET TYPE="text/css" HREF="../html/site.css">';
	if (Action == 5) { // Need helper function
		Content += '<scr'+'ipt language="javascript">function SetOptionValue (OptionId, Value) {\n';
		/* Whichever choice is selected on the pop-up, this function is called to set the Input Type on the parent to the same value. */
		Content += 'self.opener.ItemInput(OptionId, 3, Value);';
		Content += 'window.close();\n';
		Content += 'self.opener.OptionChange(OptionId);\n';
		Content += '}</scr'+'ipt>\n';
	}
	Content += '</header>\n';
	Content += '<body style="background: none">\n';
	// - Pop-up Heading = Product Summary
	Content += '<h3>' + window.document.MailOrderForm.Summary.value + '</h3>\n';
	// - Option Heading = Option Description
	var OptionDescription = getObj ("Option" + OptionId + "descr");
	Content += "<h4>" + OptionDescription.value + "</h4>\n";
	// - Content - built in 2.1
	Content += InfoContent;
	// - Close Window command
	Content += "<p align='center'><a href='javascript:window.close()'>Close window</a></p>";
	// - Complete HTML document & set focus
	Content += "</body>\n</html>\n";
	NewWin.document.write(Content);
	NewWin.document.close();
	NewWin.focus();
}


function Validate (cart) { // 3.
	/* Does any final checking of fields entered and then creates the variables needed by the shopping cart & submits */
	var cartName = "";
	if (typeof Cart == "undefined") {
		// old webs have the cart parameter value in the web page
		cartName = cart;
	} else {
		// new webs have an external var Cart
		cartName = Cart;
	}
	var HTMLHiddenInput = "";
	var FieldTranslate = new Array; // key = eShopHost field name; value = shopping cart name

	// 3.0a Check if problem browser (IE5 on Mac)
	var ProblemBrowser;
	var ua = navigator.userAgent;
	var IEoffset = ua.indexOf("MSIE");
	if ((IEoffset != -1) && (ua.indexOf("Mac") != -1)) { // IE on Mac
		var IEversion = parseFloat(ua.substring(IEoffset + 5, ua.indexOf(";", IEoffset)));
		if (IEversion >= 5 && IEversion < 6) { // and version 5
			ProblemBrowser = true;
		}
	}

	// 3.0b Prepare for output to Form
	var CartSubmit = getObj ("CartSubmit");
	if (!CartSubmit) {
		alert ("There is a problem with the web page. Element 'CartSubmit' does not exist. Please try again by refreshing the page. Do PLEASE let us know if the problem persists so we can fix it! Tell us the product you were trying to buy. See the 'Contact Us' page.");
		return;
	}

	// 3.1 Validate fields
	var ErrotText = "Sorry, please correct the following errors and then try again:";
	var OK = true;
	if (!Check ("quantity", "numeric")) { // default: integer
		var Otherdb = FieldValue ("Otherdb"); // eShopHost field containing keywords
		if (Otherdb.search(/\bQuantity\s*\(\s*Decimal\s*\)\b/i) == -1) {
			// no override - fail: non-integer
			ErrotText += "\n* Quantity can only be a number!";
			OK = false;
		} else {
			// override: Decimal
			if (!Check ("quantity", "decimal")) {
				ErrotText += "\n* Quantity can only be a decimal number!";
				OK = false;
			}
		}
	}
	// Add other checks here
	if (!OK) {
		alert (ErrotText);
		return false;
	}
	// Product Discount?
	if (ProductDiscount.active) { // object populated in A.5 at initialisation
		if (ProductDiscount.TextBox.value == ProductDiscount.code) {
			// Code matches
			ProductDiscount.TextBox.value = 
				//ProductDiscount.displayCode + " " + // suppress option value - not required
				ProductDiscount.code + " " + ProductDiscount.value;
		}
		else if (ProductDiscount.TextBox.value.length > 0) {
			alert ("Invalid Product Discount Code");
			return false;
		}
	}

	// 3.2 Translate fields for Shopping Cart
	switch (cartName) {
		case "Dansie": {
			// 3.2.Dansie.1 - OK fields - take no action
				// merchant -> merchant - id of merchant on eshophost's mall Dansie
				// add -> add = 1
				// quantity -> quantity
			if (!getObj("merchant") || !getObj("add") || !getObj("quantity")) {
				alert ("There is a problem with the web page. Verify that merchant, add and quantity exist.");
				return false;
			}

			// 3.2.Dansie.2 - 1 to 1 fields
			FieldTranslate ["Weight"] = "sh";
			FieldTranslate ["thumbnailname"] = "img";
			FieldTranslate ["Photoname"] = "img2";
			FieldTranslate ["Pricedb"] = "price";
			if (getObj("returnURL")) { // new eShop variable; B/Ws compatible
				FieldTranslate ["returnURL"] = "return";
			} // else use Dansie vars default
			if (ProblemBrowser) {
				W3CDOMfield("sh", FieldValue("Weight"));
				W3CDOMfield("img", FieldValue("thumbnailname"));
				W3CDOMfield("img2", FieldValue("Photoname"));
				W3CDOMfield("price", FieldValue("Pricedb"));
				if (getObj("returnURL")) { // new eShop variable; B/Ws compatible
					W3CDOMfield("return", FieldValue("returnURL"));
				}
			}
	
			// 3.2.Dansie.3 - Compound/ Translated Fields
			// - Dansie 'name' from eShopHost 'Productname' + 'Productcode'
			var ProdName = FieldValue ("Productname") + " - " + FieldValue ("Productcode");
			HTMLHiddenInput += MakeHiddenInput ("name", ProdName);
			if (ProblemBrowser) {
				W3CDOMfield("name", ProdName);
			}
			// - VAT/ Tax from eShopHost 'VATcode'
			if (FieldValue ("VATcode") == "0") {
				HTMLHiddenInput += MakeHiddenInput ("custom11", "#nontaxable#");
				HTMLHiddenInput += MakeHiddenInput ("custom12", "Zero Rated VAT");
				if (ProblemBrowser) {
					W3CDOMfield("custom11", "#nontaxable#");
					W3CDOMfield("custom12", "Zero Rated VAT");
				}
			}
			// - Otherdb Data
			var Otherdb = FieldValue ("Otherdb"); // eShopHost field containing keywords
			// -- No discount/ No deposit?
			if (Otherdb.indexOf ("nodeposit") != -1) {
				HTMLHiddenInput += MakeHiddenInput ("custom13", "#nodiscount#");
				HTMLHiddenInput += MakeHiddenInput
					("custom14", "Full amount payable on this item (deposits not accepted)");
				if (ProblemBrowser) {
					W3CDOMfield("custom13", "#nodiscount#");
					W3CDOMfield("custom14", "Full amount payable on this item (deposits not accepted)");
				}
			} else {
				if (Otherdb.indexOf ("nodiscount") != -1) {
					HTMLHiddenInput += MakeHiddenInput ("custom13", "#nodiscount#");
					HTMLHiddenInput += MakeHiddenInput ("custom14", "No discount on this item");
					if (ProblemBrowser) {
						W3CDOMfield("custom13", "#nodiscount#");
						W3CDOMfield("custom14", "No discount on this item");
					}
				}
			}
			// - No Shipping on this item?
			var Weight = FieldValue ("Weight");
			if ((Weight != "") && (Number(Weight) == 0) ) { // accept "0", "0.0", etc.
				HTMLHiddenInput += MakeHiddenInput ("custom15", "#noshipping#");
				if (ProblemBrowser) {
					W3CDOMfield("custom15", "#noshipping#");
				}
			}
			// - Go through the Options (select or Combo Boxes, etc.)
			var OptionCellObject;
			var si;
			var Selection;
			var DescriptionObject;
			for (m=OptionIdStart; m<=OptionIdMax; m=m+OptionIdStep) {
				OptionCellObject = getObj ("Option" + m + "dbcell");
				if (OptionCellObject.style.visibility == "hidden")  {
					continue;
				}
				Selection = ItemInput (m, 4, 0); // 4 = value of selected option
				DescriptionObject = getObj ("Option" + m + "descr");
				HTMLHiddenInput += MakeHiddenInput ("custom" + m, DescriptionObject.value + ": " + Selection);
				if (ProblemBrowser) {
					W3CDOMfield("custom" + m, DescriptionObject.value + ": " + Selection);
				}
			}
			break;
		}
		case "PayPal": {
			// 3.2.PayPal.1 - OK fields - take no action
				// add -> add = 1
				// quantity -> quantity

			// 3.2.PayPal.2 - 1 to 1 fields
//			FieldTranslate ["Weight"] = "weight"; // ! didn't work with this (when null value)
			FieldTranslate ["Productname"] = "item_name";
			FieldTranslate ["Productcode"] = "item_number";
			var returnURL = getObj("returnURL");
			if (returnURL ) { // new eShop variable; B/Ws compatible
				var returnURLvalue = returnURL.value;
				returnURLvalue = returnURLvalue.replace(/%3F/g, "?");
				returnURLvalue = returnURLvalue.replace(/%3D/g, "=");
				returnURLvalue = returnURLvalue.replace(/%26/g, "&");
				returnURL.value = decodeURI(returnURLvalue);
				FieldTranslate ["returnURL"] = "shopping_url";
			} else {
				HTMLHiddenInput += MakeHiddenInput ("shopping_url", window.location.href); // continue on same page
			}

			// 3.2.PayPal.3 - Constructed/ Compound/ Translated Fields
			HTMLHiddenInput += MakeHiddenInput ("cmd", "_cart"); // using cart
			HTMLHiddenInput += MakeHiddenInput ("currency_code", "GBP"); // no eshop var for currency
			HTMLHiddenInput += MakeHiddenInput ("lc", "GB"); // no eshop var for language
			var merchant = FieldValue ("merchant");
			HTMLHiddenInput += MakeHiddenInput ("business",
				merchant + ((merchant.search(/@/) == -1) ? ("@" + Domain) : "")); // permit format with domain name
			// - VAT/ Tax from eShopHost 'VATcode'
				// nothing implemented
			// - Otherdb Data
			// -- No discount/ No deposit?
				// nothing implemented
			// - No Shipping on this item?
				// nothing implemented
			// - Volume discount
			var Pricedb = calculateUnitPrice (); // (See H)
			HTMLHiddenInput += MakeHiddenInput ("amount", (Math.floor(Pricedb * 100))/100); // 2 decimal places
			// - Go through the Options (select or Combo Boxes, etc.)
			// -- Squeeze up to 10 options into the 2 available for PayPal
			var os0 = "", os1 = "", option = "";
			for (m = OptionIdStart; m <= OptionIdMax; m =m + OptionIdStep) {
				OptionCellObject = getObj ("Option" + m + "dbcell");
				if (OptionCellObject.style.visibility == "hidden")  {
					continue; // skip hidden (unused) options
				}
				Selection = ItemInput (m, 4, 0); // 4 = value of selected option
				DescriptionObject = getObj ("Option" + m + "descr");
				// -- Option descriptions & values
				option = DescriptionObject.value + ": " + Selection + "; ";
				if (os0.length + option.length <= 200) { // PayPal limit
					os0 += option;
				} else {
					os1 += option;
				}
			}
			// -- final options output. PayPal dislikes characters like £, so eliminate
			if (os0.length > 0) {
				HTMLHiddenInput += MakeHiddenInput ("on0", "Options");
				HTMLHiddenInput += MakeHiddenInput ("os0", os0.replace(/[^\w\s\-:;.,]/g, " "));
			}
			if (os1.length > 0) {
				HTMLHiddenInput += MakeHiddenInput ("on1", "More options");
				HTMLHiddenInput += MakeHiddenInput ("os1", os1.replace(/[^\w\s\-:;.,]/g, " "));
			}
			// - Set the Form action to https://www.paypal.com/cgi-bin/webscr: done in B (initialisation)
			break;
		}
		case "Mals-e": {
			alert ("Mals-e not implemented!");
			break;
		}
		default: {
			alert ("There is a problem with the web page. The requested shopping cart (" + Cart + ") has not been understood. Try refreshing the web page. If the problem persists PLEASE let us know so we can fix it! - See Contact Us page - Tell us which product you had the problem with. Thanks!");
			return false;
		}
	}

	// 3.3 Construct Hidden Fields for Shopping Cart
	for (x in FieldTranslate) {
		HTMLHiddenInput += MakeHiddenInput (FieldTranslate[x], FieldValue(x));
	}

	// 3.4 Write Hidden Fields for Cart
	if (!ProblemBrowser) {
		CartSubmit.innerHTML = HTMLHiddenInput;
	}
	// Debug
	//alert ("Debug CatItem 3.4. innerHTML=" + HTMLHiddenInput); // Debug
	//return confirm("Submit to cart?"); // Debug

	// 3.5 Submit to Shopping Cart
	return true; // Allow Submit to cart software
}


/* Internal Functions */
function  CatItemInitialise() { // A. Initialise
	/* Sets up Catalogue item page
	 - showing/hiding Buyer Options as required (empty/void & dependent)
	 - showing unit price - price with selected options (if OptionDB keyword is present)
	 - event handlers for Text Box Buyer Options
	 - Flash overlay processing
	 - Product Discount Code
	 - Checkout, Summary & Add to Basket
	 */
	var ItemInputObject;
	var PopUpInfoObject;
	var i, AddImage, AddText;
	
	// Debug
	//alert ("Debug. A. CatItemInitialise entered");

	// A.0 Get MailOrder Form
	var CatItemForm = getObj("MailOrderForm");
	if ( !CatItemForm ) {
		// No CatItem Form on this page (e.g. Home page or an Index Page), so just do the remaining initialisations
		// Debug
		//alert ("Debug. A.0 CatItemInitialise abort - No CatItem Form (\"MailOrderForm\")");
		InitialiseOther (); //' (see B)
		return;
	}

	// Initialise Cat Item Form Options
	// Pass 1: Hide empty Buyer Options & void Information buttons
	for (i=OptionIdStart; i<=OptionIdMax; i=i+OptionIdStep) {
		// A.1. Hide Option Elements if Options Array on Select/ Combo Box, etc. is empty
		ItemInputObject = getObj("Option" + i + "db");
		var Visibility;
		if (!ItemInputObject) {
			alert ("There is a problem with the web page. ItemInputObject: Option" + i + "db does not exist. Please try again by refreshing the page. Do PLEASE let us know if the problem persists so we can fix it! See the Contact Us page.");
			return;
		}
		if (ItemInputObject.type == "select-one" && ItemInputObject.options.length <= 0) {
			// Hide if Select element (iss 1.6) but no options within
			Visibility = "none"; // 'Display' property -> don't show space
			HiddenBuyerOptions[i] = true;
		}
		else { // Any other element type (e.g. text) or select with >= 1 option
			Visibility = "block"; // 'Display' property -> show normally
			HiddenBuyerOptions[i] = false;
		}
		ChangeVisibility ("Option" + i + "descrcell", Visibility);
		ChangeVisibility ("Option" + i + "dbcell", Visibility);
		ChangeVisibility ("Option" + i + "textcell", Visibility);
		
		// A.2. Hide button for 'more information' if optionxpopupinfo == 0 (No action)
		// or optionxpopupinfo == 4 (Extra image for that Option) but no image
		// or optionxpopupinfo == 2 (Extra text for that Option) but no text
		// or optionxpopupinfo == 5 (Set of images for that Option) but neither text nor image
		PopUpInfoObject = getObj("Option" + i + "popupinfo");
		if (!PopUpInfoObject) {
			alert ("There is a problem with the web page. ItemInputObject: Option" + i + "popupinfo does not exist. Please try again by refreshing the page. Do PLEASE let us know if the problem persists so we can fix it! See Contact Us page.");
			return;
		}
		AddImage = getObj("Option" + i + "addimage").value;
		AddText = getObj("Option" + i + "addtext").value;
		PopUpInfoType = PopUpInfoObject.value;
		if (PopUpInfoType == 0 ||
			((PopUpInfoType == 4)&&((AddImage == "")||(AddImage == " "))) ||
			((PopUpInfoType == 2)&&((AddText == "")||(AddText == " "))) ||
			((PopUpInfoType == 5)&&((AddText == "")||(AddText == " "))&&((AddImage == "")||(AddImage == " "))) )
		{
			Visibility = "none";
		}
		else {
			Visibility = "block"; // Display property to show normally
		}
		ChangeVisibility ("Option" + i + "popupinfobutton", Visibility);
	}

	// Pass 2: Set Dependent Buyer Options & set up handlers for text box Buyer Options
	for (i=OptionIdStart; i<=OptionIdMax; i=i+OptionIdStep) {
		// A.3a. DeleteDefaultTextFromTextOnFocusUntilChanged Preparation
		ItemInputObject = getObj("Option" + i + "db");
		var type = ItemInputObject.type;
		if (type == "text" || type == "textarea") {
			TextObjects[ItemInputObject.id] = {set: false}; // property: set (Boolean)
			ItemInputObject.onchange = textChange;
			ItemInputObject.onfocus = textFocus;
			ItemInputObject.onblur = textDefocus;
		}

		// A.3b. Hide, Show or Leave Option Elements according to options initially selected
		if (! HiddenBuyerOptions[i]) {
			// Buyer option is not currently hidden, so do not suppress a simulated change
			OptionChange(i); // (See 1.)
		}
	}

	// A.4. 'Flash' Overlay Processing
	// Potential enhancements: rhythmically: show/ hide, slide, change colour, filter (shadow, revealTrans).
	// Default rendition: CSS specified
	var Otherdb = FieldValue ("Otherdb"); // eShopHost field containing keywords
	var FlashOverlayRegEx = /FlashOverlay\(([^\)]*)\)/; // get all between ()
	var parameters, parameterRegEx, parameter;
	if (Otherdb.search (FlashOverlayRegEx) != -1) { // Feature required
		parameters = RegExp.$1;
		// A.4.1 Set up objects & initialise
		// Find main image from data source on Stacked Page (Dynamic eCatalogue matches)
		for (i = 0; i < document.images.length; i++) { 
			if (document.images[i].id.indexOf("DataField") != -1 ) {
				break; // found it
			}
		}
		var objImage = document.images[i];
		if (objImage) { // Got main product image, so continue
			var wImage = objImage.width;
			var objUnderlay = getObj ("UnderLay");
			objUnderlay.width = wImage;
			objUnderlay.style.zIndex = "2";
			var objFlash = getObj ("Flash");
			objFlash.style.width = wImage + "px";
			objFlash.style.zIndex = "3";
			// centre? or leave as left  objUnderlay.style.left, 10
			if (parseInt(getStyleProperty(objUnderlay, "left"), 10) != 0) { // not explicitly set to 0 for left
				objUnderlay.style.left = "50%";
				objFlash.style.left = "50%";
				objUnderlay.style.marginLeft = - wImage/2 + "px";
				objFlash.style.marginLeft = - wImage/2 + "px";
			}
	
			// A.4.2 Retrieve Data & set 'Flash' text
			// text parameter: text=[text to display]
			parameterRegEx = /text=\[([^\]]*)\]/; // get all between []
			if (parameters.search (parameterRegEx) != -1) { // parameter found
				parameter = RegExp.$1;
				objFlash.innerText = parameter;
			}
			// Any style parameters. Space delimited list DOMstylename=[value] e.g. fontWeight=[bold]
			parameters = parameters.replace (parameterRegEx, ""); // Get rid of the text parameter
			parameterRegEx = /[\w-]+=\[[^\]]*\]/g; // get remaining parameter-value pairs
			var parameterArray = parameters.match(parameterRegEx); // into an array
			if (parameterArray) { // .match returns null if no matches; instanceof Array: bad: IE5 mac
				parameterRegEx = /([\w-]+)=\[([^\]]*)\]/; // get parameter name & value between []
				for (i = 0; i < parameterArray.length; i++) {
					parameterArray[i].search (parameterRegEx);
					eval ("objFlash.style." + RegExp.$1 + " = '" + RegExp.$2 + "';");
					// objFlash.style.setProperty(RegExp.$1, RegExp.$2, ""); // does not work in IE6
				}
			}
		}
	} // end if - Flash feature required

	// A.5 Product Discount Code
	var pdKeyword = "productdiscount";
	var pdIndex = Otherdb.indexOf (pdKeyword);
	if (pdIndex != -1) { // Feature Required: productdiscount(pdData)
		var pdData = Otherdb.substring(pdIndex + pdKeyword.length + 1, 
			Otherdb.indexOf (")", pdIndex));
		var pdDataParts = pdData.split (/,\s*/); // comma separated list
		for (var i = 0; i < pdDataParts.length; i++) {
			var pdPair = pdDataParts[i].split("=");
			ProductDiscount[pdPair[0]] = pdPair[1];
		}
		if (ProductDiscount.type == 2) {
			// Discount type == 2 (Product Discount)
			ProductDiscount.active = true;
			// Get Text Box reference
			ProductDiscount.TextBox = getObj ("option" + ProductDiscount.option + "db");
			tbValue = ProductDiscount.TextBox.value;
			if (ProductDiscount.value) {
				// Text Box intially contains just displayCode
				ProductDiscount.displayCode = tbValue;
			} else {
				// Text Box initially contains: displayCode value
				var tbPair = tbValue.split (/\s+\+/); // 
				ProductDiscount.displayCode = tbPair[0];
				ProductDiscount.value = "+" + tbPair[1];
			}
			// Clear the text box
			ProductDiscount.TextBox.value = "";
		}
	}

	// A.6 Checkout & Summary settings.
	InitialiseOther ();

	// A.7 Enable 'Add to Basket'
	var buyButtons = ["AddToBasket1", "AddToBasket2"];
	for (var k = 0; k < buyButtons.length; k++) {
		var buyButton = getObj (buyButtons[k]);
		if (buyButton) {
			buyButton.style.visibility = "visible";
		}
	}
	
	// A.8 OtherdB items
	// A.8.1 TotalPrice required for this item?
	if (Otherdb.search(/\bTotalPrice\b/i) != -1) {
		// - yes
		var TotalPrice = getObj("TotalPrice");
		if (TotalPrice) {
			// - unit price row exists
			TotalPriceRequired = true;
			// -- get currency symbol
			var symbols = Otherdb.match(/\bTotalPrice\s*\((.*?)\)/i); // e.g. TotalPrice(£) = TotalPrice(&#163;)
			if (symbols == null || symbols[1].length == 0) {
				CurrencySymbol = "$";
			} else {
				CurrencySymbol = symbols[1];
			}
			// -- make visible
			TotalPrice.style.visibility = "visible";
			TotalPrice.style.display = "block";
			// -- set Event Handler
			var quantity = getObj ("quantity");
			if (quantity) {
				quantity.onchange = quantityChange;
			}
			// -- initial price calculation
			setTotalPrice ();
		}
	}
	// A.8.2 add others here 
	
} // end function A


function InitialiseOther () { // B.
	/* Changes checkout & basket summary suitable for chosen cart */
	// This section is also needed on all other pages that have checkout or summary
	// - generic return pages (paymentfail.html, paymentsuccess.html) required for carts: PayPal
	
	// B.1 Retrieve cart type, domain & merchant
	// - old: get from form's onsubmit
	//var formObj = getObj("MailOrderForm");
	//var Cart = String(formObj.getAttribute("onsubmit")); // onsubmit="return Validate('Dansie');
	//var CartStart = Cart.indexOf("'") + 1;
	//Cart = Cart.substring(CartStart, Cart.indexOf("'", CartStart));
	
	// - new: var Cart is defined externally using NOF variable
	var cartName = "";
	if (typeof Cart == "undefined") {
		// old webs use the standard cart
		cartName = "Dansie";
	} else {
		// new webs have an external var Cart
		cartName = Cart;
	}
	var merchantName = "";
	if (typeof Merchant == "undefined") {
		// old webs use the standard cart with merchant hardcoded
	} else {
		// new webs have an external var Merchant 
		merchantName = Merchant ;
	}
	var domainName = "";
	if (typeof Domain == "undefined") {
		// old webs don't use this
	} else {
		// new webs have an external var Domain 
		domainName = Domain ;
	}
	
	// B.2 Initialise per cart type
	var CO = getObj("CheckOutForm");
	switch (cartName) {
		case "PayPal": { // only 'new' webs support this cart
			// B.2.1 Checkout
			// a. Create new checkout link value
			var link = "https://www.paypal.com/cgi-bin/webscr?cmd=_cart&display=1&currency_code=GBP&lc=GB";
			link += "cancel_return=paymentfail.html&return=paymentsuccess.html&business=";
			link += merchantName;
			if (merchantName.search(/@/) == -1) link += "@" + domainName;
			// b. Find Checkout for standard cart
			// e.g. href="http://www.eshophost.co.uk/cgi-bin/dansie/cart.pl?merchant=..."
			// - Try FORM
			if (CO) {
				// Change Checkout URL
				CO.action = link;
			}
			// - Try links
			var links = window.document.body.getElementsByTagName("a");
			for (var i = 0; i < links.length; i++) {
				if (links[i].href.indexOf("http://www.eshophost.co.uk/cgi-bin/dansie/cart.pl?merchant=") == 0) {
					// Change Checkout URL
					links[i].href = link;
					break;
				}
			}
			// B.2.2 Summary - not supported by PayPal - leave disabled by default
			// B.2.3 Cat Item Form
			var formObj = getObj("MailOrderForm");
			if (formObj ) {
				formObj.action = "https://www.paypal.com/cgi-bin/webscr";
				//var onsubmit = String(formObj.getAttribute("onsubmit")); // onsubmit="return Validate('Dansie');
				// - can't use get/setAttribute with event handler - modify Validate() too
				formObj.onsubmit = Validate;
			}
			break;
		}
		case "Dansie": {
			// B.2.1 Checkout: 'old' webs: set correctly for Dansie by default; 'new' webs: merchant is missing
			if (merchantName.length > 0) {
				if (CO) {
					// Change Checkout URL: append merchant value
					CO.action += merchantName;
				}
			}
			// B.2.2 Summary - enable if present
			// <IFRAME frameborder='no'  Name='summary' width='161' height='85' align='center' src=''>
			var summaryObj = getObj("summary");
			if (!summaryObj) break;
			// - set src & visibility
			summaryObj.style.visibility = "visible";
			// src: 'old' webs: set correctly; 'new' webs: merchant is missing
			if (merchantName.length > 0) {
				summaryObj.src = "http://www.eshophost.co.uk/cgi-bin/dansie/cart.pl?summary|" + merchantName;
			}
			// B.2.3 Cat Item Form: set correctly for Dansie by default
			break;
		}
		default: {
			break;
		}
	}
	
	// B.3 Initialise any Search Box: correct action url for folder location of page
	var SearchForm = document.Search;
	if (SearchForm) {
		var path = "../cat/catindex.asp";
		if (location.href.search(/[^/]\/{1}[^/]+\/{1}.+/) == -1) {
			// Modify path as on the home page
			path = "./cat/catindex.asp";
		}
		// only do this for eCatalogue pages
		SearchForm.action = SearchForm.action.replace (/^catindex\.asp$/i, path);
	}
	
} // end function B


var Element;
function ChangeVisibility (ElementId, Visibility) { // C.
	/* For an element's id, changes display property as requested, plus visibility property. */
	/* Visibility = "block" | "none" */
	// C.1 Get Object
	Element = getObj(ElementId);
	if (!Element) {
		alert ("There is a problem with the web page. Element id " + ElementId + " does not exist. Please try again by refreshing the page. Do PLEASE let us know if the problem persists so we can fix it! See Contact Us page.");
		return;
	}
//alert("ChangeVisibility. ElementId: " + ElementId + ". Visibility: " + Visibility);

	// C.3 Change visibility
	var visibilityStyle;
	if (Visibility == "block") {
		// Show in normal display
		visibilityStyle = "visible";
	} else {
		// Contract space used
		visibilityStyle = "hidden";
	}
	Element.style.visibility = visibilityStyle;

	// C.3 Change Display Property
	if ((Visibility == "block") && (Element.tagName == "IMG")) {
		// - correct Display property
		Visibility = "inline";
		// - add float property -> No still goes on next line unles img is 1st in HTML
		//Element.style.cssFloat = "right";
		//Element.style.styleFloat = "right";
	}
	Element.style.display = Visibility;
}


function ItemInput (OptionId, Mode, Option, setValue) { // D.
	/*	OptionID - Buyer Option 1 .. 10
		Mode:
			0 = returns quantity of options
			1 = returns value of specified option - uses 3rd parameter
			2 = returns text of specified option - uses 3rd parameter
			3 = SETS specified option as 'selected' - uses 3rd parameter
			4 = returns value of the selected Option
			5 = index of selected option
			6 = SETS value (4th parameter) of specified option (3rd parameter)
			7 = Reserved - not implemented - SETS text (4th parameter) of specified option (3rd parameter)
		Option - 0 to < Length
		setValue - string
	*/
	var ItemInputObject = getObj("Option" + OptionId + "db");
	if (! ItemInputObject) {
		alert ("Invalid OptionId for: ItemInput (OptionId: " + OptionId + ", Mode, Option, setValue)");
		return 0;
	}
	var type = ItemInputObject.type;
	var si, Selection, Length, Value, Text;
	switch (type) {	// of the item's input element
		case "select-one": { // Combo box (select element) - <select .. <option ..
			if (Mode == 3) { // 3 = sets specified option as selected
				ItemInputObject.options[Option].selected = true;
				break;
			}
			if (Mode == 6) { // 6 = SETS value (4th parameter) of specified option (3rd parameter)
				ItemInputObject.options[Option].value = setValue;
				break;
			}
			si = ItemInputObject.selectedIndex;
			if (si < 0) { // if user has not made a selection
				si = 0; // set selection to 1st item
			}
			Length = ItemInputObject.options.length;
			if (Length > 0) { // in case no options in select tag
				Selection = ItemInputObject.options[si].value;
			} else {
				Selection = "";
			}
			// Only when Option is defined & in case of attempt to reference outside the available options
			if ((typeof Option == "number") && (Option < Length) && (Option >= 0)) { 
				Value = ItemInputObject.options[Option].value;
				Text = ItemInputObject.options[Option].text;
			}
			break;
		}
		case "radio": {
			var radioGrpName = ItemInputObject.name;
			//var radioGrp = eval("document." + ItemInputObject.form.name + "." + radioGrpName);
			var radioGrp = ItemInputObject.form.elements[radioGrpName];
			if ("length" in radioGrp) {
				// radio group consists of > 1 radio buttons & we can use radioGrp[x]
				if (Mode == 3) {      // 3 = sets specified option as selected
					radioGrp[Option].checked = true;
					break;
				}
				if (Mode == 6) {      // 6 = SETS value (4th parameter) of specified option (3rd parameter)
					radioGrp[Option].value = setValue;
					break;
				}
				Length = radioGrp.length;
				for (var i = 0; i < Length; i++) {
					if (radioGrp[i].checked) {
						si = i;
						Selection = radioGrp[i].value;
						break;
					}
				}
				// Only when Option is defined & in case of attempt to reference outside the available options
				if ((typeof Option == "number") && (Option < Length) && (Option >= 0)) {
					Value = radioGrp[Option].value;
					Text = Value;
				}
			} else {
				// only one radio button & radioGrp is the sole radio button
				if (Mode == 3) {      // 3 = sets specified option as selected
					ItemInputObject.checked = true;
					break;
				}
				if (Mode == 6) {      // 6 = SETS value (4th parameter) of specified option (3rd parameter)
					ItemInputObject.value = setValue;
					break;
				}
				Length = 1;
				Selection = ItemInputObject.value;
				Value = Selection;
				Text = Value;
			}
			break;
		}
		case "checkbox": {
			Value = ItemInputObject.value;
			Length = 2;
			if (Mode == 3) { // 3 = sets as selected
				ItemInputObject.checked = true;
				break;
			}
			if (Mode == 6) { // 6 = SETS value (4th parameter)
				ItemInputObject.value = setValue;
				break;
			}
			if (ItemInputObject.checked) {
				si = 1;
				Selection = Value;
			} else {
				si = 0;
				Selection = "No";
			}
			break;
		}
		default: { // "text" or "textarea"; ignore Mode == 3
			if (Mode == 6) { // 6 = SETS value (4th parameter)
				ItemInputObject.value = setValue;
				break;
			}
			si = 0;
			Length = 1;
			Selection = ItemInputObject.value;
			Value = Selection;
			Text = Value;
			break;
		}
	}
	switch (Mode) {
		// When function returns a value. Return at least an empty string
		case 0: { // 0 = quantity of options
			return Length;
		}
		case 1: { // 1 = value of specified option
			return (Value) ? Value : "";
		}
		case 2: { // 2 = text of specified option
			return (Text) ? Text : "";
		}
		case 5: { // 5 = index of specified option
			return si;
		}
		case 4: { // 4 = Value of selected Option
			return (Selection) ? Selection : "";
		}
		default: { // no value returned e.g. when setting values
			return "";
		}
	}
}


function W3CDOMfield (name, value) { // E. Create W3C DOM hidden input element & append
	var objEl = document.createElement("input"); // > IE4 OK for <input>
	objEl.setAttribute("name",name); // IE4 OK
	objEl.setAttribute("value",value);
	CartSubmit.appendChild(objEl); // IE5 OK
}


function textChange () { // F. DeleteDefaultTextFromTextOnFocusUntilChanged
	TextObjects[this.id].set = true;
}
function textFocus () {
	if (!TextObjects[this.id].set) {
		this.value = "";
	}
}
function textDefocus () {
	if (!TextObjects[this.id].set) {
		this.value = this.defaultValue;
	}
}


function setTotalPrice () { // G. - sets value in revealed Total Price Box
	// Retrieve Unit Price Box object
	var TotalPriceBox = getObj ("TotalPriceBox");
	if (!TotalPriceBox) {
		return;
	}
	var quantity = Number(FieldValue ("quantity"));
	if (isNaN(quantity)) {
		quantity = 0;
	}
	var Price = quantity * calculateUnitPrice(); // (see H)
	TotalPriceBox.value = CurrencySymbol + Price.toFixed(2); // 2 decimal places
}


function calculateUnitPrice () { // H. for unit price using selected options
	// H.1 Volume discount
	var Pricedb = FieldValue ("Pricedb");
	if (Pricedb.indexOf (":") != -1) {
		// e.g. 3.00:1:2.50:10:2.00:20 (colon delimiter) - for 1 or more 3.00; for 10 or more 2.50; etc.
		var volumeDiscounts = Pricedb.split(":"); // array
		var quantity = Number(FieldValue ("quantity"));
		if (isNaN(quantity)) {
			quantity = 0;
		}
		// N.B. quantity is now parsed as a number (may contain letters after the digit(s)), but can be decimal
		for (var d = volumeDiscounts.length - 2; d >= 0 ; d -= 2) {
			if (quantity >= volumeDiscounts[d+1]) {
				Pricedb = volumeDiscounts[d];
				break;
			}
		}
	}
	Pricedb = Number(Pricedb);
	if (!Pricedb) {
		Pricedb = 0;
	}

	// H.2 Go through the Options (select or Combo Boxes, etc.)
	var delta = 0;
	for (var m = OptionIdStart; m <= OptionIdMax; m = m + OptionIdStep) {
		OptionCellObject = getObj ("Option" + m + "dbcell");
		if (OptionCellObject.style.visibility == "hidden")  {
			continue; // skip hidden (unused) options
		}
		Selection = ItemInput (m, 4, 0); // 4 = value of selected option
		// -- Cost variations in Option's selection e.g. "no loops +£-1.23"
		var variation = null;
		variation = Selection.match (/\s+(\+?)\W(-?[0-9]+\.[0-9]{2})\s*$/);
		if (variation != null) {
			if (variation[1] == "+") {
				// delta variation - so add it
				delta += Number(variation[2]);
			} else {
				// new absolute price
				Pricedb = Number(variation[2]);
			}
		}
	}
	// -- final unit price
	Pricedb += delta;
	return Pricedb;
}


function quantityChange () { // I. event handler for quantity
	if (TotalPriceRequired) {
		setTotalPrice(); // (See G)
	}
}


/* Immediate Code */
if (typeof Inits == "object") {
	// Multiple initialisations exist using old eShopHost technique - push this onto the array
	Inits.push (CatItemInitialise); // (See A)
} else {
	// Add Event Handler to the Window object
	if (window.addEventListener) {
		// DOM Level compliant
		window.addEventListener ("load", CatItemInitialise, false);
	} else if (window.attachEvent) {
		// IE4
		window.attachEvent ("onload", CatItemInitialise);
	} else {
		// Help! - just set it! (overwrites anything else!)
		window.onload = CatItemInitialise;
	}
}

