// **************************************************
// The configuration
// **************************************************

/** The flag if images should be preloaded */
var preloadImages = true;

/** The flag if single click should be used instead of two clicks */
var singleClick = true;

/** The number of preview slots */
var previewSlots = 6;

/** The height of the content and nav2 block */
var animationHeight = 280;
/** The step-size for the animation */
var animationStep = 10;
/** The time for one step of the animation */
var animationTimer = 50;

// **************************************************
// The variables
// **************************************************

/** The flag if hovering is allowed */
var enableHover = true;

/** The id of the last clicked project */
var clickedProject = false;

/** The next step-size of the animation */
var nextStep = false;

/** The flag if the interaction is currently blocked */
var blockInteraction = false;

/** The flag if the animation is currently blocked */
var blockAnimation = false;

// **************************************************
// The data-structures for projects
// **************************************************

/**
 * Constructor of class doubleimage.
 *
 * @param lowUrl   The url of the low-res image
 * @param highUrl  The url of the high-res image
 */
function DoubleImage(lowUrl, highUrl) {
	this.lowUrl = lowUrl;
	this.highUrl = highUrl;
}

/**
 * Constructor of class project.
 *
 * @param id    The unique id of the project
 * @param type  The type of the project (1=image, 2=page, 3=photo, 4=text)
 */
function Project(id, type) {
	this.id = id;
	this.type = type;
	this.hoverSlot = false;
	this.text1 = '';
	this.text2 = '';
	this.previewSlots = new Array();
	this.images = new Array();
	this.backgroundimage = '';
	this.download = '';
}

/**
 * Sets the number of the hovered preview slot of the project.
 * 
 * @param hoverSlot  The number of the hovered preview slot
 */
Project.prototype.setHoverSlot = function(hoverSlot) {
	this.hoverSlot = hoverSlot;
}

/**
 * Sets the texts of the project.
 * 
 * @param text1  The first line of text
 * @param text2  The second line of text
 */
Project.prototype.setText = function(text1, text2) {
	this.text1 = escapeSpecialChars(text1);
	this.text2 = escapeSpecialChars(text2);
}

/**
 * Adds the image numbers for a preview slots of the project.
 * 
 * @param previewSlot  The number of the image of a preview slot
 */
Project.prototype.addPreviewSlot = function(previewSlot) {
	this.previewSlots[this.previewSlots.length] = previewSlot;
}

/**
 * Gets the number of the image for a slot.
 *
 * @param slot  The number of the preview slot
 */
Project.prototype.getSlotImageNumber = function(slot) {
	if (Number(slot) <= 0 || Number(slot) > previewSlots) alert('Error: getSlotImageNumber() with wrong slot(' + slot + ')');
	var imageNumber = this.previewSlots[Number(slot) - 1];
	if (imageNumber == '') return false;
	return imageNumber;
}

/**
 * Gets the image.
 *
 * @param imageNumber  The number of the image
 */
Project.prototype.getImage = function(imageNumber) {
	if (Number(imageNumber) <= 0 || Number(imageNumber) > this.images.length) alert('Error: getImage() with wrong number(' + imageNumber + ')');
	return this.images[Number(imageNumber) - 1];
}

/**
 * Adds an image to the project.
 * 
 * @param image  The image
 */
Project.prototype.addImage = function(image) {
	this.images[this.images.length] = image;
}

/**
 * Sets the background-image to the project.
 * 
 * @param urlImg  The url of the background-image
 */
Project.prototype.setBackgroundImage = function(urlImg) {
	this.backgroundimage = urlImg;
}

/**
 * Sets the download for the project.
 * 
 * @param url  The url of the downloadable file
 */
Project.prototype.setDownload = function(url) {
	this.download = url;
}


// **************************************************
// The stored projects, images and texts
// **************************************************

/** The projects */
var projects = new Array();

/**
 * Adds a project.
 *
 * @param id            The id of the project
 * @param type          The type of the project
 * @param text1         The first line of text
 * @param text2         The second line of text
 * @param previewHover  The number of the hovered preview slot
 *
 * @param previewM-N    The (six) numbers of preview images
 */
