/*****************************************
/* 	XML Client List Display
/*	May 14, 2008
/* 	Ver 1.0
/*		
/* 	Written by Samuel J. Lovetro, III
/* 	Site: lovetro.com
/*	Copyright (c) 2008. Unauthorized use is a violation of applicable laws.
/*
/*	This program will:
/*		Read a specified XML file of clients into an array.
/*		Sorted the array by alphabetical order or category (if specified).
/*		Format the data and insert it into a specified container.
/*
/*	HTML must have:
/*		A container for the client.
/*
/*		Optional:
/*		If there is a category field for the client, the ability to sort on category is offered.
/*
/*	In the "user defined variables" section of the invoking javascript file:
/*		1. Define the name of the folder where XML files are stored ("xmlFolder").
/*		2. Define the extension for XML files ("xmlExtension").
/*		3. Define the name of the client list container ("clientListContainer").
/*		4. Define the number of columns for the client list display ("clientListColumns").
/*		5. Define the element tag for the client element display ("clientElement").
/*		6. Define the class name of the client element display ("clientClassName").
/*		7. Set the flag for sorting by name ("sortByName").
/*		8. Set the flag for sorting by category ("sortByCategory").
/*		9. Define the categories in the "categories" array (REQUIRED if sortByCategory).
/*		10. Define the element tag for category display (REQUIRED if sortByCategory).
/*		11. Define the class name of the category display (REQUIRED if sortByCategory).
/*		
/*	Function:
/*		Will read a specified XML file with the following format:
/*		<client>
/*			<name>vanishingRow</name>													// The name of the client. REQUIRED.
/*			<category>photos</category>													// The category of the image. If "filterByCategory", REQUIRED.
/*		</client>
/*
/*		And then will do the following:
/*			1.	If "filterByCategory" is TRUE, build an array of only of objects of the specified category.

/*
/*		Function: displayClients(  ) will
/*			1.	Locate the the image display container and,
*/
/* Common JS Functions */
function $() {
	var elements = new Array();
	for (var i = 0; i < arguments.length; i++) {
		var element = arguments[i];
		if (typeof element == 'string')
			element = document.getElementById(element);
		if (arguments.length == 1)
			return element;
		elements.push(element);
	}
	return elements;
}
function addLoadEvent ( func ) {
//alert ("addLoadEvent: func="+func);
	var oldonload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function () {
			oldonload();
			func();
		}
	}
}
function whatTypeOf( element ) {
	var sArgumentType = "unknown";
    var sTypeOf = typeof( element );
    switch ( sTypeOf ) {
        case "string":
            var sArgumentType = sTypeOf;
            break;
        case "object":
            if ( element == null ) {
                var sArgumentType = "null";
            } else {
                if ( element.constructor == Array ) {
                    var sArgumentType = "array";
                }
            }
    }
	return sArgumentType;
}
function getTagText ( searchElement, tagName ) {
//alert('getTagText ( searchElement='+searchElement+', tagName='+tagName+' )');
	// Given an element, find a specified attribute
	// Return multiple attributes in an array
	var tagArray = searchElement.getElementsByTagName( tagName );
	if ( tagArray.length == 1 ) {
		if ( tagArray[ 0 ].firstChild ){
			if ( tagArray[ 0 ].firstChild.nodeValue.length > 0 ){ return tagArray[ 0 ].firstChild.nodeValue; }
		}
	}
	if ( tagArray.length > 1 ) {
		var tagChildren = new Array;
		for ( var i=0; i< tagArray.length; i++) {
			//tagChildren[ i ]. 
			if ( tagArray[ i ].firstChild ) {
				if ( tagArray[ i ].firstChild.nodeValue.length > 0 ){ tagChildren[ i ] = tagArray[ i ].firstChild.nodeValue }
			}
		}
		return tagChildren;
	}
	return null;
}
/* XML functions */
function getXMLDocument( ajax ) {
        if (typeof DOMParser == "undefined") {
                DOMParser = function()
                {};

                DOMParser.prototype.parseFromString = function(str, contentType) {
                        if (typeof ActiveXObject != "undefined") {
                                var doc = new ActiveXObject("MSXML.DomDocument");
								doc.async='false';
                                var success = doc.loadXML(str);
//alert('success='+success.toString() );
                                return doc;
                        } else if ( typeof XMLHttpRequest != "undefined" ) {
                                /*var req = new XMLHttpRequest();
                                req.open("GET", "data:" + (contentType || "application/xml") + ";charset=utf-8," + encodeURIComponent(str), false);
                                if ( req.overrideMimeType )
                                        req.overrideMimeType(contentType);
                                req.send(null);*/
                                return req.responseXML;
                        } else
                                throw new FatalException( "Can't find a valid xml parser", "AJAX::getXMLDocument()" );
                }
        }
        var strDocument = ajax.responseText;
        var xmlDocument = ajax.responseXML;
        try {
                if( ! xmlDocument || xmlDocument.childNodes.length === 0 )
                        xmlDocument = (new DOMParser()).parseFromString( strDocument, "text/xml" );
                return xmlDocument;
        } catch( e ) {
                return null;
        }

}
function processReqChange() {
//alert('processReqChange invoked.');
    // only if req shows "loaded"
    if ( req.readyState == 4 ) {
        // only if "OK"
        if ( req.status == 200 ) {
			xmlObj = getXMLDocument( req );
//alert('xmlObj='+xmlObj);
			xmlClientListParse();
        } else {
            alert( 'There was a problem retrieving the XML data:\n' + req.statusText );
        }
    }
}
function loadXMLFile ( filename ) {
//alert("loadXMLFile invoked. filename="+filename);
	//	1. Determines browser. 
	//	2. Creates proper browser xml document. 
	//	3. Sends XmlHTTPRequest.

	req = false;
	if ( 'undefined' != typeof ActiveXObject ) {
		try {
        	req = new ActiveXObject("Msxml2.XMLHTTP");
      	} catch(e) {
        	try {
          		req = new ActiveXObject("Microsoft.XMLHTTP");
        	} catch(e) {
          		req = false;
        	}
		}
	} else if ( 'undefined' != typeof document
		&& document.implementation
		&& document.implementation.createDocument
		&& 'undefined' != typeof DOMParser ) {
		try {
			req = new XMLHttpRequest();
		} catch(e) {
			req = false;
		}
	}
	if ( req ) {
		req.onreadystatechange = processReqChange;
		req.open( 'GET', filename, true );
		req.send( '' );
	} else {
		//alert('xml object not created');
	}
}
function xmlClientListRead ( filename ) {
	// Assumptions:
	// 1. All required DIVs are present
	// 2. Required XML file exists
//alert('xmlClientListRead invoked.');
	if ( !document.getElementById ) return false;
	
	// Define required elements, abort if error
	var success = defineDivs();
	if (!success) { alert( 'Required page elements not found. Client display aborted!' ); return false; }
	
	// Attempt to load the data
	xmlFile = xmlFolder + filename + xmlExtension;
	loadXMLFile( xmlFile );
}
/* Data functions */
function defineDivs () {
//alert("defineDivs invoked.");
	// Assumptions: All needed elements exist
	// Result: If one needed element is missing, return FALSE
	var failed = false;
	if ( $( clientList ))	{ clientList = $( clientList ) } else { failed = true };
	return ( !failed );
}
function clientObj ( name, category ) {
	// Create an client object for data handling
	// Note: Any empty value is NULL.
	this.name = name;
	this.category = category;
}
function xmlClientListParse () {
//alert( 'xmlClientListParse invoked. firstChild.tagName=' +xmlObj.firstChild.tagName);
	var elements = xmlObj.getElementsByTagName( 'client' );
	
	// For every element listed in the XML, add them to an array of element objects
	if ( elements.length > 0 ) {
		clients = new Array( elements.length );
		for ( var i=0; i < elements.length; i++ ) {
			var tName = getTagText( elements[ i ], 'name' );
			var tCategory = getTagText( elements[ i ], 'category' );
			clients[ i ] = new clientObj( tName, tCategory );
		}
		xmlClientListDisplay( 'name' );
	}
}
function sortClientsByName ( clientArray ) {
//alert( 'sortClientsByName invoked. clientArray.length='+clientArray.length);
	var tClientArray = clientArray;
	for ( var i=0; i<tClientArray.length; i++ ) {
		for( var j=tClientArray.length-1; j > i; j-- ) {
			if ( tClientArray[ j-1 ].name > tClientArray[ j ].name ) {
				var tempEventObj = tClientArray[ j-1 ];
				tClientArray[ j-1 ] = tClientArray[ j ];
				tClientArray[ j ] = tempEventObj;
			}
		}
	}
	return tClientArray;
}
/* Display functions */
function xmlClientListDisplay ( type ) {
//alert( 'xmlClientListDisplay invoked. type='+type );

	// Clear display, links to function and add needed display columns
	while( clientList.hasChildNodes() ) clientList.removeChild( clientList.firstChild );
	if ( sortByCategory ) {
		var p = document.createElement( 'p' );
		var nameAnchor = document.createElement( 'a' );
		nameAnchor.appendChild( document.createTextNode( 'Sort by Name' ));
		nameAnchor.href = '#';
		nameAnchor.id = 'sortByName';
		nameAnchor.onclick  = function () {
			xmlClientListDisplay ( 'name' );
			return false;
		}

		var spacer = document.createTextNode(' | ');
		
		var categoryAnchor = document.createElement( 'a' );
		categoryAnchor.appendChild( document.createTextNode( 'Sort by Industry' ));
		categoryAnchor.href = '#';
		categoryAnchor.id = 'sortByCategory';
		categoryAnchor.onclick  = function () {
			xmlClientListDisplay ( 'category' );
			return false;
		}
		
		p.appendChild( nameAnchor );
		p.appendChild( spacer );
		p.appendChild( categoryAnchor );
		clientList.appendChild( p );
	}
	for( var i=0; i<clientListColumns; i++ ) {
		var tElement = document.createElement( 'div' );
		tElement.className = clientColumnClassName;
		tElement.id = 'col' + i.toString();
		clientList.appendChild( tElement );
	}
	
	switch ( type ) {
		case 'category':
			$( 'sortByCategory' ).className = 'inactive';
			$( 'sortByName' ).className = '';
			var colNumber = 0;
			var colElements = Math.ceil(( clients.length + categories.length )/ clientListColumns );
			var elementsDisplayed = 0;
			
			// Filter clients by category into clientsTemp
			for ( var i=0; i<categories.length; i++ ) {
				var category = categories[ i ];
				var clientsTemp = new Array();
				var k = 0;
				for ( var j=0; j<clients.length; j++ ) {
					if ( clients[ j ].category == category ) {
						clientsTemp[ k ] = clients[ j ];
						k++;
					}
				}
				clientsTemp = sortClientsByName( clientsTemp );
				
				// Append category header
				var tCategory = document.createElement( categoryElement );
				tCategory.className = categoryClassName;
				var tCategoryTextNode = document.createTextNode( category );
				tCategory.appendChild( tCategoryTextNode );
				if (( elementsDisplayed-1 > colElements ) && ( colNumber+1 < clientListColumns )) {
					colNumber++;
					elementsDisplayed = 0;
				}
				$( 'col' + colNumber.toString() ).appendChild( tCategory );
				elementsDisplayed++;
				
				// Append clients
				for ( var j=0; j<clientsTemp.length; j++ ) {
					var tElement = document.createElement( clientElement );
					tElement.className = clientClassName;
					var tTextNode = document.createTextNode( clientsTemp[ j ].name );
					tElement.appendChild( tTextNode );
					if (( elementsDisplayed-1 > colElements ) && ( colNumber+1 < clientListColumns )) {
						colNumber++;
						elementsDisplayed = 0;
						// Create new category header and append it
						var tCategory = document.createElement( categoryElement );
						tCategory.className = categoryClassName;
						var tCategoryTextNode = document.createTextNode( category +' cont.' );
						tCategory.appendChild( tCategoryTextNode );
						$( 'col' + colNumber.toString() ).appendChild( tCategory );
						elementsDisplayed++;
					}
					$( 'col' + colNumber.toString() ).appendChild( tElement );
					elementsDisplayed++;
				}				
			}
			break;
		case 'name':
			$( 'sortByCategory' ).className = '';
			$( 'sortByName' ).className = 'inactive';

			if ( sortByName ) {
				var clientsTemp = sortClientsByName( clients );
			} else {
				var clientsTemp = clients;
			}
			
			// Append clients
			var numElements = Math.ceil( clientsTemp.length / clientListColumns );
			for ( var i=0; i<clientListColumns; i++ ) {
				for ( var j=( numElements*i ); j<( numElements*( i+1 )); j++ ) {
					if ( j>=clientsTemp.length ) continue;
					var tElement = document.createElement( clientElement );
					tElement.className = clientClassName;
					var tTextNode = document.createTextNode( clientsTemp[ j ].name );
					tElement.appendChild( tTextNode );
					$( 'col' + i.toString() ).appendChild( tElement );
				}
			}
	}
	
	// Clear the floats
	var tElement = document.createElement( 'br' );
	tElement.className = 'clear';
	clientList.appendChild( tElement ); 
}

// Script variables
var xmlObj;  																// The XML object
var xmlFile;																// Name of XML file (defined when invoked)
var req;																	// XmlHTTPRequest object
var clients;																// Array for client objects