/*jslint browser: true, nomen: true, passfail: false, undef: true */
/*extern CPLLoadingIndicator, ActiveXObject */

/******************************************************************************
ComprehensiveProductList is a class encapsulating functionality related to
retrieving category lists, product information, and populating an HTML page
with it.

HOW TO USE

Somewhere in the body...
	<div id="categories">
		<!-- ComprehensiveProductList object will populate here. -->
	</div>

And somewhere down below, preferably right before the closing </body> tag...
	<script type="text/javascript">
		var categories = document.getElementById("categories");
		if (categories) {
			var cpl = new ComprehensiveProductList();

			cpl.add_category({
				name: "Refrigerators"
			});
			cpl.add_subcategory({
				id: "MON0001", name: "Side-by-Sides"
			});
			cpl.add_subcategory({
				id: "MON0002", name: "Built-in Bottom Freezer"
			});
			// ...

			cpl.add_category({
				name: "Cooking Products"
			});
			// ...

			cpl.populate_html(categories);
		}
	</script>
******************************************************************************/

// @constructor
function ComprehensiveProductList () {
	this.populated = false;
	this.categories = [];

	// defaults that can be changed
	this.default_ApplProducts_siteid = "MON2";
	this.default_ProdContent_siteid = "Monogram";
	this.DIAGNOSTICS = {};

	this.loading_indicator = new CPLLoadingIndicator();

	// If you need to do any of the following, please do so
	// after creating your ComprehensiveProductList object
	// and before calling any other methods on it:
	// - setting any properties in DIAGNOSTICS
	// - changing any of the defaults listed here
	// - changing any of the defaults in the loading_indicator
}

/*****************************************************************************/

// Function to build a request URL for product info.
// @param id - a string such as "MON0012"
// @returns the request URL for pulling product info
ComprehensiveProductList.prototype.subcategory_request_url =
	function (id)
{
	//alert("id>>>"+id);
	if (this.DIAGNOSTICS.XML_SOURCE_URL) {
		return (this.DIAGNOSTICS.XML_SOURCE_URL.
			replace(/\{CATEGORY\}/, encodeURIComponent(id)));
	}
	else {
		return "/ApplProducts/Dispatcher?REQUEST=FORTHETRADERESULT" +
			"&TYPE=XML" +
			"&CATEGORY=" + encodeURIComponent(id);
	}
};

/*****************************************************************************/

// Only toggles the status of the category toggle links.  Used by
// toggle_category and change_category_toggle_link_status.
// @param link - a DOM reference to an <a> element.
// @param category - a reference to a category object from this.categories.
// @param flag - false or true to specify the final state, or null to toggle.
// @returns true if the final state of the link is expanded.
// @returns false if the final state of the link is collapsed.
// @returns null if it cannot change the state of the link.
ComprehensiveProductList.prototype.toggle_category_link =
	function (link, category, flag)
{
	if (flag !== true && flag !== false) {
		if (/\bexpanded\b/.test(link.className)) {
			flag = false;
		}
		else if (/\bcollapsed\b/.test(link.className)) {
			flag = true;
		}
		else {
			return null; // for toggle_category() sanity check
		}
	}

	if (flag) {
		link.className =
			link.className.replace(/\bcollapsed\b/, "expanded");
		link.innerHTML = "Collapse all";
	}
	else {
		link.className =
			link.className.replace(/\bexpanded\b/, "collapsed");
		link.innerHTML = "Expand all";
	}
	return flag;
};

// This method actually shows/hides all the subcategories.
// @param link - a DOM reference to an <a> element.
// @param category - a reference to a category object from this.categories.
// @param flag - false or true to specify the final state of the toggle.
//               or null or unspecified to toggle.
// @param after_toggle - a function that will be called after the
//               job is finished.  *may* be called asynchronously.
ComprehensiveProductList.prototype.toggle_category =
	function (link, category, 
		    /* optional */ flag,
		    /* optional */ after_toggle)
{
	var subcategory;
	var i;
	
	flag = this.toggle_category_link(link, category, flag);
	if (flag === null) {
		return null;	// sanity check
	}

	var count = category.subcategories.length;
	var after_each_toggle = null;
	if (after_toggle) {
		after_each_toggle = function () { // *may* be called asynch'ly.
			--count;
			if (count === 0) {
				after_toggle(flag);
			}			
		};
	}
	//alert("category is >>>"+category.id);
	for (i = 0; i < category.subcategories.length; ++i) {
		subcategory = category.subcategories[i];
		this.toggle_subcategory(subcategory.toggle_link,
					subcategory, flag, after_each_toggle);
	}
};

// Only toggles the status of subcategory toggle links.
// @param link - a DOM reference to an <a> element.
// @param subcategory - a reference to a subcategory object.
// @param flag - false or true to specify the final state of the toggle,
//               or null or unspecified to toggle.
// @returns true if the final state of the link is expanded.
// @returns false if the final state of the link is collapsed.
// @returns null if it cannot change the state of the link.
ComprehensiveProductList.prototype.toggle_subcategory_link =
	function (link, subcategory, flag)
{
	if (flag !== true && flag !== false) {
		if (/\bexpanded\b/.test(link.className)) {
			flag = false;
		}
		else if (/\bcollapsed\b/.test(link.className)) {
			flag = true;
		}
		else {
			return null; // for toggle_subcategory() sanity check
		}
	}

	if (flag) {
		link.className =
			link.className.replace(/\bcollapsed\b/, "expanded");
	}
	else {
		link.className =
			link.className.replace(/\bexpanded\b/, "collapsed");
	}
	return flag;
};

// Toggles the status of subcategory toggle links.
// Also toggles the visibility of each subcategory.
// @param link - a DOM reference to an <a> element.
// @param subcategory - a reference to a subcategory object.
// @param flag - false or true to specify the final state of the toggle,
//               or null or unspecified to toggle.
// @param after_toggle - a function that will be called after the
//               job is finished.  *may* be called asynchronously.
ComprehensiveProductList.prototype.toggle_subcategory =
	function (link, subcategory,
		    /* optional */ flag,
		    /* optional */ after_toggle)
{
	flag = this.toggle_subcategory_link(link, subcategory, flag);
	if (flag === null) {
		return null; // sanity check
	}

	var div = subcategory.subcategory_div;
	if (!div) {
		return null; // sanity check
	}

	var that = this; // in below functions, "this" refers to the window.
	var toggle_category = function () {
		var category  =
			that.categories[subcategory.parent_category_index];
		that.change_category_toggle_link_status(category);
	};
	var on_populate = function () {
		that.append_product_list(div, subcategory);
		div.style.display = "block";
		subcategory.expanded = true;
		toggle_category();
		if (after_toggle) {
			after_toggle(true);
		}
	};

	if (flag) {
		this.populate_subcategory(subcategory, on_populate);
	} else {
		div.style.display = "none";
		subcategory.expanded = false;
		toggle_category();
		if (after_toggle) {
			after_toggle(false);
		}
	}
};

// If all subcategories within a category are expanded, make sure the link is
// set to "Collapse all".  Otherwise make sure it's set to "Expand all".
ComprehensiveProductList.prototype.change_category_toggle_link_status =
	function (category)
{
	var subcategories = category.subcategories;
	var all_subcategories_expanded = true;
	//alert("category>>>"+category.name);
	for (var i = 0; i < subcategories.length; ++i) {
	//alert("subcategory is :::"+subcategories[i].name);
	subcategories[i].name = category.name + '_' + subcategories[i].name;
	//alert("subcategories[i].name"+subcategories[i].name);
		if (!subcategories[i].expanded) {
			all_subcategories_expanded = false;
			break;
		}
	}
	this.toggle_category_link(category.toggle_link, category,
				  all_subcategories_expanded);
};

// Appends a category to the list of main product categories.
// @param o - an object containing a name property.
ComprehensiveProductList.prototype.add_category =
	function (o)
{
	this.current_category = o;
	o.subcategories = [];
	this.categories.push(this.current_category);
};

// Appends a subcategory to the list of subcategories under the category
// most recently populated with add_category().
// @param o - an object containing id and name properties.
ComprehensiveProductList.prototype.add_subcategory =
	function (o)
{
	if (!this.current_category) {
		window.alert("You must call cpl.add_category() before" +
			     "calling cpl.add_subcategory().");
		return;
	}
	this.current_category.subcategories.push(o);
};