function addProject(id, type, text1, text2, hoverSlot) {
	var fixArguments = 5;
	
	var project = new Project(id, type);
	project.setHoverSlot(hoverSlot);
	project.setText(text1, text2);
	
	var optionalArguments = addProject.arguments.length - fixArguments;
	for (var i = 0; i < optionalArguments; i++) {
		project.addPreviewSlot(addProject.arguments[fixArguments + i]);
	}
	
	projects[projects.length] = project;
}

/**
 * Adds an image to the last project.
 *
 * @param prefix   The prefix for both urls
 * @param lowUrl   The url of the low-res image
 * @param highUrl  The url of the high-res image
 */
function addImage(prefix, lowUrl, highUrl) {
	var image = new DoubleImage((lowUrl != '' ? prefix + lowUrl : ''), (highUrl != '' ? prefix + highUrl : ''));
	if (projects.length == 0) alert('Error: addImage(prefix, lowUrl, highUrl) called without addProject() before');
	projects[projects.length - 1].addImage(image);
}

/**
 * Sets the download for the last project.
 *
 * @param url  The url of the downloadable file
 */
function setDownload(url) {
	if (projects.length == 0) alert('Error: setDownload(url) called without addProject() before');
	projects[projects.length - 1].setDownload(url);
}

/**
 * Adds a text.
 *
 * @param id     The id of the project
 * @param text1  The first line of text
 * @param text2  The second line of text
 * @param image  The url of the background-image
 */
function addText(id, text1, text2, image) {
	var project = new Project(id, 4);
	project.setText(text1, text2);
	project.setBackgroundImage(image);
	
	projects[projects.length] = project;
}

// **************************************************
// Events for mouse-over and mouse-click
// **************************************************

/**
 * Event handler for hovering a project.
 *
 * @param id    The unique id of the project
 * @param node  The node to set link active
 * @return      false
 */
function hoverProjectId(id, node) {
	if (!enableHover) return false;
	if (blockInteraction || blockAnimation) return false;
	
	// set active state to current link
	blockInteraction = true;
	setId(node, 'activenav');
	
	for (var i = 0; i < projects.length; i++) {
		if (projects[i].id == id) {
			hoverProject(projects[i]);
			break;
		}
	}
	if (i == projects.length) alert('Error: hoverProjectId(id, node) with unknown project-id(' + id + ')');
	blockInteraction = false;
	return false;
}

/**
 * Event handler for clicking a project.
 *
 * @param id    The unique id of the project
 * @param node  The node to set link active
 * @return      false
 */
function clickProjectId(id, node) {
	if (blockInteraction || blockAnimation) return false;
	
	// set active state to current link
	blockInteraction = true;
	setId(node, 'activenav');
	
	for (var i = 0; i < projects.length; i++) {
		if (projects[i].id == id) {
			clickProject(projects[i]);
			break;
		}
	}
	if (i == projects.length) alert('Error: clickProjectId(id, node) with unknown project-id(' + id + ')');
	blockInteraction = false;
	return false;
}

/**
 * Event handler for opening a project.
 *
 * @param id           The unique id of the project
 * @param imageNumber  The number of the shown image
 * @param node         The node to set link active
 * @return             false
 */
function openProjectId(id, imageNumber, node) {
	if (blockInteraction || blockAnimation) return false;
	
	// set active state to current link
	blockInteraction = true;
	setId(node, 'activelink');
	
	for (var i = 0; i < projects.length; i++) {
		if (projects[i].id == id) {
			if (openProjectId.arguments.length < 2) {
				openProject(projects[i]);
			} else {
				openProject(projects[i], imageNumber);
			}
			break;
		}
	}
	if (i == projects.length) alert('Error: openProjectId(id, imageNumber, node) with unknown project-id(' + id + ')');
	blockInteraction = false;
	return false;
}

// **************************************************
//  Private functions
// **************************************************

/**
 * Shows the hovered preview image.
 *
 * @param project  The project
 */
