/* ============================================================================
 * Script to enhance user experience.
 * ============================================================================
 * TODO:
 *  - (!) Investigate cookies in IE5.x
 *  - Add comment AJAX to a class
 *  - Add timeout (and cancel?) to AJAX
 *  - Chance IOC to TOC
 *  - Fix code to move the float
 *  - Add document.importNode prototype if not present
 *  - Write DOMnode2string function
 * ============================================================================
 * Use and change script as you see fit, just keep it free.
 * Last edit: Thomas Thomassen [11.02.2007]
 * ============================================================================
 */


/* ===== ON DOCUMENT LOAD ===== */
/* Code to call events straigth after the DOM has loaded is
 * based on http://dean.edwards.name/weblog/2005/09/busted/
 */
var isIE = false; // Get set to true in ie_onload.js
// Before calling any functions, check the brower capabilities
if (document.getElementById &&
	document.getElementsByTagName &&
	document.createElement &&
	document.createTextNode)
{
	function init()
	{
		// Quit if this function has already been called
		if (arguments.callee.done) return;
		// Flag this function so we don't do the same thing twice
		arguments.callee.done = true;

		// Execute functions onLoad
		if (isIE) fixIECSS();
		addTOCDetatchButton();
		addTooltips();
		expandImageTitles();
		//enhanceSitemap();
		enhanceListview();
		if (supportAJAX()) addAJAX();
	}
// For Mozilla
	if (document.addEventListener) {
		document.addEventListener("DOMContentLoaded", init, false);
	}
// For Internet Explorer
	/*@cc_on @*/
	/*@if (@_win32)
		document.write("<script defer src='scripts/ie_onload.js'><"+"/script>");
	/*@end @*/
// For other browsers
	window.onload = init;
}
fixPopPush();

function addAJAX()
{
	enhanceCommentForm();
}


// Adds tooltips to ABBR and ACRONYM elements
var tooltipFocus = false;
function addTooltips()
{
	var abbr = null;

	var abbrs = document.getElementsByTagName('abbr');
	var acronyms = document.getElementsByTagName('acronym');

	for (var i = 0; i < abbrs.length; i++)
	{
		abbr = abbrs[i];
		if (abbr.title.length)
		{
			abbr.tooltip = abbr.title;
			abbr.title = '';
			abbr.onmouseover = function() { showTooltip(this.tooltip, this); };
			abbr.onmouseout = function() { setTimeout("hideTooltip()", 200); };
		}
	}
	var acronym = null;
	var acronyms = document.getElementsByTagName('acronym');
	for (var i = 0; i < acronyms.length; i++)
	{
		acronym = acronyms[i];
		if (acronym.title.length)
		{
			acronym.tooltip = acronym.title;
			acronym.title = '';
			acronym.onmouseover = function() { showTooltip(this.tooltip, this); };
			acronym.onmouseout = function() { setTimeout("hideTooltip()", 200); };
		}
	}
}
function showTooltip(tooltipText, element)
{
	// Get the position of the element to provide tooltip for
	var position = getRect(element);
	// Build tooltip element
	var tooltip = document.createElement('div');
	tooltip.id = 'tooltip';
	tooltip.style.position = 'absolute';
	tooltip.style.left = 0;
	tooltip.style.top = 0;
	tooltip.style.visibility = 'hidden';
	tooltip.onmouseover = function() { tooltipFocus = true; };
	tooltip.onmouseout = function() { tooltipFocus = false; setTimeout("hideTooltip()", 200); };
	//tooltip.appendChild( document.createTextNode(tooltipText) );
	tooltip.innerHTML = '<h4>' + tooltipText + '</h4>' +
						'<ul>' +
						'<li><a href="http://en.wikipedia.org/wiki/' + tooltipText.replace(/\s/g, '_') + '">Wikipedia</a></li>' +
						'<li><a href="http://dictionary.reference.com/search?q=' + encodeURIComponent(tooltipText) + '">Dictionary.com</a></li>' +
						'<li><a href="http://www.google.com/search?q=' + encodeURIComponent(tooltipText) + '">Google.com</a></li>' +
						'</ul>';
	// (!) Remove element.title - store somewhere else
	// Add to the document
	document.body.appendChild( tooltip );
	// Get Size
	var tPos = getRect(tooltip);
	// Correct position
	tooltip.style.left = position[0] + 'px';
	tooltip.style.top = (position[1] + position[3]) + 'px';
	if ((position[1] + position[3] + tPos[3]) > document.body.clientHeight)
		tooltip.style.top = (position[1] - tPos[3]) + 'px';
	else
		tooltip.style.top = (position[1] + position[3]) + 'px';
	tooltip.style.visibility = 'visible';
}
function hideTooltip()
{
	if (!tooltipFocus)
	{
		var tooltip = document.getElementById('tooltip');
		if (tooltip) document.body.removeChild( tooltip );
	}
}