// After all categories and subcategories are populated using add_category()
// and add_subcategory(), this method needs to be called before further
// processing.
ComprehensiveProductList.prototype.finalize_categories =
	function ()
{
	// Populate each subcategory with references back up to its parent
	// category.  References are by index instead of by direct object
	// reference, since the latter would cause our data structure to
	// be circular.
	var c;
	var j;
	var sc;

	for (var i = 0; i < this.categories.length; ++i) {
		c = this.categories[i];
		for (j = 0; j < c.subcategories.length; ++j) {
			sc = c.subcategories[j];
			sc.parent_category_index = i;
		}
	}

	this.populated = true;
};

// Populates an entire subcategory of products from an XML response.
// @param subcategory - one of the objects inserted into this.categories
//        by the add_subcategory() method.
// @param request - an XMLHttpRequest or compatible object that has been
//        acted on (i.e., is ready with a response).
ComprehensiveProductList.prototype.populate_subcategory_from_xml =
	function (subcategory, request)
{
	var xmldoc = request.responseXML;

	// Workaround for MSIE issues loading local XML documents.
	// Unfortunately, only ASCII encoding is safe.
	if (!xmldoc.documentElement) {
		xmldoc = new ActiveXObject("MSXML2.DOMDocument");
		xmldoc.loadXML(request.responseText);
	}
	
	// Utility function that accepts a node, find the first element inside
	// of it named by tagname, and returns the contents of THAT NODE's
	// first child node, assuming that child is a text node.
	//
	// example: <foo><bar>5</bar></foo>
	//          assuming fooNode points to the <foo> node,
	//          get_xml_text(fooNode, "bar") returns "5".
	var get_xml_text = function (node, tagname) {
		try {
			return (node.getElementsByTagName(tagname)[0].
				childNodes[0].data);
		}
		catch (e) {
			return null;
		}
	};

	var products = []; // will be populated into the subcategory.

	var model_dom;
	var product;
	var photogallery_nodes;
	var node;
	var pg_siteid;
	var spec_siteid;
	var spec_tabid;
	var productspec_nodes;
	var specs_link;
	var populate_link;
	
	var models = xmldoc.getElementsByTagName("Model");

	for (var i = 0; i < models.length; ++i) {
		model_dom = models[i];

		product = {}; // will be populated into products.
		
		product.sku  = get_xml_text(model_dom, "SKU");
		product.name = get_xml_text(model_dom, "Description");
		
		photogallery_nodes =
			model_dom.getElementsByTagName("PHOTOGALLERY");
		if (photogallery_nodes.length) {
			// indicates that a photo gallery exists.

			node = photogallery_nodes[0];
			pg_siteid = get_xml_text(node, "SITEID");
			if (pg_siteid === null) {
				pg_siteid = this.default_ProdContent_siteid;
			}
			
			product.photo_gallery =
			"/ApplProducts/Dispatcher?REQUEST=PHOTOGALLERY&PRODUCTCODE="+encodeURIComponent(product.sku) + "&SITEID=" + encodeURIComponent(pg_siteid);

			/*	"/ProdContent/Dispatcher" +
				"?REQUEST=PHOTOGALLERY" +
				"&SKU=" + encodeURIComponent(product.sku) +
				"&SITEID=" + encodeURIComponent(pg_siteid);*/
		}

		spec_siteid = null;
		spec_tabid = null;
		productspec_nodes =
			model_dom.getElementsByTagName("PRODUCTSPEC");
		if (productspec_nodes.length) {
			node = productspec_nodes[0];
			spec_siteid = get_xml_text(node, "SITEID");
			spec_tabid  = get_xml_text(node, "TABID");
		}
		if (spec_siteid === null) {
			spec_siteid = this.default_ApplProducts_siteid;
		}
		if (spec_tabid === null) {
			spec_tabid = 1;	// fallback
		}

		specs_link =
			"/ApplProducts/Dispatcher?REQUEST=SPECPAGE" +
			"&SKU=" + encodeURIComponent(product.sku) +
			"&SITEID=" + encodeURIComponent(spec_siteid) +
			"&TABID=" + encodeURIComponent(spec_tabid);


		
		product.specifications = [{
			link: specs_link,
			title: "Product Specs"
		}];
		


		
		// Populates a link into the data structure for the product.
		// @param o, an object containing the following properties:
		//   tagname - the name of a tag within the <Model>
		//     element that contains a METHODID and DOC.
		//   propname - "specifications" or "installation" or
		//     "cad" or "custom_options".  governs which column on
		//     the page the link is inserted to.
		//   doctitle - used for the link title
		populate_link = function (o) {
			var tagname  = o.tagname;
			var propname = o.propname;
			var doctitle = o.doctitle;

			var el = model_dom.getElementsByTagName(tagname);
			if (!el.length) {
				return;
			}

			var pdfname = get_xml_text(el[0], "NAME");
			var doc      = get_xml_text(el[0], "DOC");
			
			if (!product[propname]) {
				product[propname] = [];
			}
			var doclink;
			if (doc=="Products.CAD Drawings")
			{
			 doclink  ="http://products.geappliances.com/MarketingObjectRetrieval/Dispatcher?RequestType=Binary&RecId=" + encodeURIComponent(pdfname);
			}
			else
			{
				 doclink  =
			"http://products.geappliances.com/MarketingObjectRetrieval/Dispatcher?RequestType=PDF&Name=" + encodeURIComponent(pdfname);
			}
			

			var item = {
				link: doclink,
				title: doctitle
			};

			if ("target" in o) {
				item.target = o.target;
			}

			product[propname].push(item);
		};

		populate_link({
			tagname:  "Installation_Instructions",
			propname: "installation",
			doctitle: "Installation Instructions",
			target:   "_blank"
		});
		populate_link({
			tagname:  "Use_and_Care_Manual",
			propname: "specifications",
			doctitle: "Use & Care Manual",
			target:   "_blank"
		});
		populate_link({
			tagname:  "Energy_Guide",
			propname: "specifications",
			doctitle: "Energy Guide",
			target:   "_blank"
		});
		populate_link({
			tagname:  "QuickSpecs",
			propname: "installation",
			doctitle: "Quick Specs",
			target:   "_blank"
		});
		populate_link({
			tagname:  "CADDRAWING",
			propname: "cad",
			doctitle: "CAD Download",
			target:   "_blank"
		});
		
		products.push(product);
	}

	subcategory.products = products;
};