function hoverProject(project) {
	// skip hovering already clicked project
	if (clickedProject == project.id) return;
	clickedProject = false;
	
	setTextLines(project);
	if (project.type != 4) {
		// set preview images
		for (var i = 1; i <= previewSlots; i++) {
			if (i == project.hoverSlot) {
				// set hover image
				setPreviewImage(i, project);
			} else {
				// clean other images
				setPreviewImage(i)
			}
		}
	} else { // project.type == 4
		setPreviewBackgroundImage(project.backgroundimage);
	}
	
	preloadPreviewImages(project);
}

/**
 * Shows all preview images.
 *
 * @param project  The project
 */
function clickProject(project) {
	if (blockAnimation) return;
	
	preloadHighResImages(project);
	
	// click twice or text-type opens the project
	if (!singleClick && clickedProject == project.id && project.type != 4) {
		openProject(project);
		return;
	}
	
	clickedProject = project.id;
	setTextLines(project);
	if (project.type != 4) {
		// set preview images
		for (var i = 1; i <= previewSlots; i++) {
			setPreviewImage(i, project);
		}
		if (singleClick) openProject(project);
	} else { // project.type == 4
		setPreviewBackgroundImage(project.backgroundimage);
		openProject(project, 0);
	}
}

/**
 * Shows the content image.
 *
 * @param project      The project
 * @param imageNumber  The number of the shown image
 * @return             false
 */
function openProject(project, imageNumber) {
	if (blockAnimation) return false;
	
	if (openProject.arguments.length < 2) imageNumber = project.getSlotImageNumber(project.hoverSlot);
	
	// visible nodes
	var showContentImage = false;
	var showContentPage = false;
	var showContentPhoto = false;
	var showContentText = false;
	switch (project.type) {
		case 1:
			showContentImage = true;
			contentImage(project, imageNumber);
			break;
		case 2:
			showContentPage = true;
			contentPage(project, imageNumber);
			break;
		case 3:
			showContentPhoto = true;
			contentPhoto(project, imageNumber);
			break;
		case 4:
			showContentText = true;
			contentText(project);
			break;
		default:
			alert('Error: openProject(project, imageNumber) with wrong project-type(' + project.type + ')');
			return false;
	}
	
	// show/hide content nodes
	var nDiv;
	nDiv = document.getElementById('contentimage');
	if (nDiv == null) alert('Error: openProject(project, imageNumber) could not find element with id "contentimage"');
	else nDiv.style.display = showContentImage ? 'block' : 'none';
	nDiv = document.getElementById('contentpage');
	if (nDiv == null) alert('Error: openProject(project, imageNumber) could not find element with id "contentpage"');
	else nDiv.style.display = showContentPage ? 'block' : 'none';
	nDiv = document.getElementById('contentphoto');
	if (nDiv == null) alert('Error: openProject(project, imageNumber) could not find element with id "contentphoto"');
	else nDiv.style.display = showContentPhoto ? 'block' : 'none';
	nDiv = document.getElementById('contenttext');
	if (nDiv == null) alert('Error: openProject(project, imageNumber) could not find element with id "contenttext"');
	else nDiv.style.display = showContentText ? 'block' : 'none';
	
	showContent();
	
	return false;
}

/**
 * Shows the content image.
 *
 * @param project      The project
 * @param imageNumber  The number of the shown image
 */
function contentImage(project, imageNumber) {
	var nImg = document.getElementById('contentimageimg');
	if (nImg == null) alert('Error: contentImage(project, imageNumber) could not find element with id "contentimageimg"');
	nImg.src = project.getImage(imageNumber).highUrl;
}

/**
 * Shows the content page.
 *
 * @param project      The project
 * @param imageNumber  The number of the shown image
 */