/* ===== ENHANCE COMMENT FORM ===== */
// Add the preview button next to the submit button
function enhanceCommentForm()
{
	// Get the form element and submit button
	var commentForm = document.getElementById('postComment');
	var submitButton = document.getElementById('submit');

	if (commentForm && submitButton)
	{
		var formButtonContainer = submitButton.parentNode;
		// Create preview button
		var previewButton = document.createElement('input');
		previewButton.value = 'Preview';
		previewButton.type = 'button';
		previewButton.className = 'button';
		previewButton.id = 'previewComment';
		previewButton.name = 'previewComment';
		previewButton.onclick = previewComment;
		// Insert button into document
		formButtonContainer.insertBefore(previewButton, submitButton);
		formButtonContainer.insertBefore(document.createTextNode(' '), submitButton); // Add a space between the buttons (Add to CSS?)
	}
}
// Take form values and compile to a argument string for POST or GET data
function formToArguments(formName)
{
	var arguments = new Array();
	var form = document.getElementById(formName);
	if (form && form.hasChildNodes)
	{
		// First get input elements
		var elements = form.getElementsByTagName('input');
		for (var i = 0; i < elements.length; i++)
		{
			// Spesial case for checkboxes where further checking has to be done to get the value
			if (elements[i].type == 'checkbox')
			{
				if (elements[i].checked == true)
					arguments.push(elements[i].name + '=on');
				else
					arguments.push(elements[i].name + '=off');
			}
			else
			{
				arguments.push(elements[i].name + '=' + encodeURIComponent(elements[i].value));
			}
		}
		// Then get textarea elements
		elements = form.getElementsByTagName('textarea');
		for (var i = 0; i < elements.length; i++)
		{
			arguments.push(elements[i].name + '=' + encodeURIComponent(elements[i].value));
			//alert(elements[i].value);
		}
	}
	return arguments.join('&');
}
// Make request for the comment preview and display a loading screen.
function previewComment()
{
	var commentForm = document.getElementById('postComment');
	// AJAX request for the comment preview
	if (commentForm.className == 'postNews')
		makeRequest('previewNews.php', formToArguments('postComment'));
	else
		makeRequest('previewComment.php', formToArguments('postComment'));


	// *** Show loading screen *** //
	// Make sure the preview list element exist
	var target = document.getElementById('commentsList');
	var preview = document.getElementById('commentPreview');
	if (!preview)
	{
		preview = document.createElement('li');
		preview.id = 'commentPreview';
		preview.className = 'comment';
		target.appendChild(preview);
	}
	// Add the loading indicator if it's not allready shown
	var loadContainer = document.getElementById('loadIndicator');
	if (!loadContainer)
	{
		// --- Create loading indicator --- //
		// Create container
		loadContainer = document.createElement('div');
		loadContainer.id = 'loadIndicator';
		// Create image
		var loadAni = document.createElement('img');
		loadAni.src = 'images/ajax-loader2.gif';
		loadAni.id = 'loadAnimation';
		// Compile elements
		loadContainer.appendChild(loadAni);
		loadContainer.appendChild( document.createTextNode(' Please wait while the preview loads...') );
		//var cancelLoad = document.createElement('a');
		//cancelLoad.href = 'javascript:cancelPreview()';
		//cancelLoad.appendChild( document.createTextNode('Cancel') );
		//loadContainer.appendChild( cancelLoad );
		//loadContainer.appendChild( document.createTextNode(')') );
		// Add to document
		preview.appendChild(loadContainer);
	}
}
// AJAX request function
//var http_request = false;
function makeRequest(url, data) {
	//if (http_request)
	//	http_request.abort(); // Make sure any previous calls are cancelled
	var http_request = false;
    if (window.XMLHttpRequest) { // IE7, Mozilla, Safari, ...
        http_request = new XMLHttpRequest();
        if (http_request.overrideMimeType) {
            http_request.overrideMimeType('text/xml');
            // See note below about this line
        }
    } else if (window.ActiveXObject) { // IE 6-
        try {
            http_request = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                http_request = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e) {}
        }
    }

    if (!http_request) {
    	showError('Giving up :( Cannot create an XMLHTTP instance.');
        //alert('Giving up :( Cannot create an XMLHTTP instance.');
        return false;
    }
    http_request.onreadystatechange = function() { alertContents(http_request); };
    http_request.open('POST', url, true);
    http_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    http_request.send(data);

}
function cancelPreview()
{
	if (http_request)
		http_request.abort();
}
// Function called when the page is loaded
function alertContents(http_request) {
    //try {
        if (http_request.readyState == 4) {
            if (http_request.status == 200) {
                showPreview(http_request.responseXML,http_request.responseText);
                //alert(http_request.responseText);
                //alert('tester h&aring;p og tru')
            } else {
               // alert('There was a problem with the request.\nStatus: ' + http_request.status);
               showError('There was a problem getting the comment preview. Please try again. If the problem persist, please <a href="http://about.thomthom.net/contact.php">contact me</a> and provide the error codes below.<br /><br />\n<samp>HTTP Request Status: ' + http_request.status + '</samp>');
                // Report error
            }
        }
    //}
    //catch( e ) {
    //    alert('Caught Exception: ' + e.description);
    //}
}
// Output errors last in the commentlist
function showError(errorMsg)
{
	// Remove the loading screen
	var loadIndicator = document.getElementById('loadIndicator');
	if (loadIndicator)
		loadIndicator.parentNode.removeChild(loadIndicator);
	// Make sure the preview list element exist
	var target = document.getElementById('commentsList');
	var preview = document.getElementById('commentPreview');
	if (!preview)
	{
		preview = document.createElement('li');
		preview.id = 'commentPreview';
		preview.className = 'comment';
		target.appendChild(preview);
	}
	// Append error.
	var errorContainer = document.getElementById('commentError');
	if (!errorContainer)
	{
		errorContainer = document.createElement('div');
		errorContainer.id = 'commentError';
		preview.appendChild(errorContainer);
	}
	errorContainer.innerHTML = errorMsg;
}
// Display the comment preview and any errors
function showPreview(domtree, text)
{
	// Fetch elements to insert comment into
	var target = document.getElementById('commentsList');
	// See if the <COMMENT> element exist, then make sure it contains data (Check if data is CDATA?)
	var commentData = domtree.getElementsByTagName('comment');
	//alert(commentData[0].childNodes[1].nodeValue);
	if (target && commentData && commentData.length && commentData[0].childNodes.length)
	{
		// Check for any existing comment preview. If no comment preview element exist, create one
		var preview = document.getElementById('commentPreview');
		if (!preview)
		{
			preview = document.createElement('li');
			preview.id = 'commentPreview';
			target.appendChild(preview);
		}
		// The <COMMENT> should be a CDATA node child to the <COMMENT> element
		var comment = commentData[0].childNodes[0].nodeValue;
		preview.innerHTML = comment;
		// Add errors
		var errors = domtree.getElementsByTagName('errors');
		if (errors && errors.length && errors[0].childNodes.length)
		{
			// Create element to contain the error information
			var errorContainer = document.createElement('div');
			errorContainer.id = 'commentError';
			// Fetch and output the errors
			var errorMessage = errors[0].childNodes[0].nodeValue;
			errorContainer.innerHTML = 'The comment may appear different from what you intended. It contained some invalid <abbr title="eXtended HyperText Markup Language">XHTML</abbr> markup. If the preview looks OK you can just post anyways. <a href="javascript:showCommentErrors()">View Errors</a>';
			errorContainer.innerHTML += errorMessage;
			preview.appendChild(errorContainer);

		}
	}
}
// Show the error list element
function showCommentErrors()
{
	var errorList = document.getElementById('commentErrorList');
	if (errorList)
		errorList.style.display = 'block';
}