// Utility method to create a new XMLHttpRequest object or something
// that works like it in Microsoft browsers.
ComprehensiveProductList.prototype.new_xml_request =
	function ()
{
	var request;

	// For Safari, Firefox, and other non-MS browsers
	try { request = new XMLHttpRequest();
	      if (request) { return request; } }
	catch (e) { }
	
	// For Internet Explorer on Windows
	try { request = new ActiveXObject("Msxml2.XMLHTTP"); 
	      if (request) { return request; } }
	catch (e) { }
	try { request = new ActiveXObject("Microsoft.XMLHTTP");
	      if (request) { return request; } }
	catch (e) { }

	window.alert("We're sorry, but this page will not work with your " +
		     " web browser.  It is known to work with the latest " +
		     " versions of Mozilla Firefox, Microsoft Internet " +
		     " Explorer, Opera, and Safari.  Please try one of them.");
	
	return null;
};

// Fetches a list of subcategories if it has not yet been fetched.
ComprehensiveProductList.prototype.populate_subcategory =
	function (subcategory, on_populate)
{
	if (subcategory.populated) {
		on_populate();
		return;
	}

	var that = this; // because in xxx.yyy functions defined here,
	                 // "this" refers to xxx.

	if (this.DIAGNOSTICS.USE_TEST_DATA) {
		if (this.DIAGNOSTICS.FAKE_REQUEST_DELAY_MS) {
			this.loading_indicator.increment();
			window.setTimeout(function () {
				that.populate_subcategory_FROM_TEST_DATA(
					subcategory);
				on_populate();
				that.loading_indicator.decrement();
			}, this.DIAGNOSTICS.FAKE_REQUEST_DELAY_MS);
			return;
		} else {
			that.populate_subcategory_FROM_TEST_DATA(
				subcategory);
			on_populate();
		}
		return;
	}
	
	var id = subcategory.id;
	//var fullid = category.name + "_" + id;
	var url = this.subcategory_request_url(id);
	//alert(url);
	//alert("");

	this.loading_indicator.increment();
	var request = this.new_xml_request();
	
	var on_success = function () {
		that.populate_subcategory_from_xml(subcategory, request);
		subcategory.populated = true;
		on_populate();
	};
	var on_failure = function () {
		window.alert("We're sorry, we encountered a problem while " +
			     "attempting to retrieve product information.  " +
			     "Please try again later.");
	};
	request.onreadystatechange = function () {
		if (request.readyState == 4 /* finished */) {
			if ((request.status == 200) || 
			    (request.status === 0) /* local requests */) {
				on_success();
			}
			else {
				on_failure();
			}
			that.loading_indicator.decrement();
		}
	};

	request.open("GET", url);
	request.send(null);
};