function contentPage(project, imageNumber) {
	hidePreviewImages();
	
	// set content image
	var nImg = document.getElementById('contentpageimg');
	if (nImg == null) alert('Error: contentPage(project, imageNumber) could not find element with id "contentpageimg"');
	nImg.src = project.getImage(imageNumber).highUrl;
	
	// recreate nodes of sub-tree
	var nDiv = document.getElementById('contentpagelinks');
	if (nDiv == null) alert('Error: contentPage(project, imageNumber) could not find element with id "contentpagelinks"');
	removeChildNodes(nDiv);
	for (var i = 1; i <= project.images.length; i++) {
		var nLink = document.createElement('a');
		//var nText = document.createTextNode(' ');
		//nLink.appendChild(nText);
		nLink.className = 'contentpagelink';
		nLink.href = 'javascript:void;';
		if (i == imageNumber) nLink.id = 'activelink';
		setChangePageEvent(nLink, project.id, i);
		nDiv.appendChild(nLink);
	}
	var nBr = document.createElement('br');
	nBr.style.clear = 'both';
	nDiv.appendChild(nBr);
	
	// append link for pdf-download
	if (project.download != '') {
		var nP = document.createElement('p');
		nP.className = 'downloadpdf';
		var nLink = document.createElement('a');
		nLink.href = project.download;
		var nText = document.createTextNode('> download PDF');
		nLink.appendChild(nText);
		nP.appendChild(nLink);
		nDiv.appendChild(nP);
	}
}

/**
 * Shows the content photo.
 *
 * @param project      The project
 * @param imageNumber  The number of the shown image
 */
function contentPhoto(project, imageNumber) {
	hidePreviewImages();
	
	// set content image
	var nImg = document.getElementById('contentphotoimg');
	if (nImg == null) alert('Error: contentPage(project, imageNumber) could not find element with id "contentphotoimg"');
	nImg.src = project.getImage(imageNumber).highUrl;
	
	// recreate nodes of sub-tree
	var nDiv = document.getElementById('contentphotolinks');
	if (nDiv == null) alert('Error: contentPage(project, imageNumber) could not find element with id "contentphotolinks"');
	removeChildNodes(nDiv);
	for (var i = 1; i <= project.images.length; i++) {
		var nLink = document.createElement('a');
		//var nText = document.createTextNode(' ');
		//nLink.appendChild(nText);
		nLink.className = 'contentphotolink';
		nLink.href = 'javascript:void;';
		if (i == imageNumber) nLink.id = 'activelink';
		setChangePhotoEvent(nLink, project.id, i);
		nDiv.appendChild(nLink);
	}
	var nBr = document.createElement('br');
	nBr.style.clear = 'both';
	nDiv.appendChild(nBr);
	
	// append link for pdf-download
	if (project.download != '') {
		var nP = document.createElement('p');
		nP.className = 'downloadpdf';
		var nLink = document.createElement('a');
		nLink.href = project.download;
		var nText = document.createTextNode('> download PDF');
		nLink.appendChild(nText);
		nP.appendChild(nLink);
		nDiv.appendChild(nP);
	}
}

/**
 * Shows the content text.
 *
 * @param project  The project
 */
function contentText(project) {
	var nDiv = document.getElementById('contenttext');
	if (nDiv == null) alert('Error: contentText(project) could not find element with id "contenttext"');
	
	// show given content text and hide others
	if (nDiv.childNodes != null) {
		for (var i = 0; i < nDiv.childNodes.length; i++) {
			var node = nDiv.childNodes[i];
			switch (node.nodeType) {
				case 1:
					node.style.display = (node.id == 'contenttext_' + project.id) ? 'block' : 'none';
					break;
				case 3:
					break;
				default:
					alert('Error: contentText(project) iterating over child nodes of id "contenttext" - found node type ' + node.nodeType);
					break;
			}
		}
	}
}

/**
 * Sets the event to change page.
 *
 * @param node         The link-node
 * @param id           The project-id
 * @param imageNumber  The number of the shown image
 */
function setChangePageEvent(node, id, imageNumber) {
	node.onclick = function(){return changePage(id, imageNumber, node);};
}

/**
 * Sets the event to change photo.
 *
 * @param node         The link-node
 * @param id           The project-id
 * @param imageNumber  The number of the shown image
 */