/* ===== EXPAND IMAGE TITLE ===== */
function expandImageTitles()
{
	var parent, container, imageTitle, image, oldElement, newElement;
	var images = document.getElementsByTagName('img');
	for (var i = 0; i < images.length; i++)
	{
		image = images[i];
		if (image.parentNode && image.className == 'illustration' && image.title.length > 0)
		{
			imageTitle = document.createElement('div');
			imageTitle.appendChild( document.createTextNode(image.title) );
			imageTitle.className = 'imageTitle';
			// If the image's parent is a link element, include it
			if (image.parentNode.nodeName.toLowerCase() == 'a')
			{
				oldElement = image.parentNode;
				newElement = image.parentNode.cloneNode(true);
			}
			else
			{
				oldElement = image;
				newElement = image.cloneNode(false);
			}
			// Assemble the container element with the image and title
			container = document.createElement('div');
			container.appendChild(newElement);
			container.appendChild(imageTitle);
			container.className = 'illustrationContainer';
			// Add it to the document
			oldElement.parentNode.replaceChild(container, oldElement);
		}
	}
}


/* ===== FLOAT/DOCK INDEX OF CONTENT ===== */
function addTOCDetatchButton()
{
	var ioc = document.getElementById('ioc');
	if (ioc)
	{
		// Create elements
		var link = document.createElement('a');
		var container = document.createElement('div');
		// Set float link properties
		link.id = 'iocFloat';
		link.href = 'javascript:floatIoc()';
		link.appendChild( document.createTextNode('Detach list') );
		// Create top link elements
		var top = document.createElement('a');
		var topWrapper = document.createElement('div');
		// Set 'scroll to top' link properties
		top.href = '#';
		top.appendChild( document.createTextNode('Scroll to top') );
		// Set its wrapper's properties
		topWrapper.id = 'iocTop';
		topWrapper.appendChild(top);
		// Set the properties for the container element and add the child elements
		container.style.margin = '6px 0 0 0'; // (!) Add to CSS
		container.appendChild(topWrapper);
		container.appendChild(link);
		// Finally add it all to the document
		ioc.appendChild(container);
		// Read settings
		if ( readCookie('iocDocked') == 'false' ) floatIoc();
		return;
		// Attach mouse event handlers
		ioc.getElementsByTagName('h3')[0].onmousedown = iocMouseDown;
		document.onmousemove = docMouseMove;
		document.onmouseup = docMouseUp;
		// Set cursor style
		ioc.getElementsByTagName('h3')[0].style.cursor = 'move';
	}
}

function floatIoc()
{
	var ioc = document.getElementById('ioc');
	var link = document.getElementById('iocFloat');
	if (ioc && link)
	{
		ioc.className = 'undocked';
		// Update text and function
		var linkText = document.createTextNode('Dock list');
		link.replaceChild(linkText, link.firstChild)
		link.href = 'javascript:dockIoc()';
		// Save setting
		createCookie('iocDocked', 'false', 1000);
	}
}