/*****************************************************************************/

// The main method to populate the main placeholder with a list of product
// categories and subcategories and toggle links and subcategory content
// placeholders.
// @param element - a DOM reference to a block-level element, normally a blank
//                  <div> somewhere in the HTML page before this is called.
ComprehensiveProductList.prototype.populate_html =
	function (element)
{
	var category;
	var li_container; // loading indicator

	if (!this.loading_indicator.append_to_element) {
		li_container = document.createElement("div");
		element.appendChild(li_container);
		this.loading_indicator.append_to_element = li_container;
	}

	for (var i = 0; i < this.categories.length; ++i) {
		category = this.categories[i];
		this.append_category_heading(element, category);
		this.append_category_placeholder(element, category);
	}
	this.load_states();
};

// Populates the document with each category's main heading (and toggle link).
// @param element - a DOM reference to a block-level element, normally the
//                  same <div> as passed to the populate_html() method.
ComprehensiveProductList.prototype.append_category_heading  =
	function (element, category)
{
	var after_toggle;

	var document = element.ownerDocument;
	
	var category_heading = document.createElement("div");
	category_heading.className = "category_heading";
	
	var toggle_link_container = document.createElement("p");
	toggle_link_container.className = "toggle_link_container";
	
	var a = document.createElement("a");
	a.className = "collapsed";
	a.href = "#"; // want css rules on links to work right? you need this.
	var that = this; // in the onclick, "this" becomes the link object.
	a.onclick = function () {
		after_toggle = function () { // *may* be called asynch'ly.
			that.save_states();
		};
		that.toggle_category(a, category, null, after_toggle);
		return false;
	};
	a.appendChild(document.createTextNode("Expand all"));
	toggle_link_container.appendChild(a);
	category.toggle_link = a;
	
	category_heading.appendChild(toggle_link_container);
		
	var h3 = document.createElement("h3");
	h3.className = "category";
	h3.appendChild(document.createTextNode(category.name));
	
	category_heading.appendChild(h3);

	element.appendChild(category_heading);
};

// Populate the document with a placeholder that will contain all the
// subcategories' headings and placeholders for product information.
// @param element - a DOM reference to a block-level element, normally the
//                  same <div> as passed to the populate_html() method.
ComprehensiveProductList.prototype.append_category_placeholder =
	function (element, category)
{
	var subcategory;

	var document = element.ownerDocument;
	var category_div = document.createElement("div");
	category_div.className = "subcategories";
	
	for (var i = 0; i < category.subcategories.length; ++i) {
		subcategory = category.subcategories[i];
		this.append_subcategory_heading(category_div, subcategory);
		this.append_subcategory_placeholder(category_div, subcategory);
	}

	element.appendChild(category_div);
};

// Populate the document with a subcategory's heading (and toggle link).
// @param element - a DOM reference to a block-level element, normally a <div>
//                  created by the append_category_placeholder() method.
ComprehensiveProductList.prototype.append_subcategory_heading  =
	function (element, subcategory)
{
	var document = element.ownerDocument;
	
	var subcategory_heading = document.createElement("h4");
	subcategory_heading.className = "subcategory_heading";

	var a = document.createElement("a");
	a.className = "collapsed";
	a.href = "#"; // want css rules on links to work right? you need this.
	var that = this; // in the onclick, "this" becomes the link object.
	a.onclick = function () {
		var after_toggle = function () { // *may* be called asynch'ly.
			that.save_states();
		};
		that.toggle_subcategory(a, subcategory, null, after_toggle);
		return false;
	};
	a.appendChild(document.createTextNode(subcategory.name));
	subcategory_heading.appendChild(a);
	subcategory.toggle_link = a;
	
	element.appendChild(subcategory_heading);
};