function setChangePhotoEvent(node, id, imageNumber) {
	node.onclick = function(){return changePhoto(id, imageNumber, node);};
}

/**
 * Changes the page.
 *
 * @param id           The unique id of the project
 * @param imageNumber  The number of the shown image
 * @param node         The node to set link active
 * @return             false
 */
function changePage(id, imageNumber, node) {
	var project = null;
	for (var i = 0; i < projects.length; i++) {
		if (projects[i].id == id) {
			project = projects[i];
			break;
		}
	}
	if (project == null) alert('Error: changePage(id, imageNumber, node) with wrong project id(' + id + ')');
	
	var nImg = document.getElementById('contentpageimg');
	if (nImg == null) alert('Error: changePage(id, imageNumber, node) could not find element with id "contentpageimg"');
	nImg.src = project.getImage(imageNumber).highUrl;
	
	// set active state to current link
	setId(node, 'activelink');
	
	return false;
}

/**
 * Changes the photo.
 *
 * @param id           The unique id of the project
 * @param imageNumber  The number of the shown image
 * @param node         The node to set link active
 * @return             false
 */
function changePhoto(id, imageNumber, node) {
	var project = null;
	for (var i = 0; i < projects.length; i++) {
		if (projects[i].id == id) {
			project = projects[i];
			break;
		}
	}
	if (project == null) alert('Error: changePhoto(id, imageNumber, node) with wrong project id(' + id + ')');
	
	var nImg = document.getElementById('contentphotoimg');
	if (nImg == null) alert('Error: changePhoto(id, imageNumber, node) could not find element with id "contentphotoimg"');
	nImg.src = project.getImage(imageNumber).highUrl;
	
	// set active state to current link
	setId(node, 'activelink');
	
	return false;
}

/**
 * Sets the preview image.
 *
 * @param slot     The number of the preview slot
 
 * @param project  The project
 */
function setPreviewImage(slot, project) {
	hidePreviewBackgroundImage();
	
	if (setPreviewImage.arguments.length < 2) project = null;
	
	var nLink;
	nLink = document.getElementById('previewA' + slot);
	if (nLink == null) alert('Error: setPreviewImage(slot, project) could not find element with id "previewA' + slot + '"');
	var nImg;
	nImg = document.getElementById('previewImg' + slot);
	if (nImg == null) alert('Error: setPreviewImage(slot, project) could not find element with id "previewImg' + slot + '"');
	
	var image = new DoubleImage('', '');
	var imageNumber = false;
	if (project) {
		imageNumber = project.getSlotImageNumber(slot);
		if (imageNumber) image = project.getImage(imageNumber);
	}
	if (image.lowUrl == '') {
		// hide preview image
		nLink.style.display = 'none';
		nLink.href = 'javascript:void;';
		nLink.onclick = function(){return false;};
		nImg.src = '';
	} else {
		// show preview image
		nImg.src = image.lowUrl;
		nLink.href = '#';
		nLink.onclick = function(){return openProject(project, imageNumber);};
		nLink.style.display = 'inline';
	}
}

/**
 * Hides the preview image.
 *
 * @param slot     The number of the preview slot
 */
function hidePreviewImage(slot) {
	var nLink;
	nLink = document.getElementById('previewA' + slot);
	if (nLink == null) alert('Error: hidePreviewImage(slot) could not find element with id "previewA' + slot + '"');
	nLink.style.display = 'none';
}

/**
 * Hides the preview images.
 */
function hidePreviewImages() {
	for (var i = 1; i <= previewSlots; i++) {
		hidePreviewImage(i);
	}
}

/**
 * Sets the preview background-image.
 *
 * @param imgUrl  The url of the background-image
 */
function setPreviewBackgroundImage(imgUrl) {
	hidePreviewImages();
	
	var nDiv;
	nDiv = document.getElementById('preview');
	if (nDiv == null) alert('Error: setPreviewBackgroundImage(imgUrl) could not find element with id "preview"');
	
	nDiv.style.backgroundImage = 'url(' + imgUrl + ')';
}

/**
 * Hides the preview background-image.
 */