function dockIoc()
{
	var ioc = document.getElementById('ioc');
	var link = document.getElementById('iocFloat');
	if (ioc && link)
	{
		ioc.className = '';
		// Update text and function
		var linkText = document.createTextNode('Detach list');
		link.replaceChild(linkText, link.firstChild)
		link.href = 'javascript:floatIoc()';
		// Save setting
		createCookie('iocDocked', 'true', 1000);
	}
}

/* --- IOC MOVE --- */
var startLeft = startTop = startX = startY = 0;
var buttonDown = false, movingElement;
function iocMouseDown(e)
{
	if (!e) var e = window.event;
	/*
	if (e.target) targ = e.target;
	else if (e.srcElement) targ = e.srcElement;
	if (targ.nodeType == 3) // defeat Safari bug
		targ = targ.parentNode;
	*/
	//alert(targ.parentNode.id);
	//alert('mouse down - X:' + e.screenX + 'Y:' + e.screenY);
	//alert(e.button);
	var ioc = document.getElementById('ioc');
	//if (ioc && (ioc.className == 'undocked') && (targ.parentNode == ioc))
	if (ioc && (ioc.className == 'undocked'))
	{
		var iocPosition = getRect(ioc);
		startLeft = iocPosition[0];
		startTop = iocPosition[1];
		startX = e.screenX;
		startY = e.screenY;

		movingElement = ioc;
		buttonDown = true;
		return true;
	}
}
function docMouseMove(e)
{
	if (!e) var e = window.event;
	// (!) Prevent selection whilst moving
	// (!) Investigate Opera oddity where element jump down on beginning of drag
	// (!) Investigate IE5.0's move cursor
	// (!) Investigate IE6's movable area. Currently only activates over its textnode child area.
	if (movingElement && buttonDown)
	{
		/*var iocPosition = getRect(ioc);
		startLeft = iocPosition[0];
		startTop = iocPosition[1];
		startX = e.screenX;
		startY = e.screenY;*/
		//alert('test');
		movingElement.style.left = (startLeft + (e.screenX - startX)) + 'px';
		movingElement.style.top = (startTop + (e.screenY - startY)) + 'px';

		return true;
	}
}
function docMouseUp(e)
{
	if (!e) var e = window.event;
	buttonDown = false;
	//alert('startLeft:' + iocX + '\nstartX:' + startX + '\ne.screenX:' + e.screenX + '\n=' + (startLeft + (e.screenX - startX)) )
}

/* ===== SITEMAP ENHANCEMENTS ===== */
function enhanceSitemap()
{
	if ( document.getElementById('sitemap') )
	{
		make_clickable( document.getElementById('sketches') );
		make_clickable( document.getElementById('process') );
		make_clickable( document.getElementById('final') );
	}
}

function make_clickable(element)
{
	var links = element.getElementsByTagName('a');
	var link = links[0].href; // (!) Errorcheck this line
	element.onmouseover = function() { this.className = 'hover'; };
	element.onmouseout  = function() { this.className = 'normal'; };

	/* === OLD CODE ===
	var links = element.getElementsByTagName('a');
	var link = links[0].href; // (!) Errorcheck this line
	element.onclick     = function() { window.open(link, "_self"); };
	element.onmouseover = function() { window.status=link; this.className = 'hover'; return true; };
	element.onmouseout  = function() { window.status=""; this.className = 'normal'; return true; };
	// IE 5 will throw an error when trying to set the cursor
	// property as "pointer". We'll catch this and set the
	// cursor property as hand instead.
	try
	{
		element.style.cursor = 'pointer';
	}
	catch(error)
	{
		element.style.cursor = 'hand';
	}
	*/
}