// Populate the document with a placeholder which will contain the list of
// products under a subcategory when that product list is requested.
// @param element - a DOM reference to a block-level element, normally a <div>
//                  created by the append_category_placeholder() method.
ComprehensiveProductList.prototype.append_subcategory_placeholder =
	function (element, subcategory)
{
	var document = element.ownerDocument;

	var subcategory_div = document.createElement("div");
	subcategory_div.className = "subcategory";
	subcategory_div.populated = false;
	subcategory_div.style.display = "none";
	element.appendChild(subcategory_div);

	subcategory.subcategory_div = subcategory_div;
};

ComprehensiveProductList.PRODUCT_LIST_HEADINGS = [
	"\xa0",	// &nbsp;
	"Model No.",
	"Specifications",
	"Installation",
	"CAD"
];

// Append a <ul> as a child of the specified element.
// list is an array of objects, each with link and title properties.
// @param element - a DOM reference to an element in which to append the <ul>.
//                  Normally a <td> created by append_product_info().
// @param list - an array containing a series of objects, each of which
//               contains the following properties:
//               - link, a URL
//               - title, the link text
//               - target, an optional link target such as "_blank"
ComprehensiveProductList.prototype.append_list_of_links =
	function (element, list)
{
	if (!list) {
		return;
	}
	if (!list.length) {
		return;
	}
	var document = element.ownerDocument;
	var ul = document.createElement("ul");
	var item;
	var li;
	var a;
	for (var i = 0; i < list.length; ++i) {
		item = list[i];
		li = document.createElement("li");
		a = document.createElement("a");
		a.href = item.link;
		if ("target" in item) {
			a.target = item.target;
		}
		a.appendChild(document.createTextNode(item.title));
		li.appendChild(a);
		ul.appendChild(li);
	}
	element.appendChild(ul);
};

// This method sticks a non-breaking space into an element if that element
// is completely empty.  This method is a bug workaround: Internet Explorer
// does not draw borders on empty <td> elements.
ComprehensiveProductList.prototype.force_nonempty =
	function (td)
{
	if (!/\S/.test(td.innerHTML)) {
		td.innerHTML = "&nbsp;";
	}
};

// Appends a product list table for the subcategory into the element.
// @param element - a DOM reference to an element in which to append the table.
//          Normally a <div> created by append_subcategory_placeholder().
ComprehensiveProductList.prototype.append_product_list  =
	function (element, subcategory) 
{
	var i;

	if (element.populated) {
		return;
	}

	var document = element.ownerDocument;

	var table = document.createElement("table");
	var thead = document.createElement("thead");
	var tbody = document.createElement("tbody");
	
	table.border = "0";
	table.cellSpacing = "0";
	table.cellPadding = "0";
	table.className = "dataTableClosed";

	var headings = ComprehensiveProductList.PRODUCT_LIST_HEADINGS;
	var tr = document.createElement("tr");
	var th;
	for (i = 0; i < headings.length; ++i) {
		th = document.createElement("th");
		th.appendChild(document.createTextNode(headings[i]));
		tr.appendChild(th);
	}
	thead.appendChild(tr);

	var product;
	for (i = 0; i < subcategory.products.length; ++i) {
		product = subcategory.products[i];
		this.append_product_info(tbody, product, i);
	}
	
	table.appendChild(thead);
	table.appendChild(tbody);
	element.appendChild(table);

	element.populated = true;
};

ComprehensiveProductList.prototype.open_photo_gallery =
	function (link)
{
	var newwindow =
		window.open(link, 'photo_gallery', 'height=590,width=750');
	if (newwindow.focus) {
		newwindow.focus();
	}
};

// Appends a div containing a link to a photo gallery.
// @param element - a DOM reference to an element in which to append the link.
//                  Normally a <td> created by append_product_info().
// @param link - the photo gallery's URL.
ComprehensiveProductList.prototype.append_photo_gallery_link =
	function (element, link)
{
	var document = element.ownerDocument;

	// NOTE: If we put both the image and the text in a single link, the
	// underline will show through the image in some browsers.  This is why
	// we're using two links butted up against one another.

	var that = this; // in the onclick, "this" becomes the link object.
	var onclick = function () {
		that.open_photo_gallery(link);
		return false;
	};

	var a1 = document.createElement("a");
	// though we're using an onclick, we must populate with an href
	// in order for css rules on this link to work.
	// might as well use the link for the href because:
	// - it facilitates hovering over to see what url this goes to
	// - things like middle-clicking in firefox do the right thing
	a1.href = link;
	a1.onclick = onclick;
	
	var img = document.createElement("img");
	img.src = "http://www.monogram.com/for_the_trade/" +
		"images/icon_photo_gallery_transparent.gif";
	img.setAttribute("width", 25);
	img.setAttribute("height", 20);
	img.setAttribute("border", 0);
	img.setAttribute("alt", "Photo Gallery");
	img.setAttribute("title", "Photo Gallery");
	img.className = "seethru";
	
	a1.appendChild(img);
	
	var a2 = document.createElement("a");
	a2.href = link;	// see comments above in this function
	a2.onclick = onclick;
	a2.appendChild(document.createTextNode("Photo Gallery"));
	
	var iconline = document.createElement("div");
	iconline.className = "iconline";
	iconline.appendChild(a1);
	iconline.appendChild(a2);
	
	element.appendChild(iconline);
};

// Appends a <tr> containing information about an individual product into a
// table's tbody.  This method is passed an array index in order to select a
// CSS class ("odd" or "even") for the <tr>.
// @param tbody - a DOM reference to a tbody, normally one within a table
//                created by append_product_list.
// @param index - the index of the specified product within the list of
//                products under a subcategory.
ComprehensiveProductList.prototype.append_product_info =
	function (tbody, product, index) 
{
	var document = tbody.ownerDocument;

	var tr = document.createElement("tr");
	tr.className = (index % 2 === 0) ? "even" : "odd";
	
	var td1 = tr.appendChild(document.createElement("td"));
	var td2 = tr.appendChild(document.createElement("td"));
	var td3 = tr.appendChild(document.createElement("td"));
	var td4 = tr.appendChild(document.createElement("td"));
	var td5 = tr.appendChild(document.createElement("td"));

	
	td1.className = "first";
	
	td1.appendChild(document.createTextNode(product.name));
	td2.appendChild(document.createTextNode(product.sku));
	
	this.append_list_of_links(td3, product.specifications);
	this.append_list_of_links(td4, product.installation);
	this.append_list_of_links(td5, product.cad);

	
	if (product.photo_gallery) {
		this.append_photo_gallery_link(td3, product.photo_gallery);
	}

	this.force_nonempty(td3);
	this.force_nonempty(td4);
	this.force_nonempty(td5);

	
	tbody.appendChild(tr);
};

/******************************************************************************
State preservation.
******************************************************************************/

ComprehensiveProductList.prototype.get_cookie =
	function (name)
{
	var cookies = document.cookie.split(";");
	var nv;
	var each_name;
	for (var i = 0; i < cookies.length; ++i) {
		nv = cookies[i].split("=");
		each_name = nv[0];
		if (name == each_name) {
			return nv[1];
		}
	}
	return null;
};

ComprehensiveProductList.prototype.save_states =
	function ()
{
	var i;
	var j;
	var category;
	var subcategory;
	
	var subcategories = [];
	for (i = 0; i < this.categories.length; ++i) {
		category = this.categories[i];
		for (j = 0; j < category.subcategories.length; ++j) {
			subcategory = category.subcategories[j];
			if (subcategory.expanded) {
				subcategories.push(subcategory.id);
			}
		}
	}
	for (i = 0; i < subcategories.length; ++i) {
		subcategories[i] = encodeURIComponent(subcategories[i]);
	}
	document.cookie = "cpl_subcategories=" + subcategories.join("&");
};

ComprehensiveProductList.prototype.load_states =
	function ()
{
	var cookie = this.get_cookie("cpl_subcategories");
	if (cookie === null || cookie === undefined) {
		return;
	}
	var subcategories = cookie.split("&");
	if (!subcategories.length) {
		return;
	}

	var expand = {};
	var i;
	var j;
	var category;
	var subcategory;
	var link;

	for (i = 0; i < subcategories.length; ++i) {
		subcategory = decodeURIComponent(subcategories[i]);
		expand[subcategory] = 1;
	}

	for (i = 0; i < this.categories.length; ++i) {
		category = this.categories[i];
		for (j = 0; j < category.subcategories.length; ++j) {
			subcategory = category.subcategories[j];
			link = subcategory.toggle_link;
			this.toggle_subcategory(link, subcategory,
						subcategory.id in expand);
		}
	}
};

/******************************************************************************
FOR TESTING PURPOSES ONLY
******************************************************************************/

ComprehensiveProductList.prototype.populate_subcategory_FROM_TEST_DATA =
	function (subcategory)
{
	if (subcategory.populated) {
		return; // idempotence
	}
	subcategory.products = [
		{ id: "005.005.001",
		  name: "Product Name Product Name Product Name Product Name",
		  model: "ZXXXXXXXX0"
		},
		{ id: "005.005.002",
		  name: "Product Name Product Name Product Name Product Name",
		  model: "ZXXXXXXXX1"
		},
		{ id: "005.005.003",
		  name: "Product Name Product Name Product Name Product Name",
		  model: "ZXXXXXXXX2"
		}
	];
	for (var i = 0; i < subcategory.products.length; ++i) {
		subcategory.products[i].specifications = [
			{ link: "http://www.google.com/", title: "Link" },
			{ link: "http://www.google.com/", title: "Link" },
			{ link: "http://www.google.com/", title: "Link" }
		];
		subcategory.products[i].installation = [
			{ link: "http://www.google.com/", title: "Link" },
			{ link: "http://www.google.com/", title: "Link" },
			{ link: "http://www.google.com/", title: "Link" }
		];
		subcategory.products[i].cad = [
			{ link: "http://www.google.com/", title: "Link" }
		];
		subcategory.products[i].custom_options = [
			{ link: "http://www.google.com/", title: "Link" }
		];
		subcategory.products[i].photo_gallery =
			"http://www.google.com/";
	}
	subcategory.populated = true;
};

/******************************************************************************
"Loading" Indicator
******************************************************************************/

CPLLoadingIndicator = function () {
	this.count = 0;	// no. of requests being handled at some point in time
	this.element = null;
	this.visible = false;

	// defaults that can be changed
	this.image_url  =
		"http://www.monogram.com/images/loading_indicator.gif";
	this.image_width = 352;
	this.image_height = 78;

	this.append_to_element = null;
};

CPLLoadingIndicator.prototype.get_element =
	function ()
{
	if (this.element) {
		return this.element;
	}

	var i = document.createElement("img");
	i.src = this.image_url;
	i.setAttribute("width", this.image_width);
	i.setAttribute("height", this.image_height);
	i.setAttribute("alt", "Loading...");
	i.setAttribute("title", "Loading...");
	
	var e = document.createElement("div");
	e.className = "loading_indicator";
	e.style.visibility = "hidden";
	
	e.appendChild(i);
	if (this.append_to_element) {
		this.append_to_element.appendChild(e);
	}
	this.element = e;
	return e;
};

CPLLoadingIndicator.prototype.show =
	function ()
{
	if (this.visible) {
		return;
	}
	var e = this.get_element();
	
	var ww = document.documentElement.clientWidth;
	var wh = document.documentElement.clientHeight;
	var st = document.documentElement.scrollTop;
	var iw = this.image_width;
	var ih = this.image_height;

	var x = Math.round((ww - iw) / 2);
	var y = Math.round((wh - ih) / 2) + st;
	
	if (x < 6) {
		x = 6;
	}
	if (y < 6) {
		y = 6;
	}

	e.style.left = x + "px";
	e.style.top = y + "px";
	e.style.visibility = "visible";
	this.visible = true;
};

CPLLoadingIndicator.prototype.hide =
	function ()
{
	if (!this.visible) {
		return;
	}
	if (!this.element) {
		return;
	}
	this.element.style.visibility = "hidden";
	this.visible = false;
};

// usually done just before request.send().
CPLLoadingIndicator.prototype.increment =
	function ()
{
	this.count++;
	if (this.count) {
		this.show();
	}
};

// usually done onreadystatechange when that value is 4.
CPLLoadingIndicator.prototype.decrement =
	function ()
{
	this.count--;
	if (this.count < 0) {
		this.count = 0;
	}
	if (!this.count) {
		this.hide();
	}
};

CPLLoadingIndicator.prototype.test =
	function ()
{
	var that = this;
	var flag = false;
	window.setInterval(function () {
		if (!flag) {
			that.show();
		}
		else {
			that.hide();
		}
		flag = !flag;
	}, 2000);
};

/* Local Variables: */
/* comment-column: 8 */
/* End: */