function hidePreviewBackgroundImage() {
	var nDiv;
	nDiv = document.getElementById('preview');
	if (nDiv == null) alert('Error: hidePreviewBackgroundImage() could not find element with id "preview"');
	
	nDiv.style.backgroundImage = 'none';
}

/**
 * Sets the text lines.
 *
 * @param project  The project
 */
function setTextLines(project) {
	var nSpan;
	nSpan = document.getElementById('text1');
	if (nSpan == null) alert('Error: setTextLines(project) could not find element with id "text1"');
	removeChildNodes(nSpan);
	appendTextNode(nSpan, project.text1 ? '> ' + project.text1 : '');
	nSpan = document.getElementById('text2');
	if (nSpan == null) alert('Error: setTextLines(project) could not find element with id "text2"');
	removeChildNodes(nSpan);
	appendTextNode(nSpan, project.text2 ? '> ' + project.text2 : '');
}

/**
 * Shows the content and hides the secondary navigation.
 */
function showContent() {
	var nContent = document.getElementById('content');
	if (nContent == null) alert('Error: showContent() could not find element with id "content"');
	var nNav = document.getElementById('nav2');
	if (nNav == null) alert('Error: showContent() could not find element with id "nav2"');
	
	var contentHeight = parseInt(nContent.style.height);
	var navHeight = parseInt(nNav.style.height);
	// skip animation if already shown
	if (contentHeight == animationHeight && navHeight == 0) return;
	
	nContent.style.height = '0px';
	nNav.style.height = animationHeight + 'px';
	nContent.style.display = 'block';
	nNav.style.display = 'block';
	blockAnimation = true;
	window.setTimeout('moveContent()', animationTimer);
}

/**
 * Hides the content and shows the secondary navigation.
 *
 * @return  false
 */
function hideContent() {
	var nContent = document.getElementById('content');
	if (nContent == null) alert('Error: hideContent() could not find element with id "content"');
	var nNav = document.getElementById('nav2');
	if (nNav == null) alert('Error: hideContent() could not find element with id "nav2"');
	
	// skip when content is already hidden
	if (nContent.style.display == 'none') return false;
	
	var contentHeight = parseInt(nContent.style.height);
	var navHeight = parseInt(nNav.style.height);
	// skip animation if already hidden and no size is nan
	if (!isNaN(contentHeight) && !isNaN(navHeight) && (contentHeight != animationHeight || navHeight != 0)) return false;
	
	nContent.style.height = animationHeight + 'px';
	nNav.style.height = '0px';
	nContent.style.display = 'block';
	nNav.style.display = 'block';
	enableHover = true;
	blockAnimation = true;
	window.setTimeout('moveContent()', animationTimer);
	
	return false;
}

/**
 * Moves the content and the secondary navigation.
 */
function moveContent() {
	var nContent = document.getElementById('content');
	if (nContent == null) alert('Error: moveContent() could not find element with id "content"');
	var nNav = document.getElementById('nav2');
	if (nNav == null) alert('Error: moveContent() could not find element with id "nav2"');
	
	var contentHeight = parseInt(nContent.style.height);
	var navHeight = parseInt(nNav.style.height);
	// set step-size and direction on first call
	if (nextStep == false) {
		nextStep = (contentHeight < navHeight) ? animationStep : - animationStep;
		enableHover = false;
	}
	// animation-step
	contentHeight += nextStep;
	navHeight -= nextStep;
	nContent.style.height = contentHeight + 'px';
	nNav.style.height = navHeight + 'px';
	if (contentHeight <= 0 || navHeight <= 0) {
		if (contentHeight < 0 || navHeight < 0) alert('Error: moveContent() with negative height for a node');
		// finish animation
		nextStep = false;
		if (contentHeight == 0) nContent.style.display = 'none';
		else if (navHeight == 0) nNav.style.display = 'none';
		enableHover = true;
		blockAnimation = false;
	} else {
		// timer for next animation-step
		window.setTimeout('moveContent()', animationTimer);
	}
}