/* ===== LISTVIEW ENHANCEMENTS ===== */
function enhanceListview()
{
	// Test image onload event. (!) Find a better method.
	//var DOTS = '#define x_width 2\n#define x_height 1\nstatic char x_bits[]={0x01}';
	var testImage = document.createElement('img');
	testImage.onload = testImageOnload;
	testImage.src = 'images/imageOnloadTest.gif'; // Small test image. (2bit 1x1px)

	var listview;
	if ( listview = document.getElementById('listView') )
	{
		var a, img, div, span;
		var lis = listview.getElementsByTagName('li');
		for (var i = 0; i < lis.length; i++)
		{
			// Try to get the list item's first link element
			if ( a = lis[i].getElementsByTagName('a')[0] )
			{
				a.onclick = function() { showLoadScreen(this.parentNode); return false; }; // Pass the LI element of the A element
				//a.onclick = function() { gallery = new classDynamicGallery; gallery.showLoadScreen(this.parentNode); return false; }; // Pass the LI element of the A element
			}
			// Expand image titles
			if ( (img = lis[i].getElementsByTagName('img')[0]) && img.title )
			{
				// Add <div><span class="hint">Right View</span></div>
				div = document.createElement('div');
				span = document.createElement('span');
				span.className = 'hint';
				span.appendChild( document.createTextNode(img.title) );
				div.appendChild(span);
				lis[i].appendChild(div);
			}
		}
	}
}
var imageOnloadWorks = false;
function testImageOnload()
{
	//alert('Image Onload Works');
	imageOnloadWorks = true;
	// (!) Add onclick handlder for links here
}
// --- GLOBAL VARIABLES FOR PREVIEW --- //
//function classDynamicGallery()
//{
var animationIntervall = 0; timerID = 0;
var originRect = targetRect = new Array(3);
var ANI_STEPS = 5;
var currentIndex, currentItem, cacheImage;
// --- GLOBAL VARIABLES END --- ///
//this.showLoadScreen = function showLoadScreen(listElement)
function showLoadScreen(listElement)
{
	currentItem = listElement;
	// If onload doesn't work for image elements, exit the function
	if (!imageOnloadWorks)
	{
		// load URL
		var link = currentElement.getElementsByTagName('a')[0];
		window.open(link.href, "_self");
		return;
	}
	// If the preview elements exist then proceed to load the next image,
	// if not, create them and animate
	var background = document.getElementById('previewBackground');
	var preview = document.getElementById('preview');
	if (background && preview)
	{
		// If the small loadingscreen is showing, remove it and start the animation again...
		if (document.getElementById('loadingScreen'))
		{
			background.parentNode.removeChild(background);
			preview.parentNode.removeChild(preview);
			// Start the animation
			animationIntervall = 0;
			animateOpenLoadScreen();
		}
		else
		{
			// ...otherwise just start loading the next image
			loadLargeImage();
		}
	}
	else
	{
		animationIntervall = 0;
		animateOpenLoadScreen();
	}

}
//this.animateOpenLoadScreen = function animateOpenLoadScreen()
function animateOpenLoadScreen()
{
	var background = document.getElementById('previewBackground');
	var preview = document.getElementById('preview');
	var linkElement = currentItem.getElementsByTagName('a')[0]; // Get the first A element
	// If the preview and background elements doesn't exist we need to create them
	if (!background && !preview)
	{
		// Create elements
		background = document.createElement('div');
		background.id = 'previewBackground';
		preview = document.createElement('div');
		preview.id = 'preview';
		// --- Position elements --- //
		preview.style.position = 'absolute';
		background.style.position = 'absolute';
		// Calculate target position
		var listView = document.getElementById('listView');
		rect = getRect(listView);
		targetRect[0] = rect[0] + (rect[3] / 2) - (260 / 2); // left + (width / 2) - (260 / 2);
		targetRect[1] = rect[1] + 50; // top + 50 (!) Change this to be relative to viewarea.
		targetRect[2] = 262;
		targetRect[3] = 260;
		// Get current RECT for the thumbnail element
		originRect = getRect( currentItem );
		// Match loading screen elements
		background.style.left	= originRect[0] + 'px';
		background.style.top	= originRect[1] + 'px';
		background.style.width	= (originRect[2] - 2) + 'px'; // (!) Account for 1px border
		background.style.height	= (originRect[3] - 2) + 'px'; // (!) Account for 1px border
		preview.style.left 		= originRect[0] + 'px';
		preview.style.top		= originRect[1] + 'px';
		preview.style.width		= originRect[2] + 'px';
		preview.style.height	= originRect[3] + 'px';
		// Add it to the document
		//listView.parentNode.insertBefore(preview, listView.nextSibling);
		//listView.parentNode.insertBefore(background, listView.nextSibling);
		document.body.appendChild( background );
		document.body.appendChild( preview );
		/* Oddity: If the insertBefore method is used, then IE 5 (not IE 5.5+) will report the offsetLeft as 0 until the function has exited */
	}

	if (animationIntervall >= ANI_STEPS)
	{
		// Stop timer as the animation is done
		clearTimeout(timerID);
		animationIntervall = 0;
		loadLargeImage();
	}
	else
	{
		// Move the loading screen to its next position
		background.style.left	= background.offsetLeft		+ ( (targetRect[0] - originRect[0]) / ANI_STEPS) + 'px';
		background.style.top	= background.offsetTop		+ ( (targetRect[1] - originRect[1]) / ANI_STEPS) + 'px';
		background.style.width	= background.offsetWidth	+ ( ( (targetRect[2] - originRect[2]) / ANI_STEPS) - 2) + 'px'; // (!) Account for 1px border
		background.style.height	= background.offsetHeight	+ ( ( (targetRect[3] - originRect[3]) / ANI_STEPS) - 2) + 'px'; // (!) Account for 1px border
		preview.style.left		= preview.offsetLeft	+ ( (targetRect[0] - originRect[0]) / ANI_STEPS) + 'px';
		preview.style.top		= preview.offsetTop		+ ( (targetRect[1] - originRect[1]) / ANI_STEPS) + 'px';
		preview.style.width		= preview.offsetWidth	+ ( (targetRect[2] - originRect[2]) / ANI_STEPS) + 'px';
		preview.style.height	= preview.offsetHeight	+ ( (targetRect[3] - originRect[3]) / ANI_STEPS) + 'px';
		// Keep timer going
		animationIntervall++;
		timerID = setTimeout('animateOpenLoadScreen()', 20);
	}
}

// Display loading screen information
// and start loading the large image
function loadLargeImage()
{
	var background = document.getElementById('previewBackground');
	var preview = document.getElementById('preview');
	if (background && preview)
	{
		var thumb, image, link, loadAni, loadInfo;
		var linkElement = currentItem.getElementsByTagName('a')[0]; // Get the first A element
		// Create container for loadingscreen
		loadingScreen = document.createElement('div');
		loadingScreen.id  = 'loadingScreen';
		if ( document.getElementById('largePreview') )
			loadingScreen.className = 'largePreviewVisible'; // If the large preview is open we want to position the load screen differently
		// Get image information
		image = linkElement.getElementsByTagName('img')[0]; // (!) Error handle
		// Create thumbnail element
		thumb = document.createElement('img');
		thumb.src = image.src; // (!) Error handle
		thumb.className = 'thumbnail';
		thumb.id = 'loadingThumb';
		// Create loading indicator
		loadAni = document.createElement('img');
		loadAni.src = 'images/ajax-loader2.gif';
		loadAni.id = 'loadAnimation';
		// Create container for loading information
		loadInfo = document.createElement('div');
		loadInfo.id = 'loadInfo';
		// Create loading information text
		var imageTitle = document.createElement('strong');
		imageTitle.appendChild(  document.createTextNode(image.title) );
		loadInfo.appendChild(loadAni);
		loadInfo.appendChild( document.createTextNode('Loading ') );
		loadInfo.appendChild(imageTitle);
		loadInfo.appendChild( document.createTextNode(' ...') );
		// Create link to abort loading
		var linkClose = document.createElement('a');
		linkClose.href = 'javascript:closePreview()';
		linkClose.style.display = 'block';
		linkClose.appendChild( document.createTextNode('Cancel') );
		// Assemble loading screen
		loadingScreen.appendChild(thumb);
		loadingScreen.appendChild(loadInfo);
		loadingScreen.appendChild(linkClose);
		preview.appendChild(loadingScreen);
		// Start loading large image
		cacheImage = document.createElement('img');
		cacheImage.id = 'cacheImage';
		cacheImage.onload = showLargeImage;
		cacheImage.src = linkElement.href;
	}
}
function showLargeImage()
{
	// Remove the loading text
	var loadingScreen = document.getElementById('loadingScreen');
	if (loadingScreen)
		loadingScreen.parentNode.removeChild(loadingScreen);
	// Check if the large preview is allready open.
	if ( document.getElementById('largePreviewContainer') )
		openLargeImage();		 // Open without animation
	else
		animateOpenLargeImage(); // Open with animation
}
function animateOpenLargeImage()
{
	var background = document.getElementById('previewBackground');
	var preview = document.getElementById('preview');
	var listView = document.getElementById('listView');
	// If animation just started, position elements to origin
	if (animationIntervall == 0 && background && preview)
	{
		// Calculate target position
		targetRect = getRect(listView);
		originRect = getRect(preview);
	}

	if (animationIntervall >= ANI_STEPS)
	{
		// Stop timer as the animation is done
		clearTimeout(timerID);
		animationIntervall = 0;
		openLargeImage();
	}
	else
	{
		// Move the loading screen to its next position
		background.style.left	= background.offsetLeft		+ ( (targetRect[0] - originRect[0]) / ANI_STEPS) + 'px';
		background.style.top	= background.offsetTop		+ ( (targetRect[1] - originRect[1]) / ANI_STEPS) + 'px';
		background.style.width	= background.offsetWidth	+ ( ( (targetRect[2] - originRect[2]) / ANI_STEPS) - 2) + 'px'; // (!) Account for 1px border
		background.style.height	= background.offsetHeight	+ ( ( (targetRect[3] - originRect[3]) / ANI_STEPS) - 2) + 'px'; // (!) Account for 1px border
		preview.style.left		= preview.offsetLeft	+ ( (targetRect[0] - originRect[0]) / ANI_STEPS) + 'px';
		preview.style.top		= preview.offsetTop		+ ( (targetRect[1] - originRect[1]) / ANI_STEPS) + 'px';
		preview.style.width		= preview.offsetWidth	+ ( (targetRect[2] - originRect[2]) / ANI_STEPS) + 'px';
		preview.style.height	= preview.offsetHeight	+ ( (targetRect[3] - originRect[3]) / ANI_STEPS) + 'px';
		// Keep timer going
		animationIntervall++;
		timerID = setTimeout("animateOpenLargeImage()", 20);
	}
}
function openLargeImage()
{
	var listView = document.getElementById('listView');
	var background = document.getElementById('previewBackground');
	var preview = document.getElementById('preview');
	var linkElement = currentItem.getElementsByTagName('a')[0]; // Get the first A element
	// Ensure correct size and position
	background.style.left	= targetRect[0] + 'px';
	background.style.top	= targetRect[1] + 'px';
	background.style.width	= (targetRect[2] - 2) + 'px'; // (!) Account for 1px border
	background.style.height	= (targetRect[3] - 2) + 'px'; // (!) Account for 1px border
	preview.style.left		= (targetRect[0] + 1) + 'px';
	preview.style.top		= (targetRect[1] + 1) + 'px';
	preview.style.width		= (targetRect[2] - 2) + 'px';
	preview.style.height	= (targetRect[3] - 2) + 'px';
	// Get image information
	var imageTitle, imageWidth, imageHeight;
	imageTitle = linkElement.getElementsByTagName('img')[0].title; // (!) debug
	imageWidth = cacheImage.width;
	imageHeight = cacheImage.height;
	// Test for the existance of '#largePreviewContainer'.
	// If it exist, use the existing elements, otherwise
	// create new ones.
	var largePreviewContainer;
	if ( document.getElementById('largePreviewContainer') )
	{
		var title = document.getElementById('largePreviewTitle');
		var imageSize = document.getElementById('largePreviewImageSize');
		var currentImageIndex = document.getElementById('largePreviewCurrentIndex');
		var linkViewFullSize = document.getElementById('largePreviewLinkViewFullSize');
		var largePreview = document.getElementById('largePreview');
		var newLargePreview = document.createElement('img');
		// Update image information
		title.replaceChild( document.createTextNode(imageTitle), title.childNodes[0] );
		imageSize.replaceChild( document.createTextNode(imageWidth + 'x' + imageHeight), imageSize.childNodes[0] );
		currentImageIndex.replaceChild( document.createTextNode(currentIndex+1), currentImageIndex.childNodes[0] );
		linkViewFullSize.href = cacheImage.src;
		// Add the new image and resize it
		newLargePreview.src = cacheImage.src;
		newLargePreview.className = 'thumbnail';
		if (cacheImage.width > 620 || cacheImage.height > 470)
		{
			if (cacheImage.width > cacheImage.height)
			{
				newLargePreview.style.width = '620px';
				newLargePreview.style.height = '470px';
			}
			else
			{
				newLargePreview.style.width = '353px';
				newLargePreview.style.height = '470px';
			}
		}
		largePreview.parentNode.replaceChild(newLargePreview, largePreview);
		newLargePreview.id = 'largePreview';
	}
	else
	{
		largePreviewContainer = document.createElement('div');	largePreviewContainer.id = 'largePreviewContainer';
		var largePreview = document.createElement('img');		largePreview.id = 'largePreview';
		var title = document.createElement('h4');				title.id = 'largePreviewTitle';
		var information = document.createElement('div');
		var imageSize = document.createElement('span');			imageSize.id = 'largePreviewImageSize';
		var navigation = document.createElement('div');			navigation.id = 'previewNavigation';
		var linkClose = document.createElement('a');
		var linkNext = document.createElement('a');
		var linkPrev = document.createElement('a');
		var optionNewWindow = document.createElement('input');
		optionNewWindow.type = 'checkbox';
		// Add image title and dimention
		title.appendChild( document.createTextNode(imageTitle) );
		imageSize.appendChild( document.createTextNode(imageWidth + 'x' + imageHeight) );
		information.appendChild( title );
		information.appendChild( imageSize );
		information.appendChild( document.createTextNode(' pixels') );
		// If image it larger than the large view, then
		// resize the image to fit the content.
		// (!) Dynamicly calculate size
		largePreview.className = 'thumbnail';
		largePreview.src = cacheImage.src;
		if (cacheImage.width > 620 || cacheImage.height > 470)
		{
			if (cacheImage.width > cacheImage.height)
			{
				largePreview.style.width = '620px';
				largePreview.style.height = '470px';
			}
			else
			{
				largePreview.style.width = '353px';
				largePreview.style.height = '470px';
			}
			// Add Full View link
			var linkViewFullSize = document.createElement('a');
			linkViewFullSize.id = 'largePreviewLinkViewFullSize';
			linkViewFullSize.href = cacheImage.src;
			linkViewFullSize.appendChild( document.createTextNode('View Full Size') );
			information.appendChild( document.createTextNode(' (') );
			information.appendChild(linkViewFullSize);
			information.appendChild( document.createTextNode(')') );
		}
		// Find out the index of the current image and total image count
		var listViewItems = listView.getElementsByTagName('li');
		for (var i = 0; i < listViewItems.length; i++)
		{
			if (listViewItems[i] == linkElement.parentNode)
			{
				// Create element containing the number of the current image
				var currentImageIndex = document.createElement('span');
				currentImageIndex.id = 'largePreviewCurrentIndex';
				currentImageIndex.appendChild( document.createTextNode(i + 1) );
				// Append the number to the description. ('# of # | ')
				navigation.appendChild(currentImageIndex);
				navigation.appendChild( document.createTextNode(' of ' + (listViewItems.length - 1) + ' | ') );
				currentIndex = i;
				break;
			}
		}
		// Create navigation elements
		linkClose.appendChild(document.createTextNode('Close Preview'));
		linkNext.appendChild(document.createTextNode('Next'));
		linkPrev.appendChild(document.createTextNode('Previous'));
		linkClose.href = 'javascript:closePreview()';
		linkPrev.href = 'javascript:prevPreview()';
		linkNext.href = 'javascript:nextPreview()';
		// Assemble navigation elements
		navigation.appendChild(linkPrev);
		navigation.appendChild(document.createTextNode(' | '));
		navigation.appendChild(linkNext);
		// Assemble everything
		largePreviewContainer.appendChild(largePreview);
		largePreviewContainer.appendChild(information);
		largePreviewContainer.appendChild(navigation);
		//largePreviewContainer.appendChild(optionNewWindow);
		//largePreviewContainer.appendChild(document.createTextNode(' Open full view in new window | '));
		largePreviewContainer.appendChild(linkClose);
		// Add to document
		preview.appendChild(largePreviewContainer);
	}
}