/**
 * Sets the preview image of the download.
 *
 * @param slot    The number of the preview slot
 * @param imgUrl  The rl of the image
 */
function setDownloadImage(slot, imgUrl) {
	hidePreviewImages();
	hidePreviewBackgroundImage();
	
	var nLink;
	nLink = document.getElementById('previewA' + slot);
	if (nLink == null) alert('Error: setDownloadImage(slot, imgUrl) could not find element with id "previewA' + slot + '"');
	var nImg;
	nImg = document.getElementById('previewImg' + slot);
	if (nImg == null) alert('Error: setDownloadImage(slot, imgUrl) could not find element with id "previewImg' + slot + '"');
	
	// show preview image
	nImg.src = imgUrl;
	nLink.href = 'javascript:void;';
	nLink.onclick = function(){return false;};
	nLink.style.display = 'inline';
}

/**
 * Preloads the hover and background images.
 */
function preloadHoverImages() {
	if (!preloadImages) return;
	for (var i = 0; i < projects.length; i++) {
		var project = projects[i]
		var hoverSlot = project.hoverSlot;
		if (hoverSlot != false) {
			var imageNumber = project.getSlotImageNumber(hoverSlot);
			var img = project.getImage(imageNumber);
			if (img.lowUrl == '' || typeof img.lowUrl == 'undefined') alert('hover image of project "'+project.id+'" is not set');
			else preloadImageNow(img.lowUrl);
		}
		var bgImg = project.backgroundImage;
		if (bgImg != '') preloadImageNow(bgImg);
	}
}

/**
 * Preloads the preview images.
 *
 * @param project  The project
 */
function preloadPreviewImages(project) {
	if (!preloadImages) return;
	for (var i = 0; i < previewSlots; i++) {
		var imageNumber = project.getSlotImageNumber(i + 1);
		if (imageNumber != false) {
			var img = project.getImage(imageNumber);
			if (img.lowUrl == '' || typeof img.lowUrl == 'undefined') alert('preview image of project "'+project.id+'" in slot "'+(i + 1)+'" is not set');
			else preloadImageNow(img.lowUrl);
		}
	}
}

/**
 * Preloads the high-res images.
 *
 * @param project  The project
 */
function preloadHighResImages(project) {
	if (!preloadImages) return;
	for (var i = 0; i < project.images.length; i++) {
		var img = project.images[i];
		if (img.highUrl == '' || typeof img.lowUrl == 'undefined') alert('high-res image number "'+(i + 1)+'" of project "'+project.id+'" is not set');
		else preloadImageNow(img.highUrl);
	}
}

// Function library

/**
 * Appends a text node.
 *
 * @param node  The parent node
 * @param text  The text
 */
function appendTextNode(node, text) {
	var nText = document.createTextNode(text);
	node.appendChild(nText);
}

/**
 * Removes all child nodes.
 *
 * @param node  The parent node
 */
function removeChildNodes(node) {
	if (node.childNodes != null) {
		for (var i = 0; node.childNodes.length; i++) {
			node.removeChild(node.childNodes[i]);
		}
	}
}

/**
 * Sets the id of a node.
 *
 * @param node  The node
 * @param id    The id
 */
function setId(node, id) {
	var nAny = document.getElementById(id);
	if (nAny != null) nAny.id = null;
	if (node) {
		node.id = id;
		node.blur();
	}
}

/**
 * Escapes special characters.
 *
 * @param text  The string
 */
function escapeSpecialChars(text) {
	var retval = text;
	// escape umlaute
	var search = new Array();
	var replace = new Array();
	search.push(/&szlig;/);
	replace.push(String.fromCharCode(223));
	search.push(/&auml;/);
	replace.push(String.fromCharCode(228));
	search.push(/&ouml;/);
	replace.push(String.fromCharCode(246));
	search.push(/&uuml;/);
	replace.push(String.fromCharCode(252));
	for (var i = 0; i < search.length; i++) {
		while (retval.search(search[i]) != -1) {
			retval = retval.replace(search[i], replace[i]);
		}
	}
	return retval;
}