function closePreview()
{
	cacheImage.src = ''; // Cancel loading (!) Clear onload event as well?

	var background = document.getElementById('previewBackground');
	var preview = document.getElementById('preview');

	// (!) Check for opacity support
	//if (background.style.opacity)
	//{
		background.style.opacity = 0.8; // (!) Needs to be set for Firefox to recognize the opacity value
		preview.style.opacity = 1; // Ditto
		animationIntervall = 0; // Reset interval to start a new animation
	//}
	//else
	//{
	//	animationIntervall = ANI_STEPS; // Set interval to max value to skip animation
	//}
	// Start timer
	timerID = setTimeout("animateClosePreview()", 20); // (!) Test for opacity support
}
function animateClosePreview()
{
	var background = document.getElementById('previewBackground');
	var preview = document.getElementById('preview');

	if (animationIntervall >= ANI_STEPS)
	{
		// Stop timer as the animation is done
		clearTimeout(timerID);
		animationIntervall = 0;
		// Animation done. Remove elements
		background.parentNode.removeChild(background);
		preview.parentNode.removeChild(preview);
	}
	else
	{
		background.style.opacity = 0.8 - (0.16 * animationIntervall);
		background.style.filter = 'alpha(opacity=' + (background.style.opacity * 100) + ')';
		preview.style.opacity = 1 - (0.2 * animationIntervall);
		preview.style.filter = 'alpha(opacity=' + (preview.style.opacity * 100) + ')';
		// Keep timer going
		animationIntervall++;
		timerID = setTimeout("animateClosePreview()", 20);
	}
}
function nextPreview()
{
	var listView = document.getElementById('listView');
	var listViewItems = listView.getElementsByTagName('li');
	// Adjust currentIndex
	currentIndex++;
	if (currentIndex >= (listViewItems.length - 1))
		currentIndex = 0;
	// Start loading the image
	currentItem = listViewItems[currentIndex];
	loadLargeImage();
}
function prevPreview()
{
	var listView = document.getElementById('listView');
	var listViewItems = listView.getElementsByTagName('li');
	// Adjust currentIndex
	currentIndex--;
	if (currentIndex < 0)
		currentIndex = listViewItems.length - 2;
	// Start loading the image
	currentItem = listViewItems[currentIndex];
	loadLargeImage();
}
//} // End of classDynamicGallery

/* ===== GENERAL FUNCTIONS ===== */
// Gets absolute position of an element
// http://www.quirksmode.org/js/findpos.html
function RECT(left, right, top, bottom)
{
	this.left = left;
	this.right = right;
	this.top = top;
	this.bottom = bottom;

	function width()
	{
		return this.right - this.left;
	}
	function height()
	{
		return this.bottom - this.top;
	}
}

function findPos(obj)
{
	var curleft = curtop = 0;
	if (obj.offsetParent) {
		curleft = obj.offsetLeft
		curtop = obj.offsetTop
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft
			curtop += obj.offsetTop
		}
	}
	return [curleft,curtop];
}
function getRect(obj)
{
	var curleft = curtop = curWidth = curHeight = 0;
	if (obj.offsetParent) {
		curleft = obj.offsetLeft;
		curtop = obj.offsetTop;
		curWidth = obj.offsetWidth;
		curHeight = obj.offsetHeight;
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft;
			curtop += obj.offsetTop;
		}
	}
	return [curleft, curtop, curWidth, curHeight];
	//return new RECT(curleft, curleft+curWidth, curtop, curtop+curHeight);
}
function setPosition(element, position)
{
	// (!) Make sure element exist and position is a RECT
	//element.style.position = 'absolute';
	element.style.left = position.left;
	element.style.top = position.top;
	element.style.width = position.width();
	element.style.height = position.height();
}

// --- COOKIE --- //
function createCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

function eraseCookie(name) {
	createCookie(name,"",-1);
}

// --- ARRAY FUNCTIONS --- //
function fixPopPush()
{
	Array.prototype.push = function() {
	    var n = this.length >>> 0;
	    for (var i = 0; i < arguments.length; i++) {
		this[n] = arguments[i];
		n = n + 1 >>> 0;
	    }
	    this.length = n;
	    return n;
	};

	Array.prototype.pop = function() {
	    var n = this.length >>> 0, value;
	    if (n) {
		value = this[--n];
		delete this[n];
	    }
	    this.length = n;
	    return value;
	};
}

// --- SNIFFER --- //
function supportAJAX()
{
	var http_request = false;
    if (window.XMLHttpRequest) { // IE7, Mozilla, Safari, ...
        http_request = new XMLHttpRequest();
    } else if (window.ActiveXObject) { // IE 6-
        try {
            http_request = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                http_request = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e) {}
        }
    }
    return http_request;
}