// debugging for browsers using Firebug -----------------------------------------------------------
var TURN_DEBUG_MESSAGES_ON = true;

function DEBUG(str) {
	if (TURN_DEBUG_MESSAGES_ON) {
		try {
			console.log(str);
		}
		catch (e) {}
	}
}



// initialize the temporary panel to display while waiting for external content to load -----------
/*
 * YUI loader is not required here, since it will be initialized according to the current color set
 *

if (typeof yuiLoader == 'undefined') {
	var yuiLoader = {
		hide: function() {},
		show: function() {}
	};
	
	YAHOO.util.Event.onContentReady('containerPage', function() {
		yuiLoader = new YAHOO.widget.Panel('boxYuiLoader', {
			autofillheight: 'body',
			constraintoviewport: true,
			fixedcenter: true,
			close: false,
			draggable: false,
			zindex: 100,
			modal: false,
			visible: false
		});
		yuiLoader.setBody('<img src="/_images/loading.gif"/>');
		yuiLoader.render(YAHOO.util.Dom.get('containerPage'));
	});
}
*/
if (typeof yuiLoader == 'undefined') {
	var yuiLoader = {
		hide: function() {},
		show: function() {}
	};
}
if (typeof yuiLoader == 'object') {
	try {
		if (document.getElementById('containerPage')) {
			initYuiLoader();
		}
		else {
			YAHOO.util.Event.onAvailable('containerPage', initYuiLoader);
		}
	}
	catch (e) {
		yuiLoader = {
			hide: function() {},
			show: function() {}
		};
		DEBUG('Warning: could not initialize loader panel.');
	}
}



// common functions and attributes ----------------------------------------------------------------
var BB = function() {
	var _private = {
		lang: '',
		xhrCount: 0,
		/*-----------------------------------------------------------------------------------------
		 * sort an array using merge sort (recursive function)
		 *
		 * params:
		 *   array: the array to be sorted
		 *   indexes: array containing indexes referring to the input array
		 *   start: index of the position to start sorting at
		 *   end: index of the first position after the position to stop sorting at
		 */
		mergeSortRec: function(array, indexes, start, end) {
			var i = start;
			var j = parseInt(start + (end - start) / 2);
			var iEnd = parseInt(start + (end - start) / 2);
			var jEnd = end;
			var index = 0;
			var tmp = new Array();
			var tmpIndexes = new Array();
			var tmpArray = new Array();

			// break on empty array or array containing just one item
			if (end - start < 2) {
				return;
			}
			// recurse
			if (end - start > 2) {
				_private.mergeSortRec(array, indexes, start, iEnd);
				_private.mergeSortRec(array, indexes, iEnd, end);
			}
			// merge sorted partial arrays
			while (i < iEnd && j < jEnd) {
				// move empty strings to the end
				if ((array[i] == null || array[i] == '') && (array[j] != null && array[j] != '')) {
					tmp[index++] = j++;
				}
				else if ((array[i] != null && array[i] != '') && (array[j] == null || array[j] == '')) {
					tmp[index++] = i++;
				}
				// sort non-empty strings
				else if (array[i] <= array [j]) {
					tmp[index++] = i++;
				}
				else {
					tmp[index++] = j++;
				}
			}
			// copy rest of partial arrays
			while (i < iEnd) {
				tmp[index++] = i++;
			}
			while (j < jEnd) {
				tmp[index++] = j++;
			}
			// apply new order to input arrays
			for (var k=0; k<end-start; k++) {
				tmpArray[k] = array[tmp[k]];
				tmpIndexes[k] = indexes[tmp[k]];
			}
			for (var k=0; k<end-start; k++) {
				array[start+k] = tmpArray[k];
				indexes[start+k] = tmpIndexes[k];
			}
			// clean up
			delete tmp;
			delete tmpArray;
			delete tmpIndexes;
		}
	};
	var _public = {
		/*-----------------------------------------------------------------------------------------
		 * get language
		 *
		 * return value:
		 *   a string containing the short name of the current language (e.g. '', 'de', 'en')
		 */
		getLang: function() {
			return _private.lang;
		},
		/*-----------------------------------------------------------------------------------------
		 * get language path
		 *
		 * return value:
		 *   a string containing the name of the directory containing files for the current
		 *   language (e.g. '', 'de/', 'en/')
		 */
		getLangPath: function() {
			if (_private.lang == '') {
				return '';
			}
			else {
				return _private.lang + '/';
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * check if a string represents a single letter
		 *
		 * params:
		 *   c: a string
		 * return value:
		 *   true if c represents a single letter, false otherwise
		 */
		isAlpha: function(c) {
			if (   typeof c == 'string' && c.length == 1
			    && ((65 <= c.charCodeAt(0) && c.charCodeAt(0) <= 90)         /* A...Z */
			        || (97 <= c.charCodeAt(0) && c.charCodeAt(0) <= 122))) { /* a...z */
				return true;
			}
			else {
				return false;
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * check if a value is an integer
		 *
		 * params:
		 *   no: the value to check
		 * return value:
		 *   true if no is an integer, false otherwise
		 */
		isInteger: function(no) {
			if (typeof no == 'number' && no == Math.floor(no)) {
				return true;
			}
			else {
				return false;
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * check if a value is a natural number (i.e. it is a positive integer)
		 *
		 * params:
		 *   no: the value to check
		 * return value:
		 *   true if no is a natural number, false otherwise
		 */
		isNatural: function(no) {
			if (_public.isInteger(no) && no > 0) {
				return true;
			}
			else {
				return false;
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * sort an array using merge sort
		 *
		 * params:
		 *   array: the array to be sorted
		 * return value:
		 *   an array of sorted indexes referring to the input array
		 */
		mergeSort: function(array) {
			var indexes = new Array();

			for (var i=0; i<array.length; i++) {
				indexes[i] = i;
			}
			_private.mergeSortRec(array, indexes, 0, array.length);

			return indexes;
		},
		/*-----------------------------------------------------------------------------------------
		 * replace special chars (like umlauts) in a string by alphabetic letters
		 *
		 * params:
		 *   str: a string
		 * return value:
		 *   a string derived from str
		 */
		replaceSpecialChars: function(str) {
			if (typeof str != 'string') {
				return '';
			}
			//str = str.replace('\u009F', 'Y');  // Ÿ

			//str = str.replace('\u00C0', 'A');  // À
			//str = str.replace('\u00C1', 'A');  // Á
			//str = str.replace('\u00C2', 'A');  // Â
			//str = str.replace('\u00C3', 'A');  // Ã
			str = str.replace('\u00C4', 'A');  // Ä
			//str = str.replace('\u00C5', 'A');  // Å
			//str = str.replace('\u00C6', 'A');  // Æ
			//str = str.replace('\u00C7', 'C');  // Ç
			//str = str.replace('\u00C8', 'E');  // È
			//str = str.replace('\u00C9', 'E');  // É
			//str = str.replace('\u00CA', 'E');  // Ê
			//str = str.replace('\u00CB', 'E');  // Ë
			//str = str.replace('\u00CC', 'I');  // Ì
			//str = str.replace('\u00CD', 'I');  // Í
			//str = str.replace('\u00CE', 'I');  // Î
			//str = str.replace('\u00CF', 'I');  // Ï

			//str = str.replace('\u00D1', 'N');  // Ñ
			//str = str.replace('\u00D2', 'O');  // Ò
			//str = str.replace('\u00D3', 'O');  // Ó
			//str = str.replace('\u00D4', 'O');  // Ô
			//str = str.replace('\u00D5', 'O');  // Õ
			str = str.replace('\u00D6', 'O');  // Ö

			//str = str.replace('\u00D8', 'O');  // Ø
			//str = str.replace('\u00D9', 'U');  // Ù
			//str = str.replace('\u00DA', 'U');  // Ú
			//str = str.replace('\u00DB', 'U');  // Û
			str = str.replace('\u00DC', 'U');  // Ü
			//str = str.replace('\u00DD', 'Y');  // Ý

			str = str.replace('\u00DF', 'ss'); // ß
			//str = str.replace('\u00E0', 'a');  // à
			//str = str.replace('\u00E1', 'a');  // á
			//str = str.replace('\u00E2', 'a');  // â
			//str = str.replace('\u00E3', 'a');  // ã
			str = str.replace('\u00E4', 'a');  // ä
			//str = str.replace('\u00E5', 'a');  // å
			//str = str.replace('\u00E6', 'a');  // æ
			//str = str.replace('\u00E7', 'c');  // ç
			//str = str.replace('\u00E8', 'e');  // è
			//str = str.replace('\u00E9', 'e');  // é
			//str = str.replace('\u00EA', 'e');  // ê
			//str = str.replace('\u00EB', 'e');  // ë
			//str = str.replace('\u00EC', 'i');  // ì
			//str = str.replace('\u00ED', 'i');  // í
			//str = str.replace('\u00EE', 'i');  // î
			//str = str.replace('\u00EF', 'i');  // ï

			//str = str.replace('\u00F1', 'n');  // ñ
			//str = str.replace('\u00F2', 'o');  // ò
			//str = str.replace('\u00F3', 'o');  // ó
			//str = str.replace('\u00F4', 'o');  // ô
			//str = str.replace('\u00F5', 'o');  // õ
			str = str.replace('\u00F6', 'o');  // ö

			//str = str.replace('\u00F8', 'o');  // ø
			//str = str.replace('\u00F9', 'u');  // ù
			//str = str.replace('\u00FA', 'u');  // ú
			//str = str.replace('\u00FB', 'u');  // û
			str = str.replace('\u00FC', 'u');  // ü
			//str = str.replace('\u00FD', 'y');  // ý

			//str = str.replace('\u00FF', 'y');  // ÿ

			return str;
		},
		/*-----------------------------------------------------------------------------------------
		 * set language
		 *
		 * params:
		 *   lang: a string containing the short name of the current language (e.g. '', 'de', 'en')
		 */
		setLang: function(lang) {
			_private.lang = lang;
		},
		/*-----------------------------------------------------------------------------------------
		 * truncate a decimal number
		 *
		 * params:
		 *   decimal: the decimal number to truncate (number or string)
		 * return value:
		 *   a string representation of the truncated number (empty string in case of an error)
		 */
		truncateDecimal: function(decimal) {
			if (typeof decimal != 'number' && typeof decimal != 'string') {
				return '';
			}
			if (typeof decimal == 'number') {
				decimal = decimal.toString();
			}
			decimal = decimal.replace(/^\s+/, '').replace(/\s+$/, '');

			if (BB.getLang() == 'de') {
				if (decimal.match(/,/)) {
					decimal = decimal.replace(/0+$/, '');
					decimal = decimal.replace(/,$/, '');
				}
			}
			else {
				if (decimal.match(/\./)) {
					decimal = decimal.replace(/0+$/, '');
					decimal = decimal.replace(/\.$/, '');
				}
			}
			return decimal;
		},
		/*-----------------------------------------------------------------------------------------
		 * increase XHR counter and return the new value
		 */
		xhrCount: function() {
			return ++_private.xhrCount;
		}
	};
	return _public;
}();
var Forms = function() {
	var _private = {
		/*-----------------------------------------------------------------------------------------
		 * hide all options, i.e. close all all selects
		 * /
		hideAllOptions: function() {
			var selects = YAHOO.util.Dom.getElementsByClassName('boxSelect', 'div', 'containerContent');

			for (var i=0; i<selects.length; i++) {
				try {
					var button = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'boxSelectButton');
					}, 'div', selects[i]);
					var boxOptions = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'boxSelectOptions');
					}, 'div', selects[i]);
					var options = YAHOO.util.Dom.getElementsBy(function(elem) {
						return true;
					}, 'li', boxOptions);
					var border = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'boxSelectBorder');
					}, 'div', selects[i]);

					YAHOO.util.Dom.removeClass(selects[i], 'open');
					selects[i].style.overflow = 'hidden';

					for (var j = 0; j < options.length; j++) {
						if (YAHOO.util.Dom.hasClass(options[j], 'liSelected')) {
							boxOptions.style.top = '-' + (j * 16) + 'px';
							break;
						}
					}
					button.style.backgroundPosition = '0 0';
					border.style.display = 'block';
				}
				catch (e) {
					// ignore misstructured selects
				}
			}
		},
*/
		/*-----------------------------------------------------------------------------------------
		 * some Boolean functions for evaluating character classes (required by parseEmail())
		 *
		 * params:
		 *   c: an integer representation of a character (this is evaluated where required)
		 */
		isAllowed: function(c) {
			return (_private.isAlNum(c) || _private.isOtherAllowed(c));
		},
		isAlNum: function(c) {
			return (_private.isAlpha(c) || _private.isDigit(c));
		},
		isAlpha: function(c) {
			if (c!=Math.floor(c)) {
				return false;
			}
			return (c>=65 && c<=90 || c>=97 && c<=122); // "A", ..., "Z", "a", ..., "z"
		},
		isDigit: function(c) {
			if (c!=Math.floor(c)) {
				return false;
			}
			return (c>=48 && c<=57); // "0", ..., "9"
		},
		isOtherAllowed: function(c) {
			if (c!=Math.floor(c)) {
				return false;
			}
			return (c==45 || c==46 || c==64 || c==95); // "-", ".", "@", "_"
		},
		/*-----------------------------------------------------------------------------------------
		 * quickly parse an e-mail for syntactical correctness (this will not recognize
		 * non-existent TLDs and/or domains)
		 *
		 * params:
		 * 		email: the string to be parsed
		 */
		parseEmail: function(email) {
			if (typeof email != "string") {
				return false;
			}

			// check if the e-mail address contains any characters that must not be there
			for (var i=0; i<email.length; i++) {
				if (!_private.isAllowed(email.charCodeAt(i))) {
					return false;
				}
			}
			// the first character must be alpha-numerical
			if (!_private.isAlNum(email.charCodeAt(0))) {
				return false;
			}
			// check if there is one and only one "@"
			var atIndex = email.indexOf("@");

			if (atIndex<0 || atIndex!=email.lastIndexOf("@")) {
				return false;
			}
			// make sure there is at least one "." after "@", followed by 2-6 letters
			var dotIndex = email.lastIndexOf(".");

			if (dotIndex<0 || dotIndex<atIndex || dotIndex<email.length-7 || dotIndex>email.length-3) {
				return false;
			}
			for (var i=dotIndex+1; i<email.length; i++) {
				if (!_private.isAlpha(email.charCodeAt(i))) {
					return false;
				}
			}
			// there must not be any character from {"@", "_", "-", "."} followed by another
			// character from the same set
			var otherAllowedIndex = -1;

			for (var i=1; i<email.length; i++) {
				if (_private.isOtherAllowed(email.charCodeAt(i))) {
					if (otherAllowedIndex == i-1) {
						return false;
					}
					else {
						otherAllowedIndex = i;
					}
				}
			}
			return true;
		},
		/*-----------------------------------------------------------------------------------------
		 * add handlers to stylable replacement for <select> tag
		 *
		 * params:
		 *   select: reference to a <div> tag containing the structure to replace a <select> tag
		 * /
		setSelectHandlers: function(select) {
			var button = YAHOO.util.Dom.getElementBy(function(elem) {
				return YAHOO.util.Dom.hasClass(elem, 'boxSelectButton');
			}, 'div', select);
			var boxOptions = YAHOO.util.Dom.getElementBy(function(elem) {
				return YAHOO.util.Dom.hasClass(elem, 'boxSelectOptions');
			}, 'div', select);
			var options = YAHOO.util.Dom.getElementsBy(function(elem) {
				return true;
			}, 'li', boxOptions);
			var border = YAHOO.util.Dom.getElementBy(function(elem) {
				return YAHOO.util.Dom.hasClass(elem, 'boxSelectBorder');
			}, 'div', select);

			// handle button click
			YAHOO.util.Event.addListener(button, 'click', function() {
				// call hideAllOptions() in any case, but evaluate the current state first
				if (select.style.overflow == 'visible') {
					_private.hideAllOptions();
				}
				else {
					_private.hideAllOptions();
					YAHOO.util.Dom.addClass(select, 'open');
					select.style.overflow = 'visible';
					boxOptions.style.top = '0';
					button.style.backgroundPosition = '0 100%';
					border.style.display = 'none';
				}
			});
			// handle option clicks
			YAHOO.util.Event.addListener(options, 'click', function() {
				YAHOO.util.Dom.removeClass(options, 'liSelected');
				YAHOO.util.Dom.addClass(this, 'liSelected');
				YAHOO.util.Dom.getElementBy(function(elem) {
					return true;
				}, 'input', select).value = YAHOO.util.Dom.getAttribute(this, 'id').replace(/^[^_]*_/, '').replace(/_/g, ' ');
				_private.hideAllOptions();
			});
			// handle "border" click
			YAHOO.util.Event.addListener(border, 'click', function() {
				// border div shows up if and only if select div is closed
				_private.hideAllOptions();
				YAHOO.util.Dom.addClass(select, 'open');
				select.style.overflow = 'visible';
				boxOptions.style.top = '0';
				button.style.backgroundPosition = '0 100%';
				border.style.display = 'none';
			});
		},
		/*-----------------------------------------------------------------------------------------
		 * set initial value of stylable replacement for <select> tag by getting the first option
		 * with class name "liSelected" or - if there is none - by getting the first option
		 *
		 * params:
		 *   select: reference to a <div> tag containing the structure to replace a <select> tag
		 * /
		setSelectInitialValue: function(select) {
			var boxOptions = YAHOO.util.Dom.getElementBy(function(elem) {
				return YAHOO.util.Dom.hasClass(elem, 'boxSelectOptions');
			}, 'div', select);
			var options = YAHOO.util.Dom.getElementsBy(function(elem) {
				return true;
			}, 'li', boxOptions);

			if (   YAHOO.util.Dom.getElementsByClassName('liSelected', 'li', boxOptions).length == 0
			    && options.length > 0) {
				var value = YAHOO.util.Dom.getElementBy(function(elem) {
					return true;
				}, 'input', select).value;

				if (value) {
					// search the "selected" option and mark it as selected
					value = value.replace(/\ /g, '_');

					for (var i=0; i<options.length; i++) {
						if (YAHOO.util.Dom.getAttribute(options[i], 'id').replace(/^[^_]*_/, '') == value) {
							YAHOO.util.Dom.addClass(options[i], 'liSelected');
							boxOptions.style.top = '-' + (i * 16) + 'px';
							break;
						}
					}
				}
				else {
					// use the value of the first option
					YAHOO.util.Dom.addClass(options[0], 'liSelected');
					YAHOO.util.Dom.getElementBy(function(elem) {
						return true;
					}, 'input', select).value = YAHOO.util.Dom.getAttribute(options[0], 'id').replace(/^[^_]*_/, '').replace(/_/g, ' ');
				}
			}
			else if (option.length > 0) {
				// use the value of the first option marked as selected and reset all other options
				var foundSelection = false;

				for (var i=0; i<options.length; i++) {
					if (foundSelection) {
						YAHOO.util.Dom.removeClass(options[i], 'liSelected');
					}
					else if (YAHOO.util.Dom.hasClass(options[i], 'liSelected')) {
						foundSelection = $true;
						YAHOO.util.Dom.getElementBy(function(elem) {
							return true;
						}, 'input', select).value = YAHOO.util.Dom.getAttribute(options[i], 'id').replace(/[^_]*_/, '').replace(/_/g, ' ');
					}
				}
			}
		},
*/
		/*-----------------------------------------------------------------------------------------
		 * trim a string
		 *
		 * params:
		 *   str: the string to be trimmed
		 * return value:
		 *   the trimmed string if a string was passed to the function, the empty string otherwise
		 */
		trim: function(str) {
			if (typeof str != "string") {
				return "";
			}
			return str.replace(/^\s+/, "").replace(/\s+$/, "");
		}
	};
	var _public = {
		/*-----------------------------------------------------------------------------------------
		 * check if all required fields are filled in and if a given e-mail address is
		 * syntactically valid
		 *
		 * return value:
		 *   true if alle tests have been passed, false otherwise
		 */
		checkForm: function() {
			var ok = true;
			var error = document.getElementById("boxFormError");
			var errorRequired = document.getElementById("boxFormErrorRequired");
			var errorEmail = document.getElementById("boxFormErrorEmail");

			error.style.display = "none";
			errorRequired.style.display = "none";
			errorEmail.style.display = "none";

			// check required fields
			var required = YAHOO.util.Dom.getElementsBy(function(elem) {
				try {
					var tag = elem.tagName.toLowerCase();
					
					return (tag == "input" || tag == "select" || tag == "textarea") && YAHOO.util.Dom.hasClass(elem, "required");
				}
				catch (e) {
					return false;
				}
			}, null, "containerContent");

			YAHOO.util.Dom.removeClass(YAHOO.util.Dom.getElementsByClassName("missingField", "div", "containerContent"), "missingField");

			for (var i=0; i<required.length; i++) {
				if (_private.trim(required[i].value).length == 0) {
					var container = YAHOO.util.Dom.getAncestorBy(required[i], function(elem) {
						return YAHOO.util.Dom.hasClass(elem, "boxCombinedOuter");
					});

					YAHOO.util.Dom.addClass(container, "missingField");
					error.style.display = "block";
					errorRequired.style.display = "block";
					ok = false;
				}
			}
			// check e-mail address
			var inputEmail = document.getElementById("inputEmail");
			var valueEmail = inputEmail.value;

			if (valueEmail.length > 0) {
				var container = YAHOO.util.Dom.getAncestorBy(inputEmail, function(elem) {
					return YAHOO.util.Dom.hasClass(elem, "boxCombinedOuter");
				});

				if (!_private.parseEmail(valueEmail)) {
					YAHOO.util.Dom.addClass(container, "missingField");
					error.style.display = "block";
					errorEmail.style.display = "block";
					ok = false;
				}
			}
			return ok;
		},
		/*-----------------------------------------------------------------------------------------
		 * initialize renderable file upload fields
		 */
		initFileFake: function() {
			var boxes = YAHOO.util.Dom.getElementsByClassName("boxInputFile", "div");

			for (var i=0; i<boxes.length; i++) {
				var inputFile = YAHOO.util.Dom.getElementsBy(function(elem) {
					return (elem.type == "file");
				}, "input", boxes[i]);

				if (inputFile.length == 1) {
					var inputFileName = document.createElement("input");

					inputFile[0].style.display = "block";

					inputFileName.type = "text";
					inputFileName.className = "inputFileName text";
					inputFileName.style.opacity = "0";
					inputFileName.style.filter = "alpha(opacity=0)";
					inputFileName.style.zIndex = "1";
					boxes[i].appendChild(inputFileName);

					inputFile[0].parentNode.appendChild(inputFileName);
					inputFile[0].relatedElement = inputFileName;

					YAHOO.util.Event.addListener(inputFile[0], "click", function () {
						this.relatedElement.style.opacity = "1";
						this.relatedElement.style.filter = "alpha(opacity=100)";
					});
					YAHOO.util.Event.addListener(inputFile[0], "focus", function () {
						this.relatedElement.style.opacity = "1";
						this.relatedElement.style.filter = "alpha(opacity=100)";
					});
					YAHOO.util.Event.addListener(inputFile[0], "change", function () {
						if ((this.relatedElement.value = this.value.replace(/^.*\\/, "")) == "") {
							this.relatedElement.style.opacity = "0";
							this.relatedElement.style.filter = "alpha(opacity=0)";
						}
						else if (!(/\.jpg$/.test(this.value) || /\.jpeg$/.test(this.value) || /\.jpe$/.test(this.value) || /\.gif$/.test(this.value) || /\.png$/.test(this.value))) {
							this.relatedElement.value = this.value = "";
							this.relatedElement.style.opacity = "0";
							this.relatedElement.style.filter = "alpha(opacity=0)";
						}
					});
					YAHOO.util.Event.addListener(inputFile[0], "blur", function () {
						if ((this.relatedElement.value = this.value.replace(/^.*\\/, "")) == "") {
							this.relatedElement.style.opacity = "0";
							this.relatedElement.style.filter = "alpha(opacity=0)";
						}
						else if (!(/\.jpg$/.test(this.value) || /\.jpeg$/.test(this.value) || /\.jpe$/.test(this.value) || /\.gif$/.test(this.value) || /\.png$/.test(this.value))) {
							this.relatedElement.value = this.value = "";
							this.relatedElement.style.opacity = "0";
							this.relatedElement.style.filter = "alpha(opacity=0)";
						}
					});
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * render labels over input fields
		 */
		initOverLabels: function() {
			var labels;
			var id;
			var field;

			// set focus and blur handlers to hide and show labels with class name "overLabel"
			labels = document.getElementsByTagName('label');

			for (var i=0; i<labels.length; i++) {
				if (/\boverLabel\b/.test(labels[i].className)) {
					// skip labels that do not have a named association with another field
					id = labels[i].htmlFor || labels[i].getAttribute('for');
					if (!id || !(field = document.getElementById(id))) {
						continue;
					}
					// change the applied class to hover the label over the form field
					labels[i].className = labels[i].className.replace(/\boverLabel\b/g, 'overLabelApply');
					_public.toggleLabelDisplay(id, false);

					// hide any fields having an initial value
					if (field.value !== '') {
						_public.toggleLabelDisplay(field.getAttribute('id'), true);
					}
					// set handlers to show and hide labels
					field.onfocus = function () {
						_public.toggleLabelDisplay(this.getAttribute('id'), true);
					};
					field.onblur = function () {
						if (this.value === '') {
							_public.toggleLabelDisplay(this.getAttribute('id'), false);
						}
					};
					// handle clicks to label elements (for Safari)
					labels[i].onclick = function () {
						var id, field;
						id = this.getAttribute('for');
						if (id && (field = document.getElementById(id))) {
							field.focus();
						}
					};
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * initialize stylable replacements for <select> tags
		 * /
		initSelects: function() {
			var selects = YAHOO.util.Dom.getElementsByClassName('boxSelect', 'div', 'containerContent');
			var inputs = YAHOO.util.Dom.getElementsBy(function(elem) {
				return true;
			}, 'input'); // get all input fields (including login and search)

			for (var i=0; i<selects.length; i++) {
				try {
					var boxOptions = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'boxSelectOptions');
					}, 'div', selects[i]);
					var options = YAHOO.util.Dom.getElementsBy(function(elem) {
						return true;
					}, 'li', boxOptions);

					boxOptions.style.height = '' + (options.length * 16 - 2) + 'px';
					_private.setSelectInitialValue(selects[i]);
					_private.setSelectHandlers(selects[i]);
				} 
				catch (e) {
					// ignore misstructured selects
				}
			}
			YAHOO.util.Event.addListener(inputs, 'click', function() {
				_private.hideAllOptions();
			});
		},
*/
		/*-----------------------------------------------------------------------------------------
		 * reset form and rerender labels
		 *
		 * params:
		 *   form: reference or ID of the form to reset
		 */
		resetForm: function(form) {
			var labels;
			var id;
			var field;

			if (!form) {
				return;
			}
			labels = YAHOO.util.Dom.getElementsByClassName('overLabelApply', 'label', form);

			for (var i=0; i<labels.length; i++) {
				if (!(id = labels[i].htmlFor || labels[i].getAttribute('for'))) {
					continue;
				}
				if (id && (field = document.getElementById(id))) {
					field.value = '';
					_public.toggleLabelDisplay(id, false);
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * toggle display of label
		 *
		 * params:
		 *   fieldID: ID of the corresponding form element
		 *   hide:    hide the label if this is set to true, show the label otherwise
		 */
		toggleLabelDisplay: function(fieldID, hide) {
			var field = document.getElementById(fieldID);

			if (!field) {
				return false;
			}
			if (hide) {
				//field.style.backgroundColor = '#ffffff';
				field.style.opacity = '1';
				field.style.filter = 'alpha(opacity:100)';
			}
			else {
				//field.style.backgroundColor = 'transparent';
				field.style.opacity = '0';
				field.style.filter = 'alpha(opacity:0)';
			}
			return true;
		}
	};
	return _public;
}();

YAHOO.util.Event.onDOMReady(function() {
	window.setTimeout(Forms.initOverLabels, 50);
	window.setTimeout(Forms.initFileFake, 100);
});
var Basket = function() {
	var _private = {
		basketLight: null,
		cookie: PBEURL('').replace(/\?/, ''),
		/*-----------------------------------------------------------------------------------------
		 * callback when weight or count of an item are changed (basket on normal order)
		 *
		 * prerequisities:
		 *   Input fields for count and weight must have been parsed before, i.e. they must contain
		 *   either valid numerical values (as strings) or empty strings.
		 * side effects:
		 *   The basket will be reloaded thus restoring the initial weight and count values of all
		 *   items in the basket.
		 */
		listenOnChange: function() {
			var code = this.code;
			var count = this.inputCount.value;
			var load = (Shop.getDisplayMode() == 'basketFast') ? Content.loadBasketFast : Content.loadBasket;

			if (this.inputWeight) {
				var weight = this.inputWeight.value.replace(',', '.');
				var weightOld = this.weight.replace(',', '.');

				if (count.length > 0 && weight.length > 0) {
					load('a-cFrontend_ShopBasketDetail-n_ChangeCount=' + count + '&a-cFrontend_ShopBasketDetail-d_ChangeWeight=' + weight + '&a-cFrontend_ShopBasketDetail-d_ChangeWeightOld=' + weightOld + '&a-cFrontend_ShopBasketDetail-sz_ERPCodeDel=' + code + '&button-cFrontend_ShopBasketDetail-changeBasket=');
				}
				else {
					load();
				}
			}
			else {
				if (count.length > 0) {
					load('a-cFrontend_ShopBasketDetail-n_ChangeCount=' + count + '&a-cFrontend_ShopBasketDetail-d_ChangeWeight=0&a-cFrontend_ShopBasketDetail-d_ChangeWeightOld=0&a-cFrontend_ShopBasketDetail-sz_ERPCodeDel=' + code + '&button-cFrontend_ShopBasketDetail-changeBasket=');
				}
				else {
					load();
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * callback for removing a single item from the basket (basket on normal order)
		 */
		listenOnDelete: function() {
			var code = this.code;
			var weight = this.weight.replace(/,/g, '.');
			var load = (Shop.getDisplayMode() == 'basketFast') ? Content.loadBasketFast : Content.loadBasket;

			load('a-cFrontend_ShopBasketDetail-sz_ERPCodeDel=' + code + '&a-cFrontend_ShopBasketDetail-d_WeightDel=' + weight + '&a-cFrontend_ShopBasketDetail-delFromBasket=x');
		},
		/*-----------------------------------------------------------------------------------------
		 * callback for displaying details on an item in the basket (on normal order)
		 */
		listenOnShow: function() {
			Shop.displayItemDetailBasket(this.pos);
		}
	};

	var _public = {
		getBasketLight: function() {
			if (!_private.basketLight) {
				_public.initBasketLight();
			}
			return _private.basketLight;
		},
		initBasketLight: function() {
			_private.basketLight = new YAHOO.widget.Module('basketLight', {
				visible: true
			});
			_private.basketLight.setHeader('');
			_private.basketLight.setBody('');
			_private.basketLight.render('boxBasketLight');
		},
		loadBasketLight: function() {
			var callback = {
				success: function(o) {
					if (!_private.basketLight) {
						_public.initBasketLight();
					}
					_private.basketLight.setBody(o.responseText);
					_private.basketLight.render('boxBasketLight');
					YAHOO.util.Event.removeListener(document.getElementById('boxBasketLight'));
					YAHOO.util.Event.addListener(document.getElementById('boxBasketLight'), 'click', Content.loadBasket);
					YAHOO.util.Event.removeListener(document.getElementById('aBasketLight'));
					YAHOO.util.Event.addListener(document.getElementById('aBasketLight'), 'click', Content.loadBasket);
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('loading basket light timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketLight.html?button-oFrontend_ShopBasketLight-fillBasketLight=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};

			yuiLoader.show();
			YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketLight.html?button-oFrontend_ShopBasketLight-fillBasketLight=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
		},
		modifyChangeDeleteLinks: function() {
			var itemCount = YAHOO.util.Dom.getElementsByClassName('dataCode', 'input', 'containerBasket').length;

			for (var i=1; i<=itemCount; i++) {
				var obj = {
					rowNo: i,
					row: document.getElementById('boxItemBundlesRow' + i),
					bulk: document.getElementById('dataBulk' + i).value != '0' ? true : false,
					liquid: document.getElementById('dataLiquid' + i).value != '0' ? true : false,
					code: document.getElementById('dataCode' + i).value,
					weight: document.getElementById('dataWeight' + i).value,
					price: parseFloat(document.getElementById('dataPrice' + i).value),
					unit: document.getElementById('dataUnit' + i).value,
					inputCount: document.getElementById('inputCount' + i),
					inputCountOld: document.getElementById('inputCountOld' + i),
					inputWeight: document.getElementById('inputWeight' + i),
					inputWeightOld: document.getElementById('inputWeightOld' + i),
					iconChange: document.getElementById('iconChange' + i),
					iconDelete: document.getElementById('iconDelete' + i),
					spanWeightTotal: document.getElementById('spanWeightTotal' + i),
					boxPriceSingle: document.getElementById('boxPriceSingle' + i),
					boxPriceTotal: document.getElementById('boxPriceTotal' + i),
					min: 1,
					multiple: false
				};

				if (obj.bulk) {
					if (obj.liquid) {
						YAHOO.util.Event.addListener(obj.inputCount, 'change', function() {
							this.inputCount.value = Shop.checkInputCount(this.inputCount.value, this.inputCountOld.value);
							this.inputCountOld.value = this.inputCount.value;
							Shop.updateItemDetailRowLiquid(this.rowNo, this.inputCount.value, this.inputWeight.value, this.price);
							YAHOO.util.Dom.addClass(this.row, 'changed');
						}, obj, true);
						YAHOO.util.Event.addListener(obj.inputWeight, 'change', function() {
							this.inputWeight.value = Shop.checkInputCount(this.inputWeight.value, this.inputWeightOld.value);
							this.inputWeightOld.value = this.inputWeight.value;
							Shop.updateItemDetailRowLiquid(this.rowNo, this.inputCount.value, this.inputWeight.value, this.price);
							YAHOO.util.Dom.addClass(this.row, 'changed');
						}, obj, true);
					}
					else {
						YAHOO.util.Event.addListener(obj.inputCount, 'change', function() {
							this.inputCount.value = Shop.checkInputCount(this.inputCount.value, this.inputCountOld.value);
							this.inputCountOld.value = this.inputCount.value;
							Shop.updateItemDetailRowBulk(this.rowNo, this.inputCount.value, this.inputWeight.value, this.price);
							YAHOO.util.Dom.addClass(this.row, 'changed');
						}, obj, true);
						YAHOO.util.Event.addListener(obj.inputWeight, 'change', function() {
							this.inputWeight.value = Shop.checkInputWeight(this.inputWeight.value, this.inputWeightOld.value);
							this.inputWeightOld.value = this.inputWeight.value;
							Shop.updateItemDetailRowBulk(this.rowNo, this.inputCount.value, this.inputWeight.value, this.price);
							YAHOO.util.Dom.addClass(this.row, 'changed');
						}, obj, true);
					}
				}
				else {
					if (obj.code.match(/10$/)) {
						obj.min = 10;
					}
					else if (obj.code.match(/44$/) || obj.code.match(/55$/) || obj.code.match(/54$/)) {
						obj.min = 6;
						obj.multiple = true;
					}
					YAHOO.util.Event.addListener(obj.inputCount, 'change', function() {
						this.inputCount.value = Shop.checkInputCount(this.inputCount.value, this.inputCountOld.value, this.min, this.multiple);
						this.inputCountOld.value = this.inputCount.value;
						YAHOO.util.Dom.addClass(this.row, 'changed');
					}, obj, true);
				}
				// add event listeners
				YAHOO.util.Event.addListener(obj.iconChange, 'click', _private.listenOnChange, obj, true);
				YAHOO.util.Event.addListener(obj.iconDelete, 'click', _private.listenOnDelete, obj, true);

				// add key listeners
				if (obj.inputCount) {
					var keyListenerCount = new YAHOO.util.KeyListener(obj.inputCount, {
						keys: [0x0a, 0x0d]
					}, {
						fn: _private.listenOnChange,
						scope: obj,
						correctScope: true
					});

					keyListenerCount.enable();
				}
				if (obj.inputWeight) {
					var keyListenerWeight = new YAHOO.util.KeyListener(obj.inputWeight, {
						keys: [0x0a, 0x0d]
					}, {
						fn: _private.listenOnChange,
						scope: obj,
						correctScope: true
					});

					keyListenerWeight.enable();
				}
				var keyListenerChange = new YAHOO.util.KeyListener(obj.iconChange, {
					keys: [0x0a, 0x0d]
				}, {
					fn: _private.listenOnChange,
					scope: obj,
					correctScope: true
				});
				var keyListenerDelete = new YAHOO.util.KeyListener(obj.iconDelete, {
					keys: [0x0a, 0x0d]
				}, {
					fn: _private.listenOnDelete,
					scope: obj,
					correctScope: true
				});

				keyListenerChange.enable();
				keyListenerDelete.enable();

				delete obj;
			}
		},
		modifyDecimals: function() {
			var weights = YAHOO.util.Dom.getElementsByClassName('inputWeight', 'input', 'containerBasket');
			var weightsOld = YAHOO.util.Dom.getElementsByClassName('inputWeightOld', 'input', 'containerBasket');
			var weightsTotal = YAHOO.util.Dom.getElementsByClassName('spanWeightTotal', 'span', 'containerBasket');

			for (var i=0; i<weights.length; i++) {
				weights[i].value = BB.truncateDecimal(weights[i].value);
			}
			for (var i=0; i<weightsOld.length; i++) {
				weightsOld[i].value = BB.truncateDecimal(weightsOld[i].value);
			}
			for (var i=0; i<weightsTotal.length; i++) {
				weightsTotal[i].innerHTML = BB.truncateDecimal(weightsTotal[i].innerHTML);
			}
		},
		modifyNames: function() {
			var filters = Filter.getSettings();
			var names = YAHOO.util.Dom.getElementsByClassName('spanName', 'span', 'containerBasket');
			var latin = YAHOO.util.Dom.getElementsByClassName('spanLatin', 'span', 'containerBasket');

			if (filters.latin) {
				for (var i=0; i<names.length; i++) {
					names[i].style.display = 'none';
					latin[i].style.display = 'block';
				}
			}
			else {
				for (var i=0; i<names.length; i++) {
					names[i].style.display = 'block';
					latin[i].style.display = 'none';
				}
			}
		},
		modifyPagingLinks: function() {
			var shopLink = document.getElementById('aBackToShop');
			var orderLink = document.getElementById('aGoToOrder');
/*
			var shopHandle = function() {
				if (Shop.getNodeId() >= 0) {
					Content.loadShop(Shop.getNodeId());
				}
				else if (Shop.getNodeId() == -1) {
					Content.loadShop();
				}
				else {
					if (BB.getLang() == 'de') {
						window.location = '/sortiment_shop';
					}
					else {
						window.location = '/products_shop';
					}
				}
			};
*/
			var shopHandle = function() {
				var isShop;
				var shopURL;

				// check if the current URL belongs to the shop
				if (BB.getLang() == 'de') {
					if (location.pathname.match(/^\/sortiment_shop$/)) {
						isShop = true;
					}
					else {
						isShop = false;
						shopURL = '/sortiment_shop';
					}
				}
				else {
					if (location.pathname.match(/^\/products_shop$/)) {
						isShop = true;
					}
					else {
						isShop = false;
						shopURL = '/products_shop';
					}
				}
				// determine next page to be shown
				switch (Shop.getDisplayMode()) {
					case 'shop':
						Content.loadShop();
						break;
					case 'search':
						Content.loadSearchResults();
						break;
					case 'favorites':
						Content.loadFavorites();
						break;
					case 'basket':
					case 'basketFast':
					case 'order':
						if (isShop) {
							Shop.init();
						}
						else {
							window.location.replace(shopURL);
						}
						break;
					default:
						DEBUG('warning: illegal value \'' + Shop.getDisplayMode() + '\' of Shop.displayMode');
				}
				Shop.toggleFilters(true);
			};
			var orderHandle = function() {
				yuiLoader.show();
				Content.loadOrder();
			};
			var keyListenerShop = new YAHOO.util.KeyListener(shopLink, {
				keys: [0x0a, 0x0d]
			}, {
				fn: shopHandle
			});
			var keyListenerOrder = new YAHOO.util.KeyListener(orderLink, {
				keys: [0x0a, 0x0d]
			}, {
				fn: orderHandle
			});

			YAHOO.util.Event.removeListener(shopLink);
			YAHOO.util.Event.addListener(shopLink, 'click', shopHandle);
			keyListenerShop.enable();

			YAHOO.util.Event.removeListener(orderLink);
			YAHOO.util.Event.addListener(orderLink, 'click', orderHandle);
			keyListenerOrder.enable();
		},
		modifyFastOrderCodeSubmit: function() {
			var submit = document.getElementById('submitFastOrderCode');
			var keyListener = new YAHOO.util.KeyListener('inputFastOrderCode', {
				keys: [0x0a, 0x0d]
			}, {
				fn: Shop.fastOrderSearch
			});

			keyListener.enable();
			YAHOO.util.Event.removeListener(submit, 'click');
			YAHOO.util.Event.addListener(submit, 'click', Shop.fastOrderSearch);
		},
		modifyShowLinks: function() {
			var links = YAHOO.util.Dom.getElementsByClassName('aShowItem', 'a', 'containerBasket');
			var rowNo;
			var code;
			var pos;

			for (var i=0; i<links.length; i++) {
				rowNo = links[i].getAttribute('id').replace('aShowItemCode', '').replace('aShowItemName', '');
				code = document.getElementById('dataCode' + rowNo).value;
				pos = parseInt(document.getElementById(links[i].getAttribute('id').replace('aShowItemCode', 'dataPosCode').replace('aShowItemName', 'dataPosName')).value);
				YAHOO.util.Event.addListener(links[i], 'click', _private.listenOnShow, {
					code: code,
					pos: pos
				}, true);
			}
		}
	};
	return _public;
}();



var Order = function() {
	var _private = {
		cookie: PBEURL('').replace(/\?/, '')
	};

	var _public = {
		modifyDecimals: function() {
			var weights = YAHOO.util.Dom.getElementsByClassName('spanBundleWeight', 'span', 'containerOrder');
			var weightsTotal = YAHOO.util.Dom.getElementsByClassName('spanWeightTotal', 'span', 'containerOrder');

			for (var i=0; i<weights.length; i++) {
				weights[i].innerHTML = BB.truncateDecimal(weights[i].innerHTML);
			}
			for (var i=0; i<weightsTotal.length; i++) {
				weightsTotal[i].innerHTML = BB.truncateDecimal(weightsTotal[i].innerHTML);
			}
		},
		modifyNames: function() {
			var filters = Filter.getSettings();
			var names = YAHOO.util.Dom.getElementsByClassName('spanName', 'span', 'containerOrder');
			var latin = YAHOO.util.Dom.getElementsByClassName('spanLatin', 'span', 'containerOrder');

			if (filters.latin) {
				for (var i=0; i<names.length; i++) {
					names[i].style.display = 'none';
					latin[i].style.display = 'block';
				}
			}
			else {
				for (var i=0; i<names.length; i++) {
					names[i].style.display = 'block';
					latin[i].style.display = 'none';
				}
			}
		},
		modifyOrderName: function() {
			var orderName = document.getElementById('textareaOrderName');

			YAHOO.util.Event.removeListener(orderName);
			YAHOO.util.Event.addListener(orderName, 'keyup', function() {
				var maxLength = 32;

				if (this.value.length > maxLength) {
					this.value = this.value.substr(0, maxLength);
				}
			});
		},
		modifyPagingLinks: function() {
			var basketLink = document.getElementById('aBackToBasket');
			var replyLink = document.getElementById('aGoToReply');
			var basketHandle = (Shop.getDisplayMode() == 'basketFast') ? Content.loadBasketFast : Content.loadBasket;
			var replyHandle = function() {
				var orderName = document.getElementById('textareaOrderName').value;
				var orderComment = document.getElementById('textareaOrderComment').value;
				var getParams = 'a-cFrontend_ShopBasketDetail-sz_OrderName=' + orderName + '&a-cFrontend_ShopBasketDetail-sz_OrderComment=' + orderComment + '&button-cFrontend_ShopBasketDetail-orderItems=x&' + _private.cookie;

				YAHOO.util.Event.removeListener(replyLink);
				document.getElementById('formOrder').setAttribute('action', '/' + BB.getLangPath() + '_shop/getReply.html?' + getParams);
				document.forms['cFrontend_ShopBasketDetail'].submit();
			};
			var keyListenerBasket = new YAHOO.util.KeyListener(basketLink, {
				keys: [0x0a, 0x0d]
			}, {
				fn: basketHandle
			});
			var keyListenerReply = new YAHOO.util.KeyListener(replyLink, {
				keys: [0x0a, 0x0d]
			}, {
				fn: replyHandle
			});

			YAHOO.util.Event.removeListener(basketLink);
			YAHOO.util.Event.addListener(basketLink, 'click', basketHandle);
			keyListenerBasket.enable();

			YAHOO.util.Event.removeListener(replyLink);
			YAHOO.util.Event.addListener(replyLink, 'click', replyHandle);
			keyListenerReply.enable();
		}
	};
	return _public;
}();



var Reply = function() {
	var _public = {
		modifyPrintLink: function() {
			var link = document.getElementById('aPrintOrder');
			
			YAHOO.util.Event.removeListener(link);
			YAHOO.util.Event.addListener(link, 'click', function() {
				window.print();
			});
		}
	};
	return _public;
}();
/**
 * \brief Replace the content in the main region with new content.
 */
var Content = function() {
	// private functions ==========================================================================
	var _private = {
		cookie: PBEURL('').replace(/\?/, ''),
		media: null,
		/*-----------------------------------------------------------------------------------------
		 * add content loaded via XHR to #containerContentInner
		 */
		addContent: function(contentNew) {
			content = document.getElementById('containerContentInner');

			if (typeof contentNew == 'object') {
				content.appendChild(contentNew);
			}
			else {
				content.innerHTML += contentNew;
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * remove current content from #containerContentInner
		 */
		removeContent: function() {
			var content = document.getElementById('containerContentInner');
			var parent;
			
			if (!content) {
				return;
			}
			parent = content.parentNode;
			parent.removeChild(content);
			content = document.createElement('div');
			content.className = 'containerRightInner';
			content.setAttribute('id', 'containerContentInner');
			parent.appendChild(content);
		},
		/*-----------------------------------------------------------------------------------------
		 * remove class selected from product navigation links
		 */
		removeSelectedLinks: function() {
			var selected = YAHOO.util.Dom.getElementsByClassName('selected', 'a', 'boxNaviLevel2Products');

			YAHOO.util.Dom.removeClass(selected, 'selected');
		},
		/*-----------------------------------------------------------------------------------------
		 * replace paging containers in div#bd by paging containers in includes loaded via XHR
		 */
		updatePaging: function() {
			var paging = {
				topOld: document.getElementById('boxPagingTop'),
				topNew: document.getElementById('boxPagingTopNew'),
				bottomOld: document.getElementById('boxPagingBottom'),
				bottomNew: document.getElementById('boxPagingBottomNew')
			}
			var node;

			// top paging
			if (paging.topOld) {
				// clear old paging
				while (paging.topOld.hasChildNodes()) {
					paging.topOld.removeChild(paging.topOld.firstChild);
				}
				// update paging
				if (paging.topNew) {
					while (paging.topNew.hasChildNodes()) {
						node = paging.topNew.firstChild;
						paging.topNew.removeChild(node);
						paging.topOld.appendChild(node);
					}
				}
			}
			// bottom paging
			if (paging.bottomOld) {
				// clear old paging
				while (paging.bottomOld.hasChildNodes()) {
					paging.bottomOld.removeChild(paging.bottomOld.firstChild);
				}
				// update paging
				if (paging.bottomNew) {
					while (paging.bottomNew.hasChildNodes()) {
						node = paging.bottomNew.firstChild;
						paging.bottomNew.removeChild(node);
						paging.bottomOld.appendChild(node);
					}
				}
			}
		}
	};
	
	// public functions ===========================================================================
	var _public = {
		loadBasket: function(getParams) {
			var callbackItems = {
				success: function(o) {
					var basket;

					try {
						basket = YAHOO.lang.JSON.parse(o.responseText);
					}
					catch (e) {
						DEBUG('could not parse JSON file containing basket items: ' + e.message);
						yuiLoader.hide();
						return;
					}
					Shop.setDisplayMode('basket');

					if (typeof getParams == 'string') {
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasket.html?' + getParams + '&xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBasket);
					}
					else {
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasket.html?button-cFrontend_ShopBasketDetail-fillBasketDetail=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBasket);
					}
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting basket items timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/basketData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackItems);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};
			var callbackBasket = {
				success: function(o) {
					_private.removeSelectedLinks();
					_public.replaceContent(o.responseText.replace(/\$NULL/g, ''));

					// focus the first input field
					try {
						YAHOO.util.Dom.getElementsByClassName('inputCount', 'input', 'containerContent')[0].focus();
					}
					catch (e) {};

					Basket.modifyNames();
					Basket.modifyDecimals();
					Basket.modifyShowLinks();
					Basket.modifyChangeDeleteLinks();
					Basket.modifyPagingLinks();
					Basket.loadBasketLight();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting basket timed out - retrying');

						if (typeof getParams == 'string') {
							YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasket.html?' + getParams + '&xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBasket);
						}
						else {
							YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasket.html?button-cFrontend_ShopBasketDetail-fillBasketDetail=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBasket);
						}
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			}

			yuiLoader.show();
			YAHOO.util.Connect.asyncRequest('POST', '/_json/basketData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackItems);
			_public.setPersNaviSelected();
		},
		loadBasketFast: function(getParams) {
			var callbackItems = {
				success: function(o) {
					var basket;

					try {
						basket = YAHOO.lang.JSON.parse(o.responseText);
					}
					catch (e) {
						DEBUG('could not parse JSON file containing basket items: ' + e.message);
						yuiLoader.hide();
						return;
					}
					Shop.setDisplayMode('basketFast');

					if (typeof getParams == 'string') {
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFast.html?' + getParams + '&xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBasket);
					}
					else {
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFast.html?button-cFrontend_ShopBasketDetail-fillBasketDetail=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBasket);
					}
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting basket items timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/basketData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackItems);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};
			var callbackBasket = {
				success: function(o) {
					_private.removeSelectedLinks();
					YAHOO.util.Dom.addClass('aFastOrder', 'selected');
					_public.replaceContent(o.responseText.replace(/\$NULL/g, ''));
					Basket.modifyFastOrderCodeSubmit();
					Basket.modifyNames();
					Basket.modifyDecimals();
					Basket.modifyShowLinks();
					Basket.modifyChangeDeleteLinks();
					Basket.modifyPagingLinks();
					Basket.loadBasketLight();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting basket timed out - retrying');

						if (typeof getParams == 'string') {
							YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFast.html?' + getParams + '&xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBasket);
						}
						else {
							YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFast.html?button-cFrontend_ShopBasketDetail-fillBasketDetail=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBasket);
						}
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			}

			yuiLoader.show();
			YAHOO.util.Connect.asyncRequest('POST', '/_json/basketData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackItems);
			_public.setPersNaviSelected();
		},
		loadFavorites: function() {
			var callback = {
				success: function(o) {
					_private.removeSelectedLinks();
					_public.replaceContent(o.responseText);
					Shop.setDisplayMode('favorites');
					Shop.displayFavorites();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting favorites timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getShop.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			}

			yuiLoader.show();
			YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getShop.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
			_public.setPersNaviSelected('aPersNaviFavorites');
		},
		loadNewsletter: function() {
			
			_public.setPersNaviSelected('aPersNaviNewsletter');
		},
		loadOrder: function(getParams) {
			var callback = {
				success: function(o) {
					_public.replaceContent(o.responseText.replace(/\$NULL/g, ''));
					//Shop.setDisplayMode('basket');
					Order.modifyNames();
					Order.modifyDecimals();
					Order.modifyPagingLinks();
					Order.modifyOrderName();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting order timed out - retrying');
						
						if (typeof getParams == 'string') {
							YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getOrder.html?button-tFrontend_ShopOrderAddress-findAddress=x&' + getParams + '&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
						}
						else {
							YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getOrder.html?button-tFrontend_ShopOrderAddress-findAddress=&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
						}
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			}

			yuiLoader.show();

			if (typeof getParams == 'string') {
				YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getOrder.html?button-tFrontend_ShopOrderAddress-findAddress=x&' + getParams + '&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
			}
			else {
				YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getOrder.html?button-tFrontend_ShopOrderAddress-findAddress=&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
			}
		},
		loadOrders: function() {
			var data;
			var callbackJSON = {
				success: function(o) {
					try {
						data = YAHOO.lang.JSON.parse(o.responseText);
					}
					catch (e) {
						DEBUG('failed to parse JSON file containing data of previous orders: ' + e.message);
						yuiLoader.hide();
						return;
					}
					YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getOrders.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackPage);
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting list of former orders timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/' + BB.getLangPath() + 'shopHistory.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackJSON);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			}
			var callbackPage = {
				success: function(o) {
					_private.removeSelectedLinks();
					_public.replaceContent(o.responseText);
					//Shop.setDisplayMode('order');
					Orders.displayOrders(data);
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting container page for list of former orders timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getOrders.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackPage);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};

			yuiLoader.show();
			YAHOO.util.Connect.asyncRequest('POST', '/_json/' + BB.getLangPath() + 'shopHistory.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackJSON);
			_public.setPersNaviSelected('aPersNaviOrders');
		},
		loadOrdersDetail: function(orderID) {
			var callback = {
				success: function(o) {
					_private.removeSelectedLinks();
					_public.replaceContent(o.responseText);
					Shop.setDisplayMode('order');
					Orders.configureDetailPaging(orderID);
					Orders.modifyNames();
					Orders.modifyItemDetailLinks();
					Orders.modifyBasketDetailLinks();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting details for order #' + orderID + ' timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getOrdersDetail.html?order_id=' + orderID + '&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			}

			if (typeof orderID == 'undefined') {
				orderID = Orders.getLastViewedOrderID();
			}
			Orders.setLastViewedOrderID(parseInt(orderID));
			yuiLoader.show();
			YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getOrdersDetail.html?order_id=' + orderID + '&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
			//_public.setPersNaviSelected('aPersNaviOrders');
		},
		loadSearchResults: function(results) {
			var callback = {
				success: function(o) {
					_public.replaceContent(o.responseText);
					Shop.setDisplayMode('search');
					Shop.displaySearchResults();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting shop timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getShop.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			}

			if (!document.getElementById('tbodyShop')) {
				yuiLoader.show();
				YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getShop.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
			}
			else {
				Shop.setDisplayMode('search');
				Shop.displaySearchResults();
			}
			_public.setPersNaviSelected();
		},
		loadShop: function(nodeId) {
			var callback = {
				success: function(o) {
					_public.replaceContent(o.responseText);
					Shop.setDisplayMode('shop');
					
					if (!document.getElementById('boxAccessDenied')) {
						Shop.init(nodeId);
					}
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting shop timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getShop.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			}

			if (!document.getElementById('tbodyShop')) {
				yuiLoader.show();
				YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getShop.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
			}
			else {
				Shop.init(nodeId);
			}
			_public.setPersNaviSelected();
		},
		replaceContent: function(contentNew) {
			if (!contentNew) {
				return;
			}
			_private.removeContent();
			_private.addContent(contentNew);
			_private.updatePaging();
			Forms.initOverLabels();
		},
		/*-----------------------------------------------------------------------------------------
		 * mark currently selected item of personal navigation (and unmark other items)
		 *
		 * params:
		 *   id: HTML ID of the item selected (if not defined, all items will be unmarked)
		 */
		setPersNaviSelected: function(id) {
			var links = YAHOO.util.Dom.getElementsBy(function(elem) {
				return true;
			}, 'a', 'boxPersNavi');
			var link;

			for (var i=0; i<links.length; i++) {
				YAHOO.util.Dom.removeClass(links[i], 'selected');
			}
			if (id && (link = document.getElementById(id))) {
				YAHOO.util.Dom.addClass(link, 'selected');
			}
		}
	};
	return _public;
}();
var DynPaging = function() {
	var _private = {
		addDynPaging: function(containers) {
			var oldPagers = YAHOO.util.Dom.getElementsByClassName('navigate', 'div', 'containerContent');
			var newPagers = YAHOO.util.Dom.getElementsByClassName('dynNavigate', 'div', 'containerContent');

			// remove old paging elements
			for (var i=0; i<oldPagers.length; i++) {
				oldPagers[i].parentNode.removeChild(oldPagers[i]);
			}
			// add new paging elements (containers is expected to be an array of IDs)
			if (newPagers.length > 0) {
				try {
					for (var i=0; i<containers.length; i++) {
						try {
							document.getElementById(containers[i]).innerHTML = newPagers[0].innerHTML;
						} 
						catch (e) {
							// ignore invalid IDs
						}
					}
				} 
				catch (e) {
					// don't do anything if containers isn't an array
				}
			}
		}
	};

	var _public = {
		init: function() {
			YAHOO.util.Event.onDOMReady(function() {
				window.setTimeout(function() {
					_private.addDynPaging(['boxPagingTop', 'boxPagingBottom']);
				}, 50);
			});
		}
	};
	return _public;
}();

DynPaging.init();
var Favorites = function() {
	var _private = {
		cookie: PBEURL('').replace(/\?/, '')
	};

	var _public = {
		deleteFavorite: function(code) {
			var callback = {
				success: function(o) {
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('deleting favorite timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/sendData.html?a-oFrontend_Favorite-sz_ERPCode=' + code + '&button-oFrontend_Favorite-deleteFavorite=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			}

			yuiLoader.show();
			YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/sendData.html?a-oFrontend_Favorite-sz_ERPCode=' + code + '&button-oFrontend_Favorite-deleteFavorite=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
		},
		setFavorite: function(code) {
			var callback = {
				success: function(o) {
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('setting favorite timed out - retrying')
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/sendData.html?a-oFrontend_Favorite-sz_ERPCode=' + code + '&button-oFrontend_Favorite-setFavorite=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			}

			yuiLoader.show();
			YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/sendData.html?a-oFrontend_Favorite-sz_ERPCode=' + code + '&button-oFrontend_Favorite-setFavorite=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
		}
	};
	return _public;
}();
var Filter = function() {
	var _private = {
		// private properties ---------------------------------------------------------------------
		cookieDomain: {
			domain: 'galke.com',
			//domain: location.hostname,
			path: '/'
		},
		letter: null, // letter product names must start with if they are to be displayed
		teasers: {
			all: [], // [{normal, latin, organic, node, headline}, ...]
			filtered: [] // [node, ...]
		},
		settings: {
			latin: false,
			organic: false,
			conventional: false,
			items: 10,
			products: 30
		},
		// private functions ----------------------------------------------------------------------
		/*-----------------------------------------------------------------------------------------
		 * collect product teasers initially shown on page in array _private.teasers.all and
		 * remove the nodes from DOM
		 *
		 * prerequisities:
		 *   * each teaser must be structured like this:
		 *
		 *       <div class="boxTeaserProd">
		 *         <h1>...</h1>
		 *         <a href="..." class="aAhead">DETAILS</a>
		 *         <span class="stringNormal" title="..."></span>
		 *         <span class="stringLatin" title="..."></span>
		 *         <span class="flagOrganic" title="..."></span>
		 *         <span class="flagConventional" title="..."></span>
		 *       </div>
		 *
		 *     (order and position of <span> tags may vary) where a value of "0" of the title
		 *     attribute of span.flagOrganic and span.flagConventional will be interpreted as
		 *     false, any other value as true
		 */
		collectProductTeasers: function() {
			var teasers = YAHOO.util.Dom.getElementsByClassName('boxTeaserProd', 'div', 'containerContent');
			var product;
			var target;

			for (var i=0; i<teasers.length; i++) {
				product = new Object();
				target = new Object();
				
				try {
					// get "normal" name and remove <span> tag from teaser
					product.normal = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'stringNormal')
					}, 'span', teasers[i]);
					teasers[i].removeChild(product.normal);
					product.normal = product.normal.getAttribute('title');

					// get Latin name and remove <span> tag from teaser
					product.latin = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'stringLatin')
					}, 'span', teasers[i]);
					teasers[i].removeChild(product.latin);
					product.latin = product.latin.getAttribute('title');

					// get organic flag and remove <span> tag from teaser
					product.organic = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'flagOrganic')
					}, 'span', teasers[i]);
					teasers[i].removeChild(product.organic);
					product.organic = product.organic.getAttribute('title') == '0' ? false : true;

					// get conventional flag and remove <span> tag from teaser
					product.conventional = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'flagConventional')
					}, 'span', teasers[i]);
					teasers[i].removeChild(product.conventional);
					product.conventional = product.conventional.getAttribute('title') == '0' ? false : true;

					// remove <span class="spanGroup"> tag from teaser
					var group = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'spanGroup')
					}, 'span', teasers[i]);

					teasers[i].removeChild(group);

					// get other properties
					product.node = teasers[i];
					product.headline = YAHOO.util.Dom.getElementBy(function(elem) {
						return true;
					}, 'h1', teasers[i]);

					// make the whole story work as a link
					target.href = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'aAhead');
					}, 'a', teasers[i]).getAttribute('href');
					YAHOO.util.Event.addListener(product.node, 'click', function() {
						location.replace(this.href);
					}, target, true);
					delete target;
				}
				catch (e) {
					delete product;
					delete target;
					continue;
				}
				_private.teasers.all[_private.teasers.all.length] = product;
				teasers[i].parentNode.removeChild(teasers[i]);
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * load _private.settings from cookie if there is one, and set handlers for filter links
		 * (feature of navigation for products)
		 */
		configureProductFilters: function() {
			var langNormal = document.getElementById('aLangNormal');
			var langLatin = document.getElementById('aLangLatin');
			var cultAll = document.getElementById('aCultAll');
			var cultOrganic = document.getElementById('aCultOrganic');
			var cultConventional = document.getElementById('aCultConventional');
			var firstLetterFilters = YAHOO.util.Dom.getElementsBy(function() {
				return true;
			}, 'a', 'spanFirstLetterFilters');
			var itemsPerPage;

			_private.initSettings();

			if (!langNormal || !langLatin || !cultAll || !cultOrganic || !cultConventional) {
				return;
			}
			// set language of names to be displayed
			if (_private.settings.latin) {
				YAHOO.util.Dom.removeClass(langNormal, 'selected');
				YAHOO.util.Dom.addClass(langLatin, 'selected');
			}
			else {
				YAHOO.util.Dom.removeClass(langLatin, 'selected');
				YAHOO.util.Dom.addClass(langNormal, 'selected');
			}
			// set cultivation filter
			YAHOO.util.Dom.removeClass(cultAll, 'selected');
			YAHOO.util.Dom.removeClass(cultOrganic, 'selected');
			YAHOO.util.Dom.removeClass(cultConventional, 'selected');

			if (_private.settings.organic) {
				YAHOO.util.Dom.addClass(cultOrganic, 'selected');
			}
			else if (_private.settings.conventional) {
				// _private.initSettings() guarantees that organic and conventional filters are not
				// active at the same time
				YAHOO.util.Dom.addClass(cultConventional, 'selected');
			}
			else {
				YAHOO.util.Dom.addClass(cultAll, 'selected');
			}
			// register handlers
			YAHOO.util.Event.addListener(firstLetterFilters, 'click', function() {
				if (YAHOO.util.Dom.hasClass(this, 'selected')) {
					YAHOO.util.Dom.removeClass(firstLetterFilters, 'selected');
					_private.setLetter('');
				}
				else {
					YAHOO.util.Dom.removeClass(firstLetterFilters, 'selected');
					YAHOO.util.Dom.addClass(this, 'selected');
					_private.setLetter(this.getAttribute('id').replace(/^aFirstLetter/, ''));
				}
			});
			YAHOO.util.Event.addListener(langNormal, 'click', function() {
				if (_private.settings.latin) {
					yuiLoader.show();
					YAHOO.util.Dom.removeClass(langLatin, 'selected');
					YAHOO.util.Dom.addClass(langNormal, 'selected');
					_private.settings.latin = false;
					_private.setCookie();
					_private.renderProductTeasers();
					yuiLoader.hide();
				}
			});
			YAHOO.util.Event.addListener(langLatin, 'click', function() {
				if (!_private.settings.latin) {
					yuiLoader.show();
					YAHOO.util.Dom.removeClass(langNormal, 'selected');
					YAHOO.util.Dom.addClass(langLatin, 'selected');
					_private.settings.latin = true;
					_private.setCookie();
					_private.renderProductTeasers();
					yuiLoader.hide();
				}
			});
			YAHOO.util.Event.addListener(cultAll, 'click', function() {
				if (_private.settings.organic || _private.settings.conventional) {
					yuiLoader.show();
					YAHOO.util.Dom.addClass(cultAll, 'selected');
					YAHOO.util.Dom.removeClass(cultOrganic, 'selected');
					YAHOO.util.Dom.removeClass(cultConventional, 'selected');
					_private.settings.organic = false;
					_private.settings.conventional = false;
					_private.setCookie();
					_private.renderProductTeasers();
					yuiLoader.hide();
				}
			});
			YAHOO.util.Event.addListener(cultOrganic, 'click', function() {
				if (!_private.settings.organic) {
					yuiLoader.show();
					YAHOO.util.Dom.removeClass(cultAll, 'selected');
					YAHOO.util.Dom.addClass(cultOrganic, 'selected');
					YAHOO.util.Dom.removeClass(cultConventional, 'selected');
					_private.settings.organic = true;
					_private.settings.conventional = false;
					_private.setCookie();
					_private.renderProductTeasers();
					yuiLoader.hide();
				}
			});
			YAHOO.util.Event.addListener(cultConventional, 'click', function() {
				if (!_private.settings.conventional) {
					yuiLoader.show();
					YAHOO.util.Dom.removeClass(cultAll, 'selected');
					YAHOO.util.Dom.removeClass(cultOrganic, 'selected');
					YAHOO.util.Dom.addClass(cultConventional, 'selected');
					_private.settings.organic = false;
					_private.settings.conventional = true;
					_private.setCookie();
					_private.renderProductTeasers();
					yuiLoader.hide();
				}
			});
			
			// register handlers for # of products per page links
			for (var i=1; i<=4; i++) {
				var obj = new Object();

				obj.products = 30 * i;
				itemsPerPage = YAHOO.util.Dom.getElementsByClassName('a' + (30 * i) + 'ItemsPerPage', 'a');
				YAHOO.util.Event.addListener(itemsPerPage, 'click', function() {
					_private.settings.products = this.products;
					_private.setCookie();
					_private.goToPage(1);
				}, obj, true);
				delete obj;
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * display a page of product teasers and update paging
		 *
		 * params:
		 *   no: the number of the page to be displayed
		 * prerequisities:
		 *   * _private.teasers.filtered must have been initialized using
		 *     _private.renderProductTeasers()
		 *   * there must be exactly three nodes matching div.boxTeasersProdCol inside a node
		 *     matching #containerTeasersProd
		 */
		goToPage: function(no) {
			// variables for cleaning up
			var nodes = YAHOO.util.Dom.getElementsByClassName('boxTeaserProd', 'div', 'containerContent');
			// variables referencing paging elements
			var itemsPerPage;
			var pageNo;
			var pageMax;
			var max = Math.ceil(_private.teasers.filtered.length / _private.settings.products);
			var firstPage;
			var prevPage;
			var nextPage;
			var lastPage;
			var counters;
			// variables for output of teasers
			var containers = YAHOO.util.Dom.getElementsByClassName('boxTeasersProdCol', 'div', 'containerTeasersProd');
			var containersHd = YAHOO.util.Dom.getElementsByClassName('boxTeasersProdColHd', 'div', 'containerTeasersProd');
			var start;
			var end;

			// break on error
			if (!BB.isInteger(no)) {
				return;
			}
			if (containers.length != 3 || containersHd.length != 3) {
				return;
			}
			if (max < 1) {
				max = 1
			}
			if (no < 1) {
				no = 1;
			}
			if (no > max) {
				no = max;
			}
			// clean up
			for (var i=0; i<nodes.length; i++) {
				nodes[i].parentNode.removeChild(nodes[i]);
			}
			// update products per page
			itemsPerPage = YAHOO.util.Dom.getElementsByClassName('aItemsPerPage', 'a');
			YAHOO.util.Dom.removeClass(itemsPerPage, 'selected');
			itemsPerPage = YAHOO.util.Dom.getElementsByClassName('a' + _private.settings.products + 'ItemsPerPage', 'a');
			YAHOO.util.Dom.addClass(itemsPerPage, 'selected');

			// update pager
			pageNo    = YAHOO.util.Dom.getElementsByClassName('spanPageNo', 'span');
			pageMax   = YAHOO.util.Dom.getElementsByClassName('spanPageMax', 'span');
			firstPage = YAHOO.util.Dom.getElementsByClassName('aFirstPage', 'a');
			prevPage  = YAHOO.util.Dom.getElementsByClassName('aPrevPage', 'a');
			nextPage  = YAHOO.util.Dom.getElementsByClassName('aNextPage', 'a');
			lastPage  = YAHOO.util.Dom.getElementsByClassName('aLastPage', 'a');

			for (var i=0; i<pageNo.length; i++) {
				pageNo[i].innerHTML = no.toString();
			}
			for (var i=0; i<pageMax.length; i++) {
				pageMax[i].innerHTML = max;
			}
			YAHOO.util.Event.removeListener(firstPage, 'click');
			YAHOO.util.Event.addListener(firstPage, 'click', function() {
				_private.goToPage(1);
			});
			YAHOO.util.Event.removeListener(prevPage, 'click');
			YAHOO.util.Event.addListener(prevPage, 'click', function() {
				_private.goToPage(no - 1);
			});
			YAHOO.util.Event.removeListener(nextPage, 'click');
			YAHOO.util.Event.addListener(nextPage, 'click', function() {
				_private.goToPage(no + 1);
			});
			YAHOO.util.Event.removeListener(lastPage, 'click');
			YAHOO.util.Event.addListener(lastPage, 'click', function() {
				_private.goToPage(max);
			});
			// update product counters
			counters = YAHOO.util.Dom.getElementsByClassName('spanCounter', 'span');

			for (var i=0; i<counters.length; i++) {
				counters[i].innerHTML = _private.teasers.filtered.length;
			}
			// display product teasers
			start = _private.settings.products * (no - 1);
			end = _private.settings.products * no - 1;

			if (end > _private.teasers.filtered.length - 1) {
				end = _private.teasers.filtered.length - 1;
			}
			YAHOO.util.Dom.removeClass(_private.teasers.filtered, 'odd');

			for (var i=0; i<3; i++) {
				containersHd[i].style.display = 'none';
			}
			for (var i=0; i<Math.ceil((end - start + 1) / 3); i++) {
				if (start + i <= end) {
					if (i == 0) {
						containersHd[0].style.display = 'block';
					}
					if (i % 2 == 0) {
						YAHOO.util.Dom.addClass(_private.teasers.filtered[start + i], 'odd');
					}
					containers[0].appendChild(_private.teasers.filtered[start + i]);
				}
			}
			for (var i=0; i<Math.ceil((end - start + 1) / 3); i++) {
				if (start + i + Math.ceil((end - start + 1) / 3) <= end) {
					if (i == 0) {
						containersHd[1].style.display = 'block';
					}
					if (i % 2 == 0) {
						YAHOO.util.Dom.addClass(_private.teasers.filtered[start + i + Math.ceil((end - start + 1) / 3)], 'odd');
					}
					containers[1].appendChild(_private.teasers.filtered[start + i + Math.ceil((end - start + 1) / 3)]);
				}
			}
			for (var i=0; i<Math.ceil((end - start + 1) / 3); i++) {
				if (start + i + 2 * Math.ceil((end - start + 1) / 3) <= end) {
					if (i == 0) {
						containersHd[2].style.display = 'block';
					}
					if (i % 2 == 0) {
						YAHOO.util.Dom.addClass(_private.teasers.filtered[start + i + 2 * Math.ceil((end - start + 1) / 3)], 'odd');
					}
					containers[2].appendChild(_private.teasers.filtered[start + i + 2 * Math.ceil((end - start + 1) / 3)]);
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * load _private.settings from a cookie, if there is one
		 */
		initSettings: function() {
			var settings = YAHOO.util.Cookie.getSubs('com.galke.products');

			if (settings) {
				switch (settings.latin) {
					case 'true':
						_private.settings.latin = true;
						break;
					default:
						_private.settings.latin = false;
				}
				switch (settings.organic) {
					case 'true':
						_private.settings.organic = true;
						break;
					default:
						_private.settings.organic = false;
				}
				switch (settings.conventional) {
					case 'true':
						// this filter takes precedence (just in case someone manually edited the
						// cookie file)
						_private.settings.organic = false;
						_private.settings.conventional = true;
						break;
					default:
						_private.settings.conventional = false;
				}
				switch (settings.items) {
					case '10':
					case '20':
					case '30':
					case '40':
						_private.settings.items = parseInt(settings.items);
						break;
					default:
						_private.settings.items = 10;
				}
				switch (settings.products) {
					case '30':
					case '60':
					case '90':
					case '120':
						_private.settings.products = parseInt(settings.products);
						break;
					default:
						_private.settings.products = 30;
				}
			}
			_private.setCookie();
		},
		/*-----------------------------------------------------------------------------------------
		 * render product teasers using data from _private.teasers.all, i.e. filter stories
		 * depending on current settings ("normal" language/Latin, organic, conventional, number of
		 * products per page)
		 *
		 * prerequisities:
		 *   * _private.teasers.all must be initialized, i.e.
		 *     _private.collectProductTeasers() must have been called before
		 */
		renderProductTeasers: function() {
			var teasers = [];
			var display;

			yuiLoader.show();
			// find teasers to be inserted into DOM
			for (var i=0; i<_private.teasers.all.length; i++) {
				// set headline of teaser acording to language currently selected
				if (_private.settings.latin) {
					_private.teasers.all[i].headline.innerHTML = _private.teasers.all[i].latin;
				}
				else {
					_private.teasers.all[i].headline.innerHTML = _private.teasers.all[i].normal;
				}
				// check if teaser is to be displayed according to filters currently selected
				display = true;

				if (   _private.letter
				    && BB.replaceSpecialChars(_private.teasers.all[i].headline.innerHTML).toUpperCase().indexOf(_private.letter) != 0) {
					display = false;
				}
				if (   (_private.settings.organic && !_private.teasers.all[i].organic)
				    || (_private.settings.conventional && !_private.teasers.all[i].conventional)) {
					display = false;
				}
				if (display) {
					// save index only (the array will be sorted later)
					teasers[teasers.length] = i;
				}
			}
			// sort teasers
			var names = new Array();
			var indexes;
			var tmp = new Array();

			for (var i=0; i<teasers.length; i++) {
				names[i] = BB.replaceSpecialChars(_private.teasers.all[teasers[i]].headline.innerHTML).toLowerCase();
			}
			indexes = BB.mergeSort(names);

			for (var i=0; i<indexes.length; i++) {
				tmp[i] = teasers[indexes[i]];
			}
			for (var i=0; i<tmp.length; i++) {
				teasers[i] = tmp[i];
			}
			delete names;
			delete indexes;
			delete tmp;

			// insert teasers into DOM
			_private.teasers.filtered.length = 0;

			for (var i=0; i<teasers.length; i++) {
				_private.teasers.filtered[i] = _private.teasers.all[teasers[i]].node;
			}
			// display first page of product teasers and update paging
			_private.goToPage(1);
			yuiLoader.hide();
		},
		/*-----------------------------------------------------------------------------------------
		 * save _private.settings to a cookie
		 */
		setCookie: function() {
			YAHOO.util.Cookie.setSubs('com.galke.products', {
				latin: _private.settings.latin,
				organic: _private.settings.organic,
				conventional: _private.settings.conventional,
				items: _private.settings.items,
				products: _private.settings.products
			}, {
				domain: _private.cookieDomain.domain,
				path: _private.cookieDomain.path,
				expires: new Date('December 31, 2099 23:59:59'),
				secure: false
			});
		},
		/*-----------------------------------------------------------------------------------------
		 * set a letter product names have to start with if they are to be displayed (if no valid
		 * value is given, the letter will be unset) - the teasers will be rendered afterwards
		 *
		 * params:
		 *   letter: a letter
		 * prerequisities:
		 *   * product teasers must have been initialized
		 */
		setLetter: function(letter) {
			_private.letter = null;

			if (BB.isAlpha(letter)) {
				_private.letter = letter.toUpperCase();
			}
			_private.renderProductTeasers();
		},
		/*-----------------------------------------------------------------------------------------
		 * sort an array of indexes referencing members of array _private.teasers.all
		 *
		 * params:
		 *   teasers: an array of indexes
		 * prerequisities:
		 *   * array _private.teasers.all must have been initialized using
		 *     _private.collectProductTeasers()
		 *   * all values in array teasers must be non-negative integers less than
		 *     _private.teasers.all.length
		 */
		sortProductTeasers: function(teasers) {
			var tmp;

			for (var i=teasers.length-1; i>0; i--) {
				for (var j=0; j<i; j++) {
					if (BB.replaceSpecialChars(_private.teasers.all[teasers[j]].headline.innerHTML).toUpperCase()
					    > BB.replaceSpecialChars(_private.teasers.all[teasers[i]].headline.innerHTML).toUpperCase()) {
						tmp = teasers[i];
						teasers[i] = teasers[j];
						teasers[j] = tmp;
					}
				}
			}
			return teasers;
		}
	};
	
	var _public = {
		// public properties ----------------------------------------------------------------------

		// public functions -----------------------------------------------------------------------
		/*-----------------------------------------------------------------------------------------
		 * get filter settings
		 *
		 * return value:
		 *   the settings from a cookie (if the cookie does not exist it will be created)
		 */
		getSettings: function() {
			_private.initSettings();
			return _private.settings;
		},
		/*-----------------------------------------------------------------------------------------
		 * initialize filters, render product teasers and paging elements
		 */
		init: function() {
			_private.collectProductTeasers();

			if (_private.teasers.all.length > 0) {
				_private.configureProductFilters();
				// there is no need to call this if _private.setLetter() is called on load
				//_private.renderProductTeasers();
			}
			_private.setLetter('');
		},
		/*-----------------------------------------------------------------------------------------
		 * save settings to a cookie
		 *
		 * params:
		 *   filters: an object containing settings
		 */
		setSettings: function(settings) {
			var settingsClone;

			if (typeof settings != 'object') {
				return;
			}
			// clone the parameter (workaround for a bug with calling _private.initSettings()) 
			settingsClone = new Object();
			settingsClone.latin = settings.latin;
			settingsClone.organic = settings.organic;
			settingsClone.conventional = settings.conventional;
			settingsClone.items = settings.items;
			settingsClone.products = settings.products;

			_private.initSettings();

			if (typeof settingsClone.latin == 'boolean') {
				_private.settings.latin = settingsClone.latin;
			}
			if (typeof settingsClone.organic == 'boolean') {
				_private.settings.organic = settingsClone.organic;
			}
			if (typeof settingsClone.conventional == 'boolean') {
				_private.settings.conventional = settingsClone.conventional;
			}
			if (typeof settingsClone.items == 'number') {
				switch (settingsClone.items) {
					case 10:
						_private.settings.items = 10;
						break;
					case 20:
						_private.settings.items = 20;
						break;
					case 30:
						_private.settings.items = 30;
						break;
					case 40:
						_private.settings.items = 40;
						break;
				}
			}
			if (typeof settingsClone.products == 'number') {
				switch (settingsClone.products) {
					case 30:
						_private.settings.products = 30;
						break;
					case 60:
						_private.settings.products = 60;
						break;
					case 90:
						_private.settings.products = 90;
						break;
					case 120:
						_private.settings.products = 120;
						break;
				}
			}
			delete settingsClone;
			_private.setCookie();
		}
	};
	return _public;
}();

YAHOO.util.Event.onDOMReady(Filter.init);
var Navi = function() {
	var _private = {
		// private properties ---------------------------------------------------------------------
		animDelay: 200,    // delay before each animation (msec)
		animDuration: 400, // duration of an ease of <_private.widthNarrow> pixels (msec)
		widthNarrow: 128,
		widthWide: 256,
		// private functions ----------------------------------------------------------------------
		/*-----------------------------------------------------------------------------------------
		 * animate color bars below navigation
		 */
		animateColorBars: function() {
			var colorBars = YAHOO.util.Dom.getElementsByClassName('boxHeaderColorBar', 'div', 'boxHeaderScreen');
			var delay = _private.animDelay;

			for (var i=0; i<colorBars.length; i++) {
				var animColorBar;
				var width;
				var duration;

				try {
					// create animation objects
					width = colorBars[i].offsetWidth;
					duration = Math.ceil(width / _private.widthNarrow) * _private.animDuration;

					animColorBar = new YAHOO.util.Anim(colorBars[i], {
						left: {
							from: -width,
							to: 0
						}
					}, duration / 1000, YAHOO.util.Easing.easeNone);

					// start animation(s) with some delay 
					YAHOO.util.Event.onDOMReady(function() {
						var colorBar = this.colorBar;
						var animColorBar = this.animColorBar;

						window.setTimeout(function() {
							colorBar.style.opacity = '1';
							animColorBar.animate();
							delete animColorBar;
						}, this.delay);
					}, {
						colorBar: colorBars[i],
						animColorBar: animColorBar,
						delay: delay
					}, true);

					//delay += duration + _private.animDelay;
				}
				catch (e) {
					colorBar.style.opacity = '1';
					colorBar.style.left = '0';
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * animate navigation level > 1, products navigation and filters respectively
		 */
		animateSubNavi: function() {
			var delay = _private.animDelay;

			// "normal" nodes
			for (var i=2; ; i++) {
				var navi = document.getElementById('boxNaviLevel' + i);
				var naviBG = document.getElementById('boxNaviLevel' + i + 'BG');
				var naviContent = document.getElementById('boxNaviLevel' + i + 'Content');
				var animBG;
				var animContent;
				var width;
				var duration;

				if (!navi || !naviBG || !naviContent) {
					break;
				}
				try {
					// create animation objects
					width = parseInt(naviBG.style.width.replace(/\D/g, ''));
					duration = Math.ceil(width / _private.widthNarrow) * _private.animDuration;

					animBG = new YAHOO.util.Anim(naviBG, {
						left: {
							from: -width,
							to: 0
						}
					}, duration / 1000, YAHOO.util.Easing.easeNone);
					animContent = new YAHOO.util.Anim(naviContent, {
						left: {
							from: -width,
							to: 0
						}
					}, duration / 1000, YAHOO.util.Easing.easeNone);

					// start animation(s) with some delay 
					YAHOO.util.Event.onDOMReady(function() {
						var navi = this.navi;
						var animBG = this.animBG;
						var animContent = this.animContent;

						window.setTimeout(function() {
							navi.style.opacity = '1';
							animBG.animate();
							animContent.animate();
							delete animBG;
							delete animContent;
						}, this.delay);
					}, {
						navi: navi,
						animBG: animBG,
						animContent: animContent,
						delay: delay
					}, true);

					delay += duration + _private.animDelay;
				}
				catch (e) {
					navi.style.opacity = '1';
					naviBG.style.left = '0';
					naviContent.style.left = '0';
				}
			}
			// product nodes
			for (var i=2; ; i++) {
				var navi = document.getElementById('boxNaviLevel' + i + 'Products');
				var naviBG = document.getElementById('boxNaviLevel' + i + 'BGProducts');
				var naviContent = document.getElementById('boxNaviLevel' + i + 'ContentProducts');
				var animBG;
				var animContent;
				var width;
				var duration;

				if (!navi || !naviBG || !naviContent) {
					break;
				}
				try {
					width = parseInt(naviBG.style.width.replace(/\D/g, ''));

					if (CMS_LastNodeWasProductNode) {
						// don't animate navi if the node visited last has been a product node 
						navi.style.opacity = '1';
						naviBG.style.left = '0';
						naviContent.style.left = '0';
					}
					else {
						// create animation objects
						duration = Math.ceil(width / _private.widthNarrow) * _private.animDuration;

						animBG = new YAHOO.util.Anim(naviBG, {
							left: {
								from: -width,
								to: 0
							}
						}, duration / 1000, YAHOO.util.Easing.easeNone);
						animContent = new YAHOO.util.Anim(naviContent, {
							left: {
								from: -width,
								to: 0
							}
						}, duration / 1000, YAHOO.util.Easing.easeNone);

						// start animation(s) with some delay 
						YAHOO.util.Event.onDOMReady(function() {
							var navi = this.navi;
							var animBG = this.animBG;
							var animContent = this.animContent;

							window.setTimeout(function() {
								navi.style.opacity = '1';
								animBG.animate();
								animContent.animate();
								delete animBG;
								delete animContent;
							}, this.delay);
						}, {
							navi: navi,
							animBG: animBG,
							animContent: animContent,
							delay: delay
						}, true);

						delay += duration + _private.animDelay;
					}
				}
				catch (e) {
					navi.style.opacity = '1';
					naviBG.style.left = '0';
					naviContent.style.left = '0';
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * set width of navigation levels, products navigation and filters respectively
		 */
		setHeight: function() {
			var layer1 = document.getElementById('boxLayer1');
			var layer2 = document.getElementById('boxLayer2');
			var height = 181;

			if (!layer1 || !layer2) {
				return;
			}
			if (document.getElementById('boxHeaderStart')) {
				height = 293;
			}
			// set height of "outer" header elements
			document.getElementById('boxHeaderGraphical').style.height = '' + height + 'px';
			document.getElementById('boxNavi').style.height = '' + height + 'px';
			document.getElementById('boxLayer1').style.height = '' + height + 'px';
			document.getElementById('boxLayer2').style.height = '' + height + 'px';

			// set height of "inner" header elements ("normal" nodes)
			for (var i=1; ; i++) {
				var navi = document.getElementById('boxNaviLevel' + i);
				var naviBG = document.getElementById('boxNaviLevel' + i + 'BG');

				if (!navi) {
					break;
				}
				navi.style.height = '' + height + 'px';

				if (naviBG) { // if (i==2), this element does not exist
					naviBG.style.height = '' + height + 'px';
				}
			}
			// set height of "inner" header elements (product nodes)
			for (var i=2; ; i++) {
				var navi = document.getElementById('boxNaviLevel' + i + 'Products');
				var naviBG = document.getElementById('boxNaviLevel' + i + 'BGProducts');

				if (!navi) {
					break;
				}
				navi.style.height = '' + height + 'px';

				if (naviBG) { // if (i==2), this element does not exist
					naviBG.style.height = '' + height + 'px';
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * set width of navigation levels > 1, products navigation and filters respectively
		 */
		setNaviWidth: function() {
			var left = 248;

			// "normal" nodes
			for (var i=2; ; i++) {
				var navi = document.getElementById('boxNaviLevel' + i);
				var naviBG = document.getElementById('boxNaviLevel' + i + 'BG');
				var naviContent = document.getElementById('boxNaviLevel' + i + 'Content');
				var spans;
				var width = 0;

				if (!navi || !naviBG || !naviContent) {
					break;
				}
				spans = YAHOO.util.Dom.getElementsByClassName('spanNavi', 'span', naviContent);

				// determine maximum link width
				for (var j=0; j<spans.length; j++) {
					if (spans[j].offsetWidth > width) {
						width = spans[j].offsetWidth;
					}
				}
				// set navi width
				if (width > 0 && width <= _private.widthNarrow - 13) {
					width = _private.widthNarrow;
				}
				else {
					width = _private.widthWide;
				}
				navi.style.width = '' + width + 'px';
				naviBG.style.width = '' + (width - 1) + 'px';       // 1px border
				naviContent.style.width = '' + (width - 13) + 'px'; // 1px border, 2 * 6px padding

				// set navi left offset
				navi.style.left = '' + left + 'px';
				naviBG.style.left = '-' + (width - 1) + 'px';
				naviContent.style.left = '-' + (width - 1) + 'px';

				left += width;
			}
			// product nodes
			for (var i=2; ; i++) {
				var navi = document.getElementById('boxNaviLevel' + i + 'Products');
				var naviBG = document.getElementById('boxNaviLevel' + i + 'BGProducts');
				var naviContent = document.getElementById('boxNaviLevel' + i + 'ContentProducts');
				var width = 0;

				if (!navi || !naviBG || !naviContent) {
					break;
				}
				// set navi width
				if (   !document.getElementById('formRegStarter')
				    && !document.getElementById('formRegStart')
				    && !document.getElementById('formRegData')
				    && !document.getElementById('boxRegReply')) {
					// index page
					if (i == 2) {
						width = 512;
					}
					else {
						width = 128;
					}
				}
				else {
					// detail page/registration
					if (i == 2) {
						width = 640;
					}
					else {
						width = 0;
					}
				}
				if (width > 0) {
					navi.style.width = '' + width + 'px';
					naviBG.style.width = '' + (width - 1) + 'px';       // 1px border
					naviContent.style.width = '' + (width - 13) + 'px'; // 1px border, 2 * 6px padding

					// set navi left offset
					navi.style.left = '' + left + 'px';
					naviBG.style.left = '-' + (width - 1) + 'px';
					naviContent.style.left = '-' + (width - 1) + 'px';

					left += width;
				}
				else {
					navi.style.display = 'none';
					naviBG.style.display = 'none';
					naviContent.style.display = 'none';
					document.getElementById('spanFirstLetterFilters').style.display = 'none';
				}
			}
		}
	};

	var _public = {
		// public properties ----------------------------------------------------------------------

		// public functions -----------------------------------------------------------------------
		/*-----------------------------------------------------------------------------------------
		 * initialize navigation
		 */
		init: function() {
			// render navigation elements
			_private.setNaviWidth();
			_private.setHeight();
			_private.animateSubNavi();
			_private.animateColorBars();
		}
	}
	return _public;
}();

YAHOO.util.Event.onDOMReady(Navi.init);
var Orders = function() {
	// private properties and functions
	var _private = {
		// properties
		cookie: PBEURL('').replace(/\?/, ''),
		lastViewedOrderID: -1, // ID of the last order viewed
		orders: [],            // order IDs
		// functions
		/*-----------------------------------------------------------------------------------------
		 * configure back links, paging links, no. of current page, and maximum page no.
		 */
		configureDetailPaging: function(orderID) {
			var back = YAHOO.util.Dom.getElementsByClassName('aPagingBack', 'a', 'containerPage');
			var prev = YAHOO.util.Dom.getElementsByClassName('aPagingPrev', 'a', 'containerPage');
			var next = YAHOO.util.Dom.getElementsByClassName('aPagingNext', 'a', 'containerPage');
			var pageNo = _private.findValueInList(orderID, _private.orders) + 1;

			// back links
			for (var i=0; i<back.length; i++) {
				YAHOO.util.Event.addListener(back[i], 'click', Content.loadOrders);
			}
			// paging links
			for (var i=0; i<prev.length; i++) {
				// <
				YAHOO.util.Event.addListener(prev[i], 'click', function() {
					if (pageNo > 1) {
						Content.loadOrdersDetail(_private.orders[pageNo-2]);
					}
				});
				// >
				YAHOO.util.Event.addListener(next[i], 'click', function() {
					if (pageNo < _private.orders.length) {
						Content.loadOrdersDetail(_private.orders[pageNo]);
					}
				});
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * configure paging, so that no more than 10 previous orders are shown at a time
		 */
		configurePaging: function() {
			var first = YAHOO.util.Dom.getElementsByClassName('aFirstPage', 'a', 'bd');
			var prev = YAHOO.util.Dom.getElementsByClassName('aPrevPage', 'a', 'bd');
			var next = YAHOO.util.Dom.getElementsByClassName('aNextPage', 'a', 'bd');
			var last = YAHOO.util.Dom.getElementsByClassName('aLastPage', 'a', 'bd');
			var page = YAHOO.util.Dom.getElementsByClassName('spanPageNo', 'span', 'bd');
			var max = YAHOO.util.Dom.getElementsByClassName('spanPageMax', 'span', 'bd');
			var pageNo;
			var pageMax;
			var counter = YAHOO.util.Dom.getElementsByClassName('spanCounter', 'span', 'bd');
			var orders = YAHOO.util.Dom.getElementsByClassName('boxOrders', 'div', 'boxOrdersBd');

			pageMax = orders.length > 0 ? Math.ceil(orders.length / 10) : 1;

			// <<
			YAHOO.util.Event.addListener(first, 'click', function() {
				var pageNo = parseInt(YAHOO.util.Dom.getElementsByClassName('spanPageNo', 'span', 'bd')[0].innerHTML);

				if (pageNo > 1) {
					_private.goToPage(1);
				}
			});
			// <
			YAHOO.util.Event.addListener(prev, 'click', function() {
				var pageNo = parseInt(YAHOO.util.Dom.getElementsByClassName('spanPageNo', 'span', 'bd')[0].innerHTML);

				if (pageNo > 1) {
					_private.goToPage(pageNo - 1);
				}
			});
			// >
			YAHOO.util.Event.addListener(next, 'click', function() {
				var pageNo = parseInt(YAHOO.util.Dom.getElementsByClassName('spanPageNo', 'span', 'bd')[0].innerHTML);

				if (pageNo < pageMax) {
					_private.goToPage(pageNo + 1);
				}
			});
			// >>
			YAHOO.util.Event.addListener(last, 'click', function() {
				var pageNo = parseInt(YAHOO.util.Dom.getElementsByClassName('spanPageNo', 'span', 'bd')[0].innerHTML);

				if (pageNo < pageMax) {
					_private.goToPage(pageMax);
				}
			});
			for (var i=0; i<max.length; i++) {
				max[i].innerHTML = pageMax;
			}
			for (var i=0; i<counter.length; i++) {
				counter[i].innerHTML = _private.orders.length;
			}
			_private.goToPage(1);
		},
		/*-----------------------------------------------------------------------------------------
		 * find the index of a value in a list of values
		 * 
		 * params:
		 *   value: the value to search for
		 *   list: the list of values to search in
		 * return value:
		 *   index of the value if found, -1 otherwise
		 */
		findValueInList: function(value, list) {
			for (var i=0; i<list.length; i++) {
				if (value == list[i]) {
					return i;
				}
			}
			return -1;
		},
		/*-----------------------------------------------------------------------------------------
		 * switch to another page of orders
		 *
		 * params:
		 *   pageNo: the no. of the page to switch to
		 */
		goToPage: function(pageNo) {
			var page = YAHOO.util.Dom.getElementsByClassName('spanPageNo', 'span', 'bd');
			var orders = YAHOO.util.Dom.getElementsByClassName('boxOrders', 'div', 'boxOrdersBd');

			for (var i=0; i<page.length; i++) {
				page[i].innerHTML = pageNo;
			}
			for (var i=0; i<orders.length; i++) {
				orders[i].style.display = 'none';
			}
			for (var i=10*(pageNo-1); i<orders.length && i<10*pageNo; i++) {
				orders[i].style.display = 'block';
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * add click listeners to the names of previous orders that will display more detailled
		 * information on those orders
		 */
		modifyOrderDetailLinks: function() {
			YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('aOrdersDetails', 'a', 'boxOrdersBd'), 'click', function() {
				var orderID = YAHOO.util.Dom.getAncestorByClassName(this, 'boxOrders').getAttribute('id').replace(/^boxOrders/, '');

				Content.loadOrdersDetail(orderID);
			})
		}
	};

	// public functions
	var _public = {
		/*-----------------------------------------------------------------------------------------
		 * wrapper for the private function with the same name
		 *
		 * param:
		 *   orderID: ID of the order currently viewed
		 */
		configureDetailPaging: function(orderID) {
			_private.configureDetailPaging(orderID);
		},
		/*-----------------------------------------------------------------------------------------
		 * display a list of all previous orders
		 *
		 * params:
		 *   data: an object containing order data - {orders: [id, date, name, items: [{code, count}]}
		 */
		displayOrders: function(data) {
			var container = document.getElementById('boxOrdersBd');
			var html;
			var codes;
			var counts;

			if (!container) {
				return;
			}
			// print orders in descending order, i.e. newest order first
			_private.orders.length = 0;

			if (data.orders.length == 0) {
				var boxOrders = document.getElementById('boxOrders');

				while (boxOrders.hasChildNodes()) {
					boxOrders.removeChild(boxOrders.firstChild);
				}
				boxOrders.innerHTML =
					  '<div class="box6Col boxTopDotted">'
					+   '<div class="box6ColInner>'
					+     '<div class="box3Col">'
					+       '<b>' + ((BB.getLang() == 'de') ? 'Sie haben bisher keine Bestellungen aufgegeben.' : 'You have so far made no orders.') + '</b>'
					+     '</div>'
					+     '<div class="clearing"></div>'
					+   '</div>'
					+ '</div>';
			}
			else {
				for (var i=data.orders.length-1; i>=0; i--) {
					// add order ID to list of order IDs (required for detail paging)
					_private.orders[_private.orders.length] = data.orders[i].id;

					// add HTML code
					codes = '';
					counts = '';

					for (var j=0; j<data.orders[i].items.length; j++) {
						if (j > 0) {
							codes += ',';
							counts += ',';
						}
						codes += data.orders[i].items[j].code;
						counts += data.orders[i].items[j].count;
					}
					html  = '<div class="clearing"></div>';
					html += '<div class="boxOrders" id="boxOrders' + data.orders[i].id + '" style="display: none;">';
					html +=   '<div class="boxOrdersDate">';
					html +=     data.orders[i].date;
					html +=   '</div>';
					html +=   '<div class="boxOrdersName">';
					html +=     '<a class="aOrdersDetails">' + data.orders[i].name + ((BB.getLang() == 'de') ? ' (Bestellnummer ' : ' (order no. ') + data.orders[i].id + ')</a>';
					html +=   '</div>';
					html +=   '<div class="boxOrdersItemsOrdered">';
					html +=     data.orders[i].itemCount;
					html +=   '</div>';
					html += '</div>';
					html += '<div class="clearing"></div>';

					container.innerHTML += html;
				}
			}
			_private.modifyOrderDetailLinks();
			_private.configurePaging();
		},
		/*-----------------------------------------------------------------------------------------
		 * get the ID of the order viewed last
		 *
		 * return value:
		 *   ID of the order last viewed, -1 if no order has been viewed yet
		 */
		getLastViewedOrderID: function() {
			return _private.lastViewedOrderID;
		},
		/*-----------------------------------------------------------------------------------------
		 * add click listeners to basket links
		 */
		modifyBasketDetailLinks: function() {
			var basketLinks = YAHOO.util.Dom.getElementsByClassName('iconBasket', 'span', 'containerOrdersDetail');

			for (var i=0; i<basketLinks.length; i++) {
				YAHOO.util.Event.addListener(basketLinks[i], 'click', function() {
					var code = this.getAttribute('id').replace(/^iconBasket/, '').replace(/_.*$/, '');
					var count = this.getAttribute('id').replace(/^iconBasket.*_/, '');
					var callback = {
						success: function(o) {
							Basket.loadBasketLight();
							yuiLoader.hide();
						},
						failure: function(o) {
							if (o.status == -1) {
								DEBUG('adding item to basket timed out - retrying');
								YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/sendData.html?a-oFrontend_FastOrder-count=' + count + '&a-oFrontend_FastOrder-code=' + code + '&button-oFrontend_FastOrder-but_order=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
							}
							else {
								yuiLoader.hide();
							}
						},
						timeout: 30000
					};

					yuiLoader.show();
					YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/sendData.html?a-oFrontend_FastOrder-count=' + count + '&a-oFrontend_FastOrder-code=' + code + '&button-oFrontend_FastOrder-but_order=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
				});
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * add click listeners to the ERP codes of items in the detailled view of previous orders
		 */
		modifyItemDetailLinks: function() {
			YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('aShowItem', 'a', 'containerOrdersDetail'), 'click', function() {
				Shop.displayItemDetailOrder(this.innerHTML);
			})
		},
		/*-----------------------------------------------------------------------------------------
		 * display German/English or Latin names depending on filter settings
		 */
		modifyNames: function() {
			var filters = Filter.getSettings();
			var names = YAHOO.util.Dom.getElementsByClassName('spanName', 'span', 'containerOrdersDetail');
			var latin = YAHOO.util.Dom.getElementsByClassName('spanLatin', 'span', 'containerOrdersDetail');

			if (filters.latin) {
				for (var i=0; i<names.length; i++) {
					names[i].style.display = 'none';
					latin[i].style.display = 'inline';
				}
			}
			else {
				for (var i=0; i<names.length; i++) {
					names[i].style.display = 'inline';
					latin[i].style.display = 'none';
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * set the ID of the order viewed last
		 *
		 * params:
		 *   orderID: the ID of the order viewed last
		 */
		setLastViewedOrderID: function(orderID) {
			_private.lastViewedOrderID = orderID;
		}
	};
	return _public;
}();
/**
 * \brief Functions for creating and rendering shop menu and teasers of shop items.
 */
var Shop = function() {
	// private properties and functions ===========================================================
	var _private = {
		cookie: PBEURL('').replace(/\?/, ''),
		// reference to current item if in detail view
		currentItem: {
			data: null,   // data set
			bundles: null // bundles data set (used in basket mode)
		},
		// data of all items
		data: null, // {items: [{CG: [nodeId], _*, ..., _*, price, VAT, code}], count}
		// mode for displaying items ('shop', 'search', 'favorites', 'basket', 'basketFast',
		// 'order') - this is used to determine what to do when returning to overview
		displayMode: 'shop',
		// arrays of all/filtered favorite items
		// some settings used for filtering items
		filters: null, // {latin, organic, conventional, items, products}
		// May the current user order pharmacy only items?
		fullAccess: false,
		// reference to favorites, nodeItems, items in the basket, searchResults, and the set of
		// items currently used
		items: {
			basket: {
				all: [], // [{data, name, latin, organic, code, row}]
				codes: [],
				filtered: []
			},
			current: null,
			favorites: {
				all: [], // [{data, name, latin, organic, code, row}]
				codes: [],
				filtered: [],
				initialized: false
			},
			node: {
				all: [], // [{data, name, latin, organic, code, row}]
				filtered: []
			},
			search: {
				all: [], // [{data, name, latin, organic, code, row}]
				filtered: []
			}
		},
		// (upper case) letter, items to be shown have to begin with; maximum number of item pages,
		// before filtering by first letter is turned on automatically; ID of the current node
		letter: '',
		maxPagesUnfiltered: 6,
		nodeId: 57,
		// current page no (used when returning to overview - should be reset to 1 when changing
		// node or display mode)
		pageNo: 1,
		// panels containing information on special items, handling fees, and special conditions
		// for large orders
		panels: {
			conditions: {
				hide: function() {},
				show: function() {}
			},
			fees: {
				hide: function() {},
				show: function() {}
			},
			info: {
				hide: function() {},
				show: function() {}
			}
		},
		// synonyms to be used when searching for items
		synonyms: null,
		/*-----------------------------------------------------------------------------------------
		 * add an item to the basket
		 *
		 * The row holding the addToBasket-link works as context of the function.
		 *
		 * prerequisities:
		 *   this function may be called in item detail view only
		 */
		addToBasket: function() {
			var count = '';
			var weight = '';
			var weightInitial;
			var code = this.code;
			var pos = this.pos;

			var callback = {
				success: function(o) {
					Basket.loadBasketLight();
					_private.generateItemDetail(_private.currentItem.data, pos);
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/sendData.html?a-oFrontend_FastOrderIndex-code=' + code + '&a-cFrontend_ShopBasketDetail-n_ChangeCount=' + count + '&a-cFrontend_ShopBasketDetail-d_ChangeWeight=' + weight + '&a-cFrontend_ShopBasketDetail-d_ChangeWeightOld=' + weightInitial + '&a-cFrontend_ShopBasketDetail-sz_ERPCodeDel=' + code + '&button-cFrontend_ShopBasketDetail-changeBasket=&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};

			if (this.bulk) {
				try {
					count = _public.checkInputCount(this.inputCount.value, '');
					weight = _public.checkInputWeight(this.inputWeight.value, '').replace(',', '.');
					weightInitial = parseFloat(this.inputWeightInitial.value.replace(/,/g, '.'));
				}
				catch (e) {
					DEBUG('Error: could not get weight or amount (' + e.message + ')');
					return;
				}
			}
			else {
				var min = 1;
				var multiple = false;

				if (code.match(/10$/)) {
					min = 10;
				}
				else if (code.match(/44$/) || code.match(/55$/) || code.match(/54$/)) {
					min = 6;
					multiple = true;
				}
				count = _public.checkInputCount(this.inputCount.value, '', min, multiple);
				weight = '0';
				weightInitial = '0';
			}
			if (weight.length == 0 || count.length == 0) {
				return;
			}
			yuiLoader.show();

			YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/sendData.html?a-oFrontend_FastOrderIndex-code=' + code + '&a-cFrontend_ShopBasketDetail-n_ChangeCount=' + count + '&a-cFrontend_ShopBasketDetail-d_ChangeWeight=' + weight + '&a-cFrontend_ShopBasketDetail-d_ChangeWeightOld=' + weightInitial + '&a-cFrontend_ShopBasketDetail-sz_ERPCodeDel=' + code + '&button-cFrontend_ShopBasketDetail-changeBasket=&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
		},
		/*-----------------------------------------------------------------------------------------
		 * add an item to the basket (fast order)
		 *
		 * The row holding the addToBasket-link works as context of the function.
		 *
		 * prerequisities:
		 *   this function may be called in fast order mode only
		 */
		addToBasketFast: function() {
			var count = '';
			var weight = '';
			var weightInitial;
			var code = this.code;
			var pos = this.pos;

			var callback = {
				success: function(o) {
					Basket.loadBasketLight();
					Content.loadBasketFast();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/sendData.html?a-oFrontend_FastOrderIndex-code=' + code + '&a-cFrontend_ShopBasketDetail-n_ChangeCount=' + count + '&a-cFrontend_ShopBasketDetail-d_ChangeWeight=' + weight + '&a-cFrontend_ShopBasketDetail-d_ChangeWeightOld=' + weightInitial + '&a-cFrontend_ShopBasketDetail-sz_ERPCodeDel=' + code + '&button-cFrontend_ShopBasketDetail-changeBasket=&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};

			if (this.bulk) {
				try {
					count = _public.checkInputCount(this.inputCount.value, '');
					weight = _public.checkInputWeight(this.inputWeight.value, '').replace(',', '.');
					weightInitial = parseFloat(this.inputWeightInitial.value.replace(/,/g, '.'));
				}
				catch (e) {
					DEBUG('Error: could not get weight or amount (' + e.message + ')');
					return;
				}
			}
			else {
				var min = 1;
				var multiple = false;

				if (code.match(/10$/)) {
					min = 10;
				}
				else if (code.match(/44$/) || code.match(/55$/) || code.match(/54$/)) {
					min = 6;
					multiple = true;
				}
				count = _public.checkInputCount(this.inputCount.value, '', min, multiple);
				weight = '0';
				weightInitial = '0';
			}
			if (weight.length == 0 || count.length == 0) {
				return;
			}
			yuiLoader.show();

			YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/sendData.html?a-oFrontend_FastOrderIndex-code=' + code + '&a-cFrontend_ShopBasketDetail-n_ChangeCount=' + count + '&a-cFrontend_ShopBasketDetail-d_ChangeWeight=' + weight + '&a-cFrontend_ShopBasketDetail-d_ChangeWeightOld=' + weightInitial + '&a-cFrontend_ShopBasketDetail-sz_ERPCodeDel=' + code + '&button-cFrontend_ShopBasketDetail-changeBasket=&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
		},
		/*-----------------------------------------------------------------------------------------
		 * get filter settings, and set handlers for filter links (feature of shop navigation)
		 */
		configureItemFilters: function() {
			var langNormal = document.getElementById('aLangNormal');
			var langLatin = document.getElementById('aLangLatin');
			var cultAll = document.getElementById('aCultAll');
			var cultOrganic = document.getElementById('aCultOrganic');
			var cultConventional = document.getElementById('aCultConventional');

			_private.filters = Filter.getSettings();

			// set language of names to be displayed
			if (langNormal && langLatin) {
				if (_private.filters.latin) {
					YAHOO.util.Dom.removeClass(langNormal, 'selected');
					YAHOO.util.Dom.addClass(langLatin, 'selected');
				}
				else {
					YAHOO.util.Dom.removeClass(langLatin, 'selected');
					YAHOO.util.Dom.addClass(langNormal, 'selected');
				}
			}
			// set cultivation filter
			if (cultAll && cultOrganic && cultConventional) {
				YAHOO.util.Dom.removeClass(cultAll, 'selected');
				YAHOO.util.Dom.removeClass(cultOrganic, 'selected');
				YAHOO.util.Dom.removeClass(cultConventional, 'selected');

				if (_private.filters.organic) {
					YAHOO.util.Dom.addClass(cultOrganic, 'selected');
				}
				else if (_private.filters.conventional) {
					YAHOO.util.Dom.addClass(cultConventional, 'selected');
				}
				else {
					YAHOO.util.Dom.addClass(cultAll, 'selected');
				}
			}
			// register handlers
			var firstLetterFilters = YAHOO.util.Dom.getElementsBy(function() {
				return true;
			}, 'a', 'spanFirstLetterFilters');

			YAHOO.util.Event.removeListener(firstLetterFilters, 'click');
			YAHOO.util.Event.addListener(firstLetterFilters, 'click', function() {
				if (YAHOO.util.Dom.hasClass(this, 'selected')) {
					_private.setLetterAndRenderItems();
				}
				else {
					_private.setLetterAndRenderItems(this.getAttribute('id').replace(/^aFirstLetter/, ''));
				}
			});
			if (langNormal && langLatin) {
				YAHOO.util.Event.removeListener(langNormal, 'click');
				YAHOO.util.Event.addListener(langNormal, 'click', function() {
					if (_private.filters.latin) {
						YAHOO.util.Dom.removeClass(langLatin, 'selected');
						YAHOO.util.Dom.addClass(langNormal, 'selected');
						_private.filters.latin = false;
						Filter.setSettings(_private.filters);
						_private.filterItems(_private.items.current);

						switch (_private.displayMode) {
							case 'shop':
							case 'search':
							case 'favorites':
								_private.goToPage(1);
								break;
							case 'basket':
							case 'basketFast':
								break;
							case 'order': 
								Orders.modifyNames();
								break;
						}
					}
				});
				YAHOO.util.Event.removeListener(langLatin, 'click');
				YAHOO.util.Event.addListener(langLatin, 'click', function() {
					if (!_private.filters.latin) {
						YAHOO.util.Dom.removeClass(langNormal, 'selected');
						YAHOO.util.Dom.addClass(langLatin, 'selected');
						_private.filters.latin = true;
						Filter.setSettings(_private.filters);
						_private.filterItems(_private.items.current);

						switch (_private.displayMode) {
							case 'shop':
							case 'search':
							case 'favorites':
								_private.goToPage(1);
								break;
							case 'basket':
							case 'basketFast':
								break;
							case 'order': 
								Orders.modifyNames();
								break;
						}
					}
				});
			}
			if (cultAll && cultOrganic && cultConventional) {
				YAHOO.util.Event.removeListener(cultAll, 'click');
				YAHOO.util.Event.addListener(cultAll, 'click', function() {
					if (_private.filters.organic || _private.filters.conventional) {
						yuiLoader.show();
						YAHOO.util.Dom.addClass(cultAll, 'selected');
						YAHOO.util.Dom.removeClass(cultOrganic, 'selected');
						YAHOO.util.Dom.removeClass(cultConventional, 'selected');
						_private.filters.organic = false;
						_private.filters.conventional = false;
						Filter.setSettings(_private.filters);
						_private.filterItems(_private.items.current);
						_private.goToPage(1);
						yuiLoader.hide();
					}
				});
				YAHOO.util.Event.removeListener(cultOrganic, 'click');
				YAHOO.util.Event.addListener(cultOrganic, 'click', function() {
					if (!_private.filters.organic) {
						yuiLoader.show();
						YAHOO.util.Dom.removeClass(cultAll, 'selected');
						YAHOO.util.Dom.addClass(cultOrganic, 'selected');
						YAHOO.util.Dom.removeClass(cultConventional, 'selected');
						_private.filters.organic = true;
						_private.filters.conventional = false;
						Filter.setSettings(_private.filters);
						_private.filterItems(_private.items.current);
						_private.goToPage(1);
						yuiLoader.hide();
					}
				});
				YAHOO.util.Event.removeListener(cultConventional, 'click');
				YAHOO.util.Event.addListener(cultConventional, 'click', function() {
					if (!_private.filters.conventional) {
						yuiLoader.show();
						YAHOO.util.Dom.removeClass(cultAll, 'selected');
						YAHOO.util.Dom.removeClass(cultOrganic, 'selected');
						YAHOO.util.Dom.addClass(cultConventional, 'selected');
						_private.filters.organic = false;
						_private.filters.conventional = true;
						Filter.setSettings(_private.filters);
						_private.filterItems(_private.items.current);
						_private.goToPage(1);
						yuiLoader.hide();
					}
				});
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * register event handlers for navigation
		 */
		configureNavi: function() {
			var fastOrder = document.getElementById('aFastOrder');
			//var links = YAHOO.util.Dom.getElementsByClassName('aNavi', 'a', 'boxNaviLevel2Products');
			var links = YAHOO.util.Dom.getElementsBy(function(elem) {
				try {
					return YAHOO.util.Dom.hasClass(elem, 'aNavi') && (elem.getAttribute('id').search(/^CG_/) >= 0);
				}
				catch (e) {
					return false;
				}
			}, 'a', 'boxNavi');
			var linkId;
			var obj;

			if (fastOrder != null) {
				YAHOO.util.Event.removeListener(fastOrder, 'click');
				YAHOO.util.Event.addListener(fastOrder, 'click', function() {
					Content.loadBasketFast();
				});
			}
			for (var i=0; i<links.length; i++) {
				linkId = links[i].getAttribute('id');

				if (linkId.search(/^CG_/) >= 0) {
					if (linkId == 'CG_0') {
						links[i].removeAttribute('href');
					}
					obj = new Object();
					obj.id = parseInt(linkId.replace(/^CG_/, ''));
					YAHOO.util.Event.removeListener(links[i], 'click');
					YAHOO.util.Event.addListener(links[i], 'click', function() {
						_public.toggleFilters(true);
						_private.showNode(this.id);
					}, obj, true);
					delete obj;
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * delete an item from the basket
		 *
		 * The row holding the deleteFromBasket-link works as context of the function.
		 *
		 * prerequisities:
		 *   this function may be called in item detail view only
		 */
		deleteFromBasket: function() {
			var weight = this.inputWeightInitial ? parseFloat(this.inputWeightInitial.value.replace(/,/g, '.')) : '0';
			var code = this.code;
			var pos = this.pos;
			var rows = YAHOO.util.Dom.getElementsByClassName('boxItemBundlesRow', 'div', 'containerItemDetail').length;
			var returnToBasket;

			var callback = {
				success: function(o) {
					Basket.loadBasketLight();

					if (returnToBasket) {
						Content.loadBasket();
					}
					else {
						_private.generateItemDetail(_private.currentItem.data, pos);
					}
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('deleting item from basket timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/sendData.html?a-cFrontend_ShopBasketDetail-d_WeightDel=' + weight + '&a-cFrontend_ShopBasketDetail-sz_ERPCodeDel=' + code + '&a-cFrontend_ShopBasketDetail-delFromBasket=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};

			yuiLoader.show();

			// return to basket overview after deleting the last bundle of the current item (if in
			// detail view of an item in the basket)
			if (_private.displayMode == 'basket' && (rows == 1 || (_private.currentItem.bundles.length < 2 && weight > 0.0))) {
				returnToBasket = true;
			}
			else {
				returnToBasket = false;
			}
			YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/sendData.html?a-cFrontend_ShopBasketDetail-d_WeightDel=' + weight + '&a-cFrontend_ShopBasketDetail-sz_ERPCodeDel=' + code + '&a-cFrontend_ShopBasketDetail-delFromBasket=x&xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
		},
		/*-----------------------------------------------------------------------------------------
		 * add data and event listeners to the form required for the item currently selected (bulk
		 * ware)
		 *
		 * params:
		 *   data: an object containing a set of data for the item currently selected
		 * prerequisities:
		 *   * there must be a div with ID 'boxFastOrderItem'
		 *   * data._9 == 'kg' (i.e. data must represent an item sold as bulk ware)
		 *   * data.price > 0.00 (i.e. data must represent an item no request is required for)
		 */
		fastOrderBulk: function(data) {
			var units = YAHOO.util.Dom.getElementsByClassName('spanUnit', 'span', 'boxFastOrderItem');

			try {
				// insert unit name
				for (var i=0; i<units.length; i++) {
					units[i].innerHTML = 'kg';
				}
			}
			catch (e) {
				DEBUG('could not determine unit name for current item: ' + e.message);
				return;
			}
			// insert JSON data -------------------------------------------------------------------
			if (!_private.filters) {
				_private.configureItemFilters();
			}
			// Artikelnummer
			document.getElementById('JSON_CODE').innerHTML = _private.trim(data.code);

			// _6 -> Artikelbezeichnung, _5 -> lateinische Bezeichnung
			if (data._6 && !_private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._6);
			}
			if (data._5 && _private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._5);
			}
			// Monografie, _19 -> kbA
			if (data._33 && data._19) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33) + ', ' + ((BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>');
			}
			else if (data._33) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33);
			}
			else if (data._19) {
				document.getElementById('JSON_MONO').innerHTML = (BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>';
			}
			// Info: _7 -> apthokenpflichtig, VAT
			if (data._7) {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA" title="' + ((BB.getLang() == 'de') ? 'apothekenpflichtig' : 'pharmacy-only') + '">a</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA"></span>';
			}
			if (data.VAT == 7.0) {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM" title="' + ((BB.getLang() == 'de') ? 'reduzierter Mehrwertsteuersatz' : 'reduced VAT rate') + '">m</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM"></span>';
			}
			// _17 -> Verfügbarkeit
			if (typeof data._17 != 'undefined') {
				var availBox = document.getElementById('JSON_AVAIL');

				switch (data._17) {
					case 0:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'sofort verf\u00FCgbar' : 'available immediately');
						break;
					case 1:
						availBox.className = 'iconAvail iconAvail1';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'begrenzt verf\u00FCgbar' : 'limited availability');
						break;
					case 2:
						availBox.className = 'iconAvail iconAvail2';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
						break;
					default:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
				}
			}
			// Favoritenstatus
			var obj = new Object();
			var iconFav = document.getElementById('JSON_FAV');

			if (_private.isFavorite(data.code)) {
				iconFav.className = 'iconFav iconFavOn';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'aus meinen Favoriten entfernen' : 'remove from my favourites');
			}
			else {
				iconFav.className = 'iconFav iconFavOff';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'zu meinen Favoriten hinzuf\u00FCgen' : 'add to my favourites');
			}
			obj.code = data.code;
			obj.icon = iconFav;
			YAHOO.util.Event.removeListener(iconFav, 'click');
			YAHOO.util.Event.addListener(iconFav, 'click', _private.toggleFavorite, obj, true);
			delete obj;

			// Preisangaben
			if (typeof data.price == 'number') {
				document.getElementById('JSON_PRICE_100').innerHTML = _private.priceToString(_private.roundPrice(data.price * 0.10 + 2.10));
				document.getElementById('JSON_PRICE_250').innerHTML = _private.priceToString(_private.roundPrice(data.price * 0.25 + 2.10));
				document.getElementById('JSON_PRICE_500').innerHTML = _private.priceToString(_private.roundPrice(data.price * 0.50 + 2.10));
				document.getElementById('JSON_PRICE_1').innerHTML = _private.priceToString(_private.roundPrice(data.price + 1.50));
				document.getElementById('JSON_PRICE_5').innerHTML = _private.priceToString(_private.roundPrice(data.price));
			}
			// register handlers
			var inputCount = document.getElementById('inputCount');
			var inputCountOld = document.getElementById('inputCountOld');
			var inputWeight = document.getElementById('inputWeight');
			var inputWeightOld = document.getElementById('inputWeightOld');
			var inputWeightInitial = document.getElementById('inputWeightInitial');
			var iconBasket = document.getElementById('iconBasket');
			var spanWeightTotal = document.getElementById('spanWeightTotal');
			var boxPriceSingle = document.getElementById('boxPriceSingle');
			var boxPriceTotal = document.getElementById('boxPriceTotal');

			if (!(inputCount && inputCountOld && inputWeight && inputWeightOld && inputWeightInitial &&
			      iconBasket && spanWeightTotal && boxPriceSingle && boxPriceTotal)) {
				return;
			}
			YAHOO.util.Event.removeListener(inputCount, 'change');
			YAHOO.util.Event.addListener(inputCount, 'change', function() {
				this.value = _public.checkInputCount(this.value, inputCountOld.value);
				inputCountOld.value = this.value;
				_private.fastOrderUpdateBulk(inputCount.value, inputWeight.value, data.price);
			});
			YAHOO.util.Event.removeListener(inputWeight, 'change');
			YAHOO.util.Event.addListener(inputWeight, 'change', function() {
				this.value = _public.checkInputWeight(this.value, inputWeightOld.value);
				inputWeightOld.value = this.value;
				_private.fastOrderUpdateBulk(inputCount.value, inputWeight.value, data.price);
			});
			var obj = {
				code: data.code,
				bulk: true,
				inputCount: inputCount,
				inputWeight: inputWeight,
				inputWeightInitial: inputWeightInitial
			};

			YAHOO.util.Event.removeListener(iconBasket, 'click');
			YAHOO.util.Event.addListener(iconBasket, 'click', _private.addToBasketFast, obj, true);

			// add key listeners
			var keyListenerCount = new YAHOO.util.KeyListener(inputCount, {
				keys: [0x0a, 0x0d]
			}, {
				fn: _private.addToBasketFast,
				scope: obj,
				correctScope: true
			});
			var keyListenerWeight = new YAHOO.util.KeyListener(inputWeight, {
				keys: [0x0a, 0x0d]
			}, {
				fn: _private.addToBasketFast,
				scope: obj,
				correctScope: true
			});
			var keyListenerBasket = new YAHOO.util.KeyListener(iconBasket, {
				keys: [0x0a, 0x0d]
			}, {
				fn: _private.addToBasketFast,
				scope: obj,
				correctScope: true
			});

			keyListenerCount.enable();
			keyListenerWeight.enable();
			keyListenerBasket.enable();

			delete obj;
		},
		/*-----------------------------------------------------------------------------------------
		 * add data and event listeners to the form required for the item currently selected
		 * (bundle ware)
		 *
		 * params:
		 *   data: an object containing a set of data for the item currently selected
		 * prerequisities:
		 *   * there must be a div with ID 'boxFastOrderItem'
		 *   * data._9 != 'kg' and data._9 != 'Ltr' (i.e. data must represent an item not sold as
		 *     bulk or liquid ware)
		 *   * data.price > 0.00 (i.e. data must represent an item no request is required for)
		 */
		fastOrderBundle: function(data) {
			var units = YAHOO.util.Dom.getElementsByClassName('spanUnit', 'span', 'boxFastOrderItem');
			var min;
			var mutliple = false;

			// set minimum amount and unit --------------------------------------------------------
			try {
				// insert unit name
				for (var i=0; i<units.length; i++) {
					switch (data._9.toLowerCase()) {
						case 'dos':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Dos.' : 'tins';
							break;
						case 'kar':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Kar.' : 'boxes';
							break;
						case 'pck':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Pck.' : 'pcks.';
							break;
						case 'st':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'St.' : 'pcs.';
							break;
						case 't\u00FCt':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'T\u00FCt.' : 'bags';
							break;
						default:
							DEBUG('could not determine unit name for current item: ' + e.message);
							return;
					}
				}
			}
			catch (e) {
				DEBUG('could not determine unit name for current item: ' + e.message);
				return;
			}
			if (_private.trim(data.code).match(/10$/)) {
				document.getElementById('spanMinCount').innerHTML = '10';
				min = 10;
			}
			else if (_private.trim(data.code).match(/44$/) || _private.trim(data.code).match(/55$/) || _private.trim(data.code).match(/54$/)) {
				document.getElementById('spanMinCount').innerHTML = '6';
				min = 6;
				multiple = true;

				try {
					document.getElementById('boxBiogaHint').style.display = "block";
				}
				catch (e) {}
			}
			else {
				document.getElementById('spanMinCount').innerHTML = '1';
				min = 1;
			}
			// insert JSON data -------------------------------------------------------------------
			if (!_private.filters) {
				_private.configureItemFilters();
			}
			// Artikelnummer
			document.getElementById('JSON_CODE').innerHTML = _private.trim(data.code);

			// _6 -> Artikelbezeichnung, _5 -> lateinische Bezeichnung
			if (data._6 && !_private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._6);
			}
			if (data._5 && _private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._5);
			}
			// Monografie, _19 -> kbA
			if (data._33 && data._19) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33) + ', ' + ((BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>');
			}
			else if (data._33) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33);
			}
			else if (data._19) {
				document.getElementById('JSON_MONO').innerHTML = (BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>';
			}
			// Info: _7 -> apthokenpflichtig, VAT
			if (data._7) {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA" title="' + ((BB.getLang() == 'de') ? 'apothekenpflichtig' : 'pharmacy-only') + '">a</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA"></span>';
			}
			if (data.VAT == 7.0) {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM" title="' + ((BB.getLang() == 'de') ? 'reduzierter Mehrwertsteuersatz' : 'reduced VAT rate') + '">m</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM"></span>';
			}
			// _17 -> Verfügbarkeit
			if (typeof data._17 != 'undefined') {
				var availBox = document.getElementById('JSON_AVAIL');

				switch (data._17) {
					case 0:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'sofort verf\u00FCgbar' : 'available immediately');
						break;
					case 1:
						availBox.className = 'iconAvail iconAvail1';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'begrenzt verf\u00FCgbar' : 'limited availability');
						break;
					case 2:
						availBox.className = 'iconAvail iconAvail2';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
						break;
					default:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
				}
			}
			// Favoritenstatus
			var obj = new Object();
			var iconFav = document.getElementById('JSON_FAV');

			if (_private.isFavorite(data.code)) {
				iconFav.className = 'iconFav iconFavOn';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'aus meinen Favoriten entfernen' : 'remove from my favourites');
			}
			else {
				iconFav.className = 'iconFav iconFavOff';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'zu meinen Favoriten hinzuf\u00FCgen' : 'add to my favourites');
			}
			obj.code = data.code;
			obj.icon = iconFav;
			YAHOO.util.Event.removeListener(iconFav, 'click');
			YAHOO.util.Event.addListener(iconFav, 'click', _private.toggleFavorite, obj, true);
			delete obj;

			// Preisangaben
			if (typeof data.price == 'number') {
				document.getElementById('JSON_PRICE').innerHTML = _private.priceToString(data.price);
			}
			// register handlers
			var inputCount = document.getElementById('inputCount');
			var inputCountOld = document.getElementById('inputCountOld');
			var iconBasket = document.getElementById('iconBasket');
			var boxPriceTotal = document.getElementById('boxPriceTotal');

			if (!(inputCount && inputCountOld && iconBasket && boxPriceTotal)) {
				return;
			}
			var obj = {
				code: data.code,
				bulk: false,
				inputCount: inputCount,
				inputCountOld: inputCountOld,
				boxPriceTotal: boxPriceTotal,
				min: min,
				multiple: multiple
			};

			YAHOO.util.Event.removeListener(inputCount, 'change');
			YAHOO.util.Event.addListener(inputCount, 'change', function() {
				this.inputCount.value = _public.checkInputCount(this.inputCount.value, this.inputCountOld.value, this.min, this.multiple);
				this.inputCountOld.value = this.inputCount.value;
				this.boxPriceTotal.innerHTML = _private.priceToString(this.inputCount.value ? (parseInt(this.inputCount.value) * data.price) : 0.00);
			}, obj, true);
			YAHOO.util.Event.removeListener(iconBasket, 'click');
			YAHOO.util.Event.addListener(iconBasket, 'click', _private.addToBasketFast, obj, true);

			// add key listeners
			var keyListenerCount = new YAHOO.util.KeyListener(inputCount, {
				keys: [0x0a, 0x0d]
			}, {
				fn: _private.addToBasketFast,
				scope: obj,
				correctScope: true
			});
			var keyListenerBasket = new YAHOO.util.KeyListener(iconBasket, {
				keys: [0x0a, 0x0d]
			}, {
				fn: _private.addToBasketFast,
				scope: obj,
				correctScope: true
			});

			keyListenerCount.enable();
			keyListenerBasket.enable();

			delete obj;			
		},
		/*-----------------------------------------------------------------------------------------
		 * add data and event listeners to the form required for the item currently selected
		 * (liquid ware)
		 *
		 * params:
		 *   data: an object containing a set of data for the item currently selected
		 * prerequisities:
		 *   * there must be a div with ID 'boxFastOrderItem'
		 *   * data._9 == 'Ltr' (i.e. data must represent an item sold as liquid ware)
		 *   * data.price > 0.00 (i.e. data must represent an item no request is required for)
		 */
		fastOrderLiquid: function(data) {
			var units = YAHOO.util.Dom.getElementsByClassName('spanUnit', 'span', 'boxFastOrderItem');

			try {
				// insert unit name
				for (var i=0; i<units.length; i++) {
					units[i].innerHTML = (BB.getLang() == 'de') ? 'Ltr.' : 'l';
				}
			}
			catch (e) {
				DEBUG('could not determine unit name for current item: ' + e.message);
				return;
			}
			// insert JSON data -------------------------------------------------------------------
			if (!_private.filters) {
				_private.configureItemFilters();
			}
			// Artikelnummer
			document.getElementById('JSON_CODE').innerHTML = _private.trim(data.code);

			// _6 -> Artikelbezeichnung, _5 -> lateinische Bezeichnung
			if (data._6 && !_private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._6);
			}
			if (data._5 && _private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._5);
			}
			// Monografie, _19 -> kbA
			if (data._33 && data._19) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33) + ', ' + ((BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>');
			}
			else if (data._33) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33);
			}
			else if (data._19) {
				document.getElementById('JSON_MONO').innerHTML = (BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>';
			}
			// Info: _7 -> apthokenpflichtig, VAT
			if (data._7) {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA" title="' + ((BB.getLang() == 'de') ? 'apothekenpflichtig' : 'pharmacy-only') + '">a</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA"></span>';
			}
			if (data.VAT == 7.0) {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM" title="' + ((BB.getLang() == 'de') ? 'reduzierter Mehrwertsteuersatz' : 'reduced VAT rate') + '">m</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM"></span>';
			}
			// _17 -> Verfügbarkeit
			if (typeof data._17 != 'undefined') {
				var availBox = document.getElementById('JSON_AVAIL');

				switch (data._17) {
					case 0:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'sofort verf\u00FCgbar' : 'available immediately');
						break;
					case 1:
						availBox.className = 'iconAvail iconAvail1';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'begrenzt verf\u00FCgbar' : 'limited availability');
						break;
					case 2:
						availBox.className = 'iconAvail iconAvail2';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
						break;
					default:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
				}
			}
			// Favoritenstatus
			var obj = new Object();
			var iconFav = document.getElementById('JSON_FAV');

			if (_private.isFavorite(data.code)) {
				iconFav.className = 'iconFav iconFavOn';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'aus meinen Favoriten entfernen' : 'remove from my favourites');
			}
			else {
				iconFav.className = 'iconFav iconFavOff';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'zu meinen Favoriten hinzuf\u00FCgen' : 'add to my favourites');
			}
			obj.code = data.code;
			obj.icon = iconFav;
			YAHOO.util.Event.removeListener(iconFav, 'click');
			YAHOO.util.Event.addListener(iconFav, 'click', _private.toggleFavorite, obj, true);
			delete obj;

			// Preisangaben
			if (typeof data.price == 'number') {
				document.getElementById('JSON_PRICE').innerHTML = _private.priceToString(data.price);
			}
			// register handlers
			var inputCount = document.getElementById('inputCount');
			var inputCountOld = document.getElementById('inputCountOld');
			var inputWeight = document.getElementById('inputWeight');
			var inputWeightOld = document.getElementById('inputWeightOld');
			var inputWeightInitial = document.getElementById('inputWeightInitial');
			var iconBasket = document.getElementById('iconBasket');
			var spanWeightTotal = document.getElementById('spanWeightTotal');
			var boxPriceSingle = document.getElementById('boxPriceSingle');
			var boxPriceTotal = document.getElementById('boxPriceTotal');

			if (!(inputCount && inputCountOld && inputWeight && inputWeightOld && inputWeightInitial &&
			      iconBasket && spanWeightTotal && boxPriceSingle && boxPriceTotal)) {
				yuiLoader.hide();
				return;
			}
			YAHOO.util.Event.removeListener(inputCount, 'change');
			YAHOO.util.Event.addListener(inputCount, 'change', function() {
				this.value = _public.checkInputCount(this.value, inputCountOld.value);
				inputCountOld.value = this.value;
				_private.fastOrderUpdateLiquid(inputCount.value, inputWeight.value, data.price);
			});
			YAHOO.util.Event.removeListener(inputWeight, 'change');
			YAHOO.util.Event.addListener(inputWeight, 'change', function() {
				this.value = _public.checkInputCount(this.value, inputWeightOld.value);
				inputWeightOld.value = this.value;
				_private.fastOrderUpdateLiquid(inputCount.value, inputWeight.value, data.price);
			});
			var obj = {
				code: data.code,
				bulk: true,
				inputCount: inputCount,
				inputWeight: inputWeight,
				inputWeightInitial: inputWeightInitial
			};

			YAHOO.util.Event.removeListener(iconBasket, 'click');
			YAHOO.util.Event.addListener(iconBasket, 'click', _private.addToBasketFast, obj, true);

			// add key listeners
			var keyListenerCount = new YAHOO.util.KeyListener(inputCount, {
				keys: [0x0a, 0x0d]
			}, {
				fn: _private.addToBasketFast,
				scope: obj,
				correctScope: true
			});
			var keyListenerWeight = new YAHOO.util.KeyListener(inputWeight, {
				keys: [0x0a, 0x0d]
			}, {
				fn: _private.addToBasketFast,
				scope: obj,
				correctScope: true
			});
			var keyListenerBasket = new YAHOO.util.KeyListener(inputBasket, {
				keys: [0x0a, 0x0d]
			}, {
				fn: _private.addToBasketFast,
				scope: obj,
				correctScope: true
			});

			keyListenerCount.enable();
			keyListenerWeight.enable();
			keyListenerBasket.enable();

			delete obj;
		},
		/*-----------------------------------------------------------------------------------------
		 * add data and event listeners to the form required for the item currently selected (only
		 * available on request)
		 *
		 * params:
		 *   data: an object containing a set of data for the item currently selected
		 * prerequisities:
		 *   * data.price <= 0.00 (i.e. data must represent an item only available on request)
		 */
		fastOrderRequest: function(data) {
			var units = YAHOO.util.Dom.getElementsByClassName('spanUnit', 'span', 'boxFastOrderItem');

			try {
				// insert unit name
				for (var i=0; i<units.length; i++) {
					switch (data._9.toLowerCase()) {
						case 'kg':
							units[i].innerHTML = 'kg';
							break;
						case 'ltr':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Ltr.' : 'l';
							break;
						case 'dos':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Dos.' : 'tins';
							break;
						case 'kar':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Kar.' : 'boxes';
							break;
						case 'pck':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Pck.' : 'pcks.';
							break;
						case 'st':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'St.' : 'pcs.';
							break;
						case 't\u00FCt':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'T\u00FCt.' : 'bags';
							break;
						default:
							DEBUG('could not determine unit name for current item: ' + e.message);
							return;						}
				}
			}
			catch (e) {
				DEBUG('could not determine unit name for current item: ' + e.message);
				return;
			}
			// insert JSON data -------------------------------------------------------------------
			if (!_private.filters) {
				_private.configureItemFilters();
			}
			// Artikelnummer
			document.getElementById('JSON_CODE').innerHTML = _private.trim(data.code);

			// _6 -> Artikelbezeichnung, _5 -> lateinische Bezeichnung
			if (data._6 && !_private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._6);
			}
			if (data._5 && _private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._5);
			}
			// Monografie, _19 -> kbA
			if (data._33 && data._19) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33) + ', ' + ((BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>');
			}
			else if (data._33) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33);
			}
			else if (data._19) {
				document.getElementById('JSON_MONO').innerHTML = (BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>';
			}
			// Info: _7 -> apthokenpflichtig, VAT
			if (data._7) {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA" title="' + ((BB.getLang() == 'de') ? 'apothekenpflichtig' : 'pharmacy-only') + '">a</span>';

				if (!_private.fullAccess) {
					try {
						var request = document.getElementById('aItemRequest');

						request.parentNode.removeChild(request);
					}
					catch (e) {}
				}
			}
			else {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA"></span>';
			}
			if (data.VAT == 7.0) {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM" title="' + ((BB.getLang() == 'de') ? 'reduzierter Mehrwertsteuersatz' : 'reduced VAT rate') + '">m</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM"></span>';
			}
			// _17 -> Verfügbarkeit
			if (typeof data._17 != 'undefined') {
				var availBox = document.getElementById('JSON_AVAIL');

				switch (data._17) {
					case 0:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'sofort verf\u00FCgbar' : 'available immediately');
						break;
					case 1:
						availBox.className = 'iconAvail iconAvail1';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'begrenzt verf\u00FCgbar' : 'limited availability');
						break;
					case 2:
						availBox.className = 'iconAvail iconAvail2';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
						break;
					default:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
				}
			}
			// Favoritenstatus
			var obj = new Object();
			var iconFav = document.getElementById('JSON_FAV');

			if (_private.isFavorite(data.code)) {
				iconFav.className = 'iconFav iconFavOn';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'aus meinen Favoriten entfernen' : 'remove from my favourites');
			}
			else {
				iconFav.className = 'iconFav iconFavOff';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'zu meinen Favoriten hinzuf\u00FCgen' : 'add to my favourites');
			}
			obj.code = data.code;
			obj.icon = iconFav;
			YAHOO.util.Event.removeListener(iconFav, 'click');
			YAHOO.util.Event.addListener(iconFav, 'click', _private.toggleFavorite, obj, true);
			delete obj;

			// request panel
			var request = document.getElementById('aItemRequest');

			if (request) {
				var callback = {
					success: function(o) {
						var name;

						if (_private.filters.latin) {
							name = _private.trim(data._5);
						}
						else {
							name = _private.trim(data._6);
						}
						document.getElementById('boxFormRequest').innerHTML = o.responseText;
						document.getElementById('spanItem').innerHTML = data.code + ' ' + name;
						document.getElementById('hiddenItem').value = data.code + ' ' + name;
						YAHOO.util.Event.addListener('aRequestSubmit', 'click', function() {
							document.forms['Galke_PreisAnfrage'].elements['command-Galke_PreisAnfrage-sendmail'].value='x';
							document.forms['Galke_PreisAnfrage'].submit();
						});
						YAHOO.util.Event.addListener('aRequestPanelClose', 'click', function() {
							_private.panels.request.hide();
						});
						_private.panels.request.show();
						Forms.initOverLabels();
						yuiLoader.hide();
					},
					failure: function(o) {
						if (o.status = -1) {
							DEBUG('getting price request form timed out - retrying');
							YAHOO.util.Connect.asyncRequest('POST', '/_includes/' + BB.getLangPath() + 'shopPriceRequest.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
						}
						else {
							if (BB.getLang() == 'de') {
								document.getElementById('boxFormRequest').innerHTML
								 =   'Leider ist ein Fehler aufgetreten. Bitte wenden Sie '
								   + 'sich mit Ihrer Anfrage per E-Mail an '
								   + '<a href="mailto:info@galke.com">info@galke.com</a>.'
								   + '<br>'
								   + 'Gerne k\u00F6nnen Sie auch telefonisch unter '
								   + '+49.\u00A05537.\u00A08681.\u00A00 oder per Fax an '
								   + 'die Nummer +49.\u00A05537.\u00A08681.\u00A020 '
								   + 'Kontakt zu uns aufnehmen';
							}
							else {
								document.getElementById('boxFormRequest').innerHTML
								 =   'Unfortunately an error has occured. Please refer to '
								   + '<a href="mailto:info@galke.com">info@galke.com</a> per '
								   + 'e-mail with your request.'
								   + '<br>'
								   + 'Please feel free to contact us by telephone as well at '
								   + '+49.\u00A05537.\u00A08681.\u00A00 or per fax at '
								   + '+49.\u00A05537.\u00A08681.\u00A020.';
							}
							_private.panels.request.show();
							yuiLoader.hide();
						}
					},
					timeout: 30000
				};
				var showRequestPanel = function() {
					yuiLoader.show();
					YAHOO.util.Connect.asyncRequest('POST', '/_includes/' + BB.getLangPath() + 'shopPriceRequest.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
				};
				var keyListenerRequest = new YAHOO.util.KeyListener(request, {
					keys: [0x0a, 0x0d]
				}, {
					fn: showRequestPanel
				});

				YAHOO.util.Event.removeListener(request, 'click');
				YAHOO.util.Event.addListener(request, 'click', showRequestPanel);
				keyListenerRequest.enable();
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * update fast order form for bulk ware (total weight, single price, total price)
		 *
		 * params:
		 *   count: number of bundles ordered (number or string)
		 *   weight: the weight of a single bundle (number or string)
		 *   price5: price per kg if at least 5kg ordered (number (!))
		 */
		fastOrderUpdateBulk: function(count, weight, price5) {
			// create numerical values from strings passed to the function
			var weightTotal;
			var priceSingle = parseFloat(_public.calculatePriceSingleBulk(count, weight, price5).replace(',', '.'));
			var priceTotal;

			if (typeof count == 'string') {
				count = _public.checkInputCount(count, '');

				if (count == '') {
					count = 0;
				}
				else {
					count = parseInt(count);
				}
			}
			else if (typeof count != 'number') {
				count = 0;
			}
			if (typeof weight == 'string') {
				weight = _public.checkInputWeight(weight, '').replace(',', '.');

				if (weight == '') {
					weight = 0.0;
				}
				else {
					weight = parseFloat(weight);
				}
			}
			else if (typeof weight != 'number' || weight < 0.1) {
				weight = 0.0;
			}
			if (typeof price5 != 'number' || price5 < 0) {
				price5 = 0.0;
			}
 			// handle numerical values
			weightTotal = weight * count;
			priceTotal = priceSingle * count;

			// create strings from numerical values for output to HTML
			weightTotal = weightTotal.toFixed(3);

			if (weightTotal.match(/\./)) {
				weightTotal = weightTotal.replace(/0+$/, '').replace(/\.$/, '');
			}
			priceSingle = priceSingle.toFixed(2);
			priceTotal = priceTotal.toFixed(2);

			if (BB.getLang() == 'de') {
				weightTotal = weightTotal.replace('.', ',');
				priceSingle = priceSingle.replace('.', ',');
				priceTotal = priceTotal.replace('.', ',');
			}
			// output of values calculated above
			var spanWeightTotal = document.getElementById('spanWeightTotal');
			var boxPriceSingle = document.getElementById('boxPriceSingle');
			var boxPriceTotal = document.getElementById('boxPriceTotal');

			if (spanWeightTotal) {
				spanWeightTotal.innerHTML = weightTotal;
			}
			if (boxPriceSingle) {
				boxPriceSingle.innerHTML = priceSingle;
			}
			if (boxPriceTotal) {
				boxPriceTotal.innerHTML = priceTotal;
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * update fast order form for liquid ware (total weight, single price, total price)
		 *
		 * params:
		 *   count: number of bundles ordered (number or string)
		 *   weight: the volume of a single bundle (number or string)
		 *   price: price per liter (number (!))
		 */
		fastOrderUpdateLiquid: function(count, weight, price) {
			// create numerical values from strings passed to the function
			var weightTotal;
			var priceSingle;
			var priceTotal;

			if (typeof count == 'string') {
				count = _public.checkInputCount(count, '');

				if (count == '') {
					count = 0;
				}
				else {
					count = parseInt(count);
				}
			}
			else if (typeof count != 'number') {
				count = 0;
			}
			if (typeof weight == 'string') {
				weight = _public.checkInputCount(weight, '').replace(',', '.');

				if (weight == '') {
					weight = 0;
				}
				else {
					weight = parseInt(weight);
				}
			}
			else if (typeof weight != 'number' || weight < 1) {
				weight = 0;
			}
			if (typeof price != 'number' || price < 0) {
				price = 0.0;
			}
			priceSingle = weight * price;

 			// handle numerical values
			weightTotal = weight * count;
			priceTotal = priceSingle * count;

			// create strings from numerical values for output to HTML
			weightTotal = weightTotal.toFixed(3);

			if (weightTotal.match(/\./)) {
				weightTotal = weightTotal.replace(/0+$/, '').replace(/\.$/, '');
			}
			priceSingle = priceSingle.toFixed(2);
			priceTotal = priceTotal.toFixed(2);

			if (BB.getLang() == 'de') {
				weightTotal = weightTotal.replace('.', ',');
				priceSingle = priceSingle.replace('.', ',');
				priceTotal = priceTotal.replace('.', ',');
			}
			// output of values calculated above
			var spanWeightTotal = document.getElementById('spanWeightTotal');
			var boxPriceSingle = document.getElementById('boxPriceSingle');
			var boxPriceTotal = document.getElementById('boxPriceTotal');

			if (spanWeightTotal) {
				spanWeightTotal.innerHTML = weightTotal;
			}
			if (boxPriceSingle) {
				boxPriceSingle.innerHTML = priceSingle;
			}
			if (boxPriceTotal) {
				boxPriceTotal.innerHTML = priceTotal;
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * apply filters (_private.filters) to an array of items
		 *
		 * params:
		 *   items: an object containing two arrays .all (items to be filtered) and .filtered (for
		 *          saving filtered items)
		 * prerequisities:
		 *   _private.nodeId and _private.letter must be initialized. Thus this function should be
		 *   called by _private.showNode() or event handlers only.
		 * side effects:
		 *   Items will be filtered only if _private.displayMode is set to 'shop', 'favorites', or
		 *   'search'. Any other value will result in (items.filtered == items.all) as long as no
		 *   error is detected.
		 */
		filterItems: function(items) {
			if (   typeof items != 'object' || items == null
			    || typeof items.all != 'object' || typeof items.all.length != 'number'
			    || typeof items.filtered != 'object' || typeof items.filtered.length != 'number') {
				return;
			}
			// filter items
			if (   _private.displayMode == 'shop'
			    || _private.displayMode == 'favorites'
			    || _private.displayMode == 'search') {
				items.filtered.length = 0;

				if (    _private.displayMode == 'shop'
				    && (_private.letter == null || _private.letter == '')
				    && items.all.length > _private.maxPagesUnfiltered * _private.filters.items
					&& !BB.isAlpha(_private.letter)) {
					// only show items beginning with 'A' if there are more than
					//  _private.maxPagesUnfiltered pages of items (shop mode only)
					_private.setLetter('A');
				}
				for (var i=0; i<items.all.length; i++) {
					var show = true;

					// apply filters
					if (BB.isAlpha(_private.letter)) {
						if (!_private.filters.latin) {
							try {
								if (items.all[i].name.substring(0,1).toUpperCase() != _private.letter) {
									show = false;
								}
							}
							catch (e) {
								show = false; // name missing
							}
						}
						else {
							try {
								if (items.all[i].latin.substring(0,1).toUpperCase() != _private.letter) {
									show = false;
								}
							}
							catch (e) {
								show = false; // name missing
							}
						}
					}
					if (   (_private.filters.organic && !items.all[i].organic)
					    || (_private.filters.conventional && items.all[i].organic)) {
						show = false;
					}
					// save items to be shown
					if (show) {
						items.filtered[items.filtered.length] = items.all[i];
					}
				}
			}
			// do not filter items
			else {
				items.filtered = items.all;
			}
			// sort filtered items
			var names = new Array();
			var indexes;
			var tmp = new Array();

			if (!_private.filters.latin) {
				for (var i=0; i<items.filtered.length; i++) {
					names[i] = BB.replaceSpecialChars(items.filtered[i].name).toLowerCase();
					//names[i] = BB.replaceSpecialChars(items.filtered[i].name.replace(/ .*$/, '')).toLowerCase();
				}
			}
			else {
				for (var i=0; i<items.filtered.length; i++) {
					names[i] = BB.replaceSpecialChars(items.filtered[i].latin).toLowerCase();
					//names[i] = BB.replaceSpecialChars(items.filtered[i].latin.replace(/ .*$/, '')).toLowerCase();
				}
			}
			indexes = BB.mergeSort(names);

			for (var i=0; i<indexes.length; i++) {
				tmp[i] = items.filtered[indexes[i]];
			}
			for (var i=0; i<tmp.length; i++) {
				items.filtered[i] = tmp[i];
			}
			delete names;
			delete indexes;
			delete tmp;
		},
		/*-----------------------------------------------------------------------------------------
		 * find the index of a value in a list of values
		 * 
		 * params:
		 *   value: the value to search for
		 *   list: the list of values to search in
		 * return value:
		 *   index of the value if found, -1 otherwise
		 */
		findValueInList: function(value, list) {
			for (var i=0; i<list.length; i++) {
				if (value == list[i]) {
					return i;
				}
			}
			return -1;
		},
		/*-----------------------------------------------------------------------------------------
		 * generate _private.items.basket.all
		 *
		 * prerequisities:
		 *   _private.items.basket.codes must have been initialized
		 */
		generateBasketItems: function() {
			var found;
			var item;

			_private.items.basket.all.length = 0;

			try {
				// find ERP codes in _private.data
				for (var i=0; i<_private.items.basket.codes.length; i++) {
					found = false;

					for (var j=0; j<_private.data.items.length && !found; j++) {
						if (_private.items.basket.codes[i] == _private.data.items[j].code) {
							item = {
								data: _private.data.items[j]
							}
							item.code = item.data.code;
							item.latin = item.data._5;
							item.name = item.data._6;
							item.organic = item.data._19 ? true : false;
							item.pharmacy = item.data._7 ? true : false;
							found = true;
						}
					}
					if (found) {
						_private.items.basket.all[_private.items.basket.all.length] = item;
					}
				}
				// don't filter items in the basket
				_private.items.basket.filtered = _private.items.basket.all;
			}
			catch (e) {
				DEBUG('Error: could not generate array of favorites (' + e.message + ')');
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * generate _private.items.favorites.all
		 *
		 * prerequisities:
		 *   _private.items.favorites.codes must have been initialized
		 */
		generateFavorites: function() {
			var found;
			var item;

			try {
				// find ERP codes in _private.data
				for (var i=0; i<_private.items.favorites.codes.length; i++) {
					found = false;

					for (var j=0; j<_private.data.items.length && !found; j++) {
						if (_private.items.favorites.codes[i] == _private.data.items[j].code) {
							item = _private.generateItem(_private.data.items[j]);
							found = true;
						}
					}
					if (!found) {
						item = _private.generateItemRemovedFromShop(_private.items.favorites.codes[i]);
					}
					_private.items.favorites.all[i] = item;
				}
			}
			catch (e) {
				DEBUG('Error: could not generate array of favorites (' + e.message + ')');
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * generate DOM for a single shop item
		 *
		 * params:
		 *   data:     an object containing a set of data for the shop item
		 *   synonyms: (optional) list of synonyms to be added to item name
		 * return value:
		 *   an object containing German/English name, Latin name, and an HTML object of the item
		 */
		generateItem: function(data, synonyms) {
			/*
			 * {
			 *   name: (German/English name),
			 *   latin: (Latin name),
			 *   organic: (boolean),
			 *   code: (string),
			 *   row: (HTML object)
			 * }
			 *
			 * <tr class="trItem">
			 *   <td class="tdCode">(Art.-Nr.)</td>
			 *   <td class="tdName"></td> (This value will be filled in later depending on filters.)
			 *   <td class="tdMono">(Monografie)</td>
			 *   <td class="tdOrganic">(kbA)</td>
			 *   <td class="tdInfo">(Info)</td>
			 *   <td class="tdAction">(Aktion)</td>
			 *   <td class="tdPrice">(&euro;/kg ab 5kg)</t>
			 * </tr>
			*/
			var strings = {
				code: '',
				name: '',
				latin: '',
				mono: '',
				organic: '',
				info: '',
				icons: {
					favorite: (BB.getLang() == 'de') ? '<span title="zu meinen Favoriten hinzuf\u00FCgen" class="iconFav"></span>' : '<span title="add to my favourites" class="iconFav"></span>',
					info: '<span class="iconInfo noIcon"></span>',
					availability: '<span title="?" class="iconAvail noIcon"></span>'
				},
				price: ''
			};
			var result = {
				data: data,
				name: '',
				latin: '',
				organic: false,
				pharmacy: false,
				row: null
			};
			var row;
			var cell;

			// set up string data
			if (typeof data.code != 'undefined') {
				strings.code = data.code;
			}
			if (typeof data._6 != 'undefined') {
				strings.name = data._6;

				if (synonyms && synonyms.length > 0) {
					strings.name += ' <span title="' + (BB.getLang() == 'de' ? 'Treffer bei Synonymsuche' : 'Result(s) on synonymous search') + '" class="colorSetFG">[';

					for (var i=0; i<synonyms.length; i++) {
						strings.name += synonyms[i] + ((i < synonyms.length - 1) ? ", " : "");
					}
					strings.name += ']</span>';
				}
			}
			if (typeof data._5 != 'undefined') {
				strings.latin = data._5;

				if (synonyms && synonyms.length > 0) {
					strings.latin += ' <span title="' + (BB.getLang() == 'de' ? 'Treffer bei Synonymsuche' : 'Result(s) on synonymous search') + '" class="colorSetFG">[';

					for (var i=0; i<synonyms.length; i++) {
						strings.latin += synonyms[i] + ((i < synonyms.length - 1) ? ", " : "");
					}
					strings.latin += ']</span>';
				}
			}
			if (typeof data._33 != 'undefined') {
				strings.mono = _private.getMonographString(data._33);
			}
			if (typeof data._19 != 'undefined') {
				if (data._19 == 0) {
					strings.organic = '';
				}
				else {
					strings.organic = (BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>';
					strings.icons.info = '<span title="Info" class="iconInfo"></span>';
					result.organic = true;
				}
			}
			if (typeof data._7 != 'undefined') {
				if (data._7) {
					strings.info += '<span class="infoA" title="' + ((BB.getLang() == 'de') ? 'apothekenpflichtig' : 'pharmacy-only') + '">a</span>';
					strings.icons.info = '<span title="Info" class="iconInfo"></span>';
					result.pharmacy = true;
				}
				else {
					strings.info += '<span class="infoA"></span>';
				}
			}
			else {
				strings.info += '<span class="infoA"></span>';
			}
			if (typeof data.VAT != 'undefined') {
				if (data.VAT == 7.0) {
					strings.info += '<span class="infoM" title="' + ((BB.getLang() == 'de') ? 'reduzierter Mehrwertsteuersatz' : 'reduced VAT rate') + '">m</span>';
				}
				else {
					strings.info += '<span class="infoM"></span>';
				}
			}
			else {
				strings.info += '<span class="infoM"></span>';
			}
			if (typeof data._17 != 'undefined') {
				if (data.price >= 0) {
					switch (data._17) {
						case 0:
							strings.icons.availability = '<span title="' + ((BB.getLang() == 'de') ? 'sofort verf\u00FCgbar' : 'available immediately') + '" class="iconAvail iconAvail0"></span>';
							break;
						case 1:
							strings.icons.availability = '<span title="' + ((BB.getLang() == 'de') ? 'begrenzt verf\u00FCgbar' : 'limited availability') + '" class="iconAvail iconAvail1"></span>';
							break;
						case 2:
							strings.icons.availability = '<span title="' + ((BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request') + '" class="iconAvail iconAvail2"></span>';
							break;
					}
				}
				else {
					strings.icons.availability = '<span title="' + ((BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request') + '" class="iconAvail iconAvail0"></span>'
				}
			}
			if (data.price) {
				strings.price = _private.priceToString(data.price);
			}
			else {
				strings.price = (BB.getLang() == 'de') ? 'a.A.' : 'on request';

				if (BB.getLang() == 'de') {
					strings.priceLegend = 'auf Anfrage';
				}
			}

			// create table row containing item data
			row = document.createElement('tr');
			row.className = 'trItem colorSetFGHover';

			cell = document.createElement('td');
			cell.className = 'tdCode';
			cell.innerHTML = '<span class="showDetails">' + strings.code + '</span>';
			row.appendChild(cell);

			cell = document.createElement('td');
			cell.className = 'tdName';
			//cell.innerHTML = strings.name;
			row.appendChild(cell);

			cell = document.createElement('td');
			cell.className = 'tdMono';
			cell.innerHTML = strings.mono;
			row.appendChild(cell);

			cell = document.createElement('td');
			cell.className = 'tdOrganic';
			cell.innerHTML = strings.organic;
			row.appendChild(cell);

			cell = document.createElement('td');
			cell.className = 'tdInfo';
			cell.innerHTML = strings.info;
			row.appendChild(cell);

			cell = document.createElement('td');
			cell.className = 'tdAction';
			cell.innerHTML = strings.icons.favorite + strings.icons.info + strings.icons.availability;
			row.appendChild(cell);

			cell = document.createElement('td');
			cell.className = 'tdPrice';
			cell.innerHTML = strings.price;

			if (strings.priceLegend) {
				cell.setAttribute("title", strings.priceLegend);
			}
			row.appendChild(cell);

			// save data to result object (result.organic has been set before)
			result.name = strings.name;
			result.latin = strings.latin;
			result.code = strings.code;
			result.row = row;

			return result;
		},
		/*-----------------------------------------------------------------------------------------
		 * generate detail view of a single item
		 * 
		 * params:
		 *   data: an object containing information on the item to be generated
		 *   pos: (optional) the position of the item inside _private.items.current.filtered
		 */
		generateItemDetail: function(data, pos) {
			var isBulk;
			var isLiquid;
			var callbackBundles = {
				success: function(o) {
					try {
						delete _private.currentItem;
						_private.currentItem = {
							data: data,
							bundles: YAHOO.lang.JSON.parse(o.responseText).bundles
						};
					}
					catch (e) {
						DEBUG('failed to parse JSON file containing bundle data: ' + e.message);
						yuiLoader.hide();
						return;
					}
					switch (_private.displayMode) {
						case 'shop':
						case 'search':
						case 'favorites':
						case 'order':
							if (data.price <= 0.0) {
								YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailRequest.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
							}
							else if (isBulk) {
								if (isLiquid) {
									YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailLiquid.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
								}
								else {
									YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailBulk.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
								}
							}
							else {
								YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailBundle.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
							}
							break;
						case 'basket':
						case 'basketFast':
							if (isBulk) {
								if (isLiquid) {
									YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailLiquidBasket.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
								}
								else {
									YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailBulkBasket.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
								}
							}
							else {
								YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailBundleBasket.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
							}
							break;
						default:
							DEBUG('warning: illegal value \'' + _private.displayMode + '\' of Shop.displayMode');
					}
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting JSON file containing bundle data timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/itemBundles.json?code=' + _private.trim(data.code) + '&xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBundles);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};
			var callbackDetail = {
				success: function(o) {
					var itemNo = -1;

					_public.toggleFilters(false);
					Content.replaceContent(o.responseText);
					Forms.initOverLabels();

					// quit, if access denied page has been loaded or if there is no ERP code
					if (!document.getElementById('containerItemDetail') || !data.code) {
						return;
					}
					if (   typeof pos == 'number' && BB.isInteger(pos)
					    && pos >= 0 && pos < _private.items.current.filtered.length) {
						itemNo = pos;
					}
					else {
						for (var i=0; i<_private.items.current.filtered.length; i++) {
							if (data.code == _private.items.current.filtered[i].code) {
								itemNo = i;
								break;
							}
						}
					}
					if (itemNo < 0) {
						return;
					}
					if (data.price <= 0.0) {
						_private.generateItemDetailRequest(data, itemNo);
					}
					else if (isBulk) {
						if (isLiquid) {
							_private.generateItemDetailLiquid(data, itemNo);
						}
						else {
							_private.generateItemDetailBulk(data, itemNo);
						}
					}
					else {
						_private.generateItemDetailBundle(data, itemNo);
					}

					// Paging
					var back = YAHOO.util.Dom.getElementsByClassName('aPagingBack', 'a', 'bd');
					var prev = YAHOO.util.Dom.getElementsByClassName('aPagingPrev', 'a', 'bd');
					var next = YAHOO.util.Dom.getElementsByClassName('aPagingNext', 'a', 'bd');

					YAHOO.util.Event.removeListener(back, 'click');
					YAHOO.util.Event.addListener(back, 'click', function() {
						switch (_private.displayMode) {
							case 'shop':
								Content.loadShop(_private.nodeId);
								break;
							case 'search':
								Content.loadSearchResults();
								break;
							case 'favorites':
								Content.loadFavorites();
								break;
							case 'basket':
								Content.loadBasket();
								break;
							case 'basketFast':
								Content.loadBasketFast();
								break;
							case 'order':
								Content.loadOrders();
								break;
						}
						_public.toggleFilters(true);
					});
					YAHOO.util.Event.removeListener(prev, 'click');
					YAHOO.util.Event.addListener(prev, 'click', function() {
						if (itemNo > 0) {
							_private.generateItemDetail(_private.items.current.filtered[itemNo-1].data, itemNo - 1);
						}
					});
					YAHOO.util.Event.removeListener(next, 'click');
					YAHOO.util.Event.addListener(next, 'click', function() {
						if (itemNo < _private.items.current.filtered.length - 1) {
							_private.generateItemDetail(_private.items.current.filtered[itemNo+1].data, itemNo + 1);
						}
					});

					// additional event listeners
					var addBundle = document.getElementById('aItemAddBundle');
/*
					var showFees = document.getElementById('aItemShowFees');
*/
					var request = document.getElementById('aItemRequest');
					var specialConditions = document.getElementById('aItemSpecialConditions');
					var buyFurtherItems = document.getElementById('aItemBuyFurtherItems');
					var goToBasket = document.getElementById('aItemGoToBasket');

					if (addBundle) {
						if (isBulk) {
							if (isLiquid) {
								var add = function() {
									addBundle.parentNode.removeChild(addBundle);
									_private.getItemDetailRowLiquid(data, itemNo);
								};
								var keyListenerAdd = new YAHOO.util.KeyListener(addBundle, {
									keys: [0x0a, 0x0d]
								}, {
									fn: add
								});

								YAHOO.util.Event.addListener(addBundle, 'click', add);
								keyListenerAdd.enable();
							}
							else {
								var add = function() {
									addBundle.parentNode.removeChild(addBundle);
									_private.getItemDetailRowBulk(data, itemNo);
								};
								var keyListenerAdd = new YAHOO.util.KeyListener(addBundle, {
									keys: [0x0a, 0x0d]
								}, {
									fn: add
								});

								YAHOO.util.Event.addListener(addBundle, 'click', add);
								keyListenerAdd.enable();
							}
						}
					}
/*
					if (showFees) {
						YAHOO.util.Event.addListener(showFees, 'click', function() {
							_private.panels.fees.show();
						});
					}
*/
					if (request) {
						var callbackRequest = {
							success: function(o) {
								var name;

								if (_private.filters.latin) {
									name = _private.trim(data._5);
								}
								else {
									name = _private.trim(data._6);
								}
								document.getElementById('boxFormRequest').innerHTML = o.responseText;
								document.getElementById('spanItem').innerHTML = data.code + ' ' + name;
								document.getElementById('hiddenItem').value = data.code + ' ' + name;
								YAHOO.util.Event.addListener('aRequestSubmit', 'click', function() {
									document.forms['Galke_PreisAnfrage'].elements['command-Galke_PreisAnfrage-sendmail'].value='x';
									document.forms['Galke_PreisAnfrage'].submit();
								});
								YAHOO.util.Event.addListener('aRequestPanelClose', 'click', function() {
									_private.panels.request.hide();
								});
								_private.panels.request.show();
								Forms.initOverLabels();
								yuiLoader.hide();
							},
							failure: function(o) {
								if (o.status = -1) {
									DEBUG('getting price request form timed out - retrying');
									YAHOO.util.Connect.asyncRequest('POST', '/_includes/' + BB.getLangPath() + 'shopPriceRequest.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackRequest);
								}
								else {
									if (BB.getLang() == 'de') {
										document.getElementById('boxFormRequest').innerHTML
										 =   'Leider ist ein Fehler aufgetreten. Bitte wenden Sie '
										   + 'sich mit Ihrer Anfrage per E-Mail an '
										   + '<a href="mailto:info@galke.com">info@galke.com</a>.'
										   + '<br>'
										   + 'Gerne k\u00F6nnen Sie auch telefonisch unter '
										   + '+49.\u00A05537.\u00A08681.\u00A00 oder per Fax an '
										   + 'die Nummer +49.\u00A05537.\u00A08681.\u00A020 '
										   + 'Kontakt zu uns aufnehmen';
									}
									else {
										document.getElementById('boxFormRequest').innerHTML
										 =   'Unfortunately an error has occured. Please refer to '
										   + '<a href="mailto:info@galke.com">info@galke.com</a> per '
										   + 'e-mail with your request.'
										   + '<br>'
										   + 'Please feel free to contact us by telephone as well at '
										   + '+49.\u00A05537.\u00A08681.\u00A00 or per fax at '
										   + '+49.\u00A05537.\u00A08681.\u00A020.';
									}
									_private.panels.request.show();
									yuiLoader.hide();
								}
							},
							timeout: 30000
						};
						var showRequest = function() {
							yuiLoader.show();
							YAHOO.util.Connect.asyncRequest('POST', '/_includes/' + BB.getLangPath() + 'shopPriceRequest.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackRequest);
						};
						var keyListenerRequest = new YAHOO.util.KeyListener(request, {
							keys: [0x0a, 0x0d]
						}, {
							fn: showRequest
						});

						YAHOO.util.Event.addListener(request, 'click', showRequest);
						keyListenerRequest.enable();
					}
					if (specialConditions) {
						var callbackSpecialConditions = {
							success: function(o) {
								var name;

								if (_private.filters.latin) {
									name = _private.trim(data._5);
								}
								else {
									name = _private.trim(data._6);
								}
								document.getElementById('boxFormSpecialConditions').innerHTML = o.responseText;
								document.getElementById('spanItem').innerHTML = data.code + ' ' + name;
								document.getElementById('hiddenItem').value = data.code + ' ' + name;
								YAHOO.util.Event.addListener('aSpecialConditionsSubmit', 'click', function() {
									document.forms['Galke_Sonderkonditionen'].elements['command-Galke_Sonderkonditionen-sendmail'].value='x';
									document.forms['Galke_Sonderkonditionen'].submit();
								});
								YAHOO.util.Event.addListener('aSpecialConditionsPanelClose', 'click', function() {
									_private.panels.conditions.hide();
								});
								_private.panels.conditions.show();
								Forms.initOverLabels();
								yuiLoader.hide();
							},
							failure: function(o) {
								if (o.status = -1) {
									DEBUG('getting special conditions form timed out - retrying');
									YAHOO.util.Connect.asyncRequest('POST', '/_includes/' + BB.getLangPath() + 'specialConditions.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackSpecialConditions);
								}
								else {
									if (BB.getLang() == 'de') {
										document.getElementById('boxFormRequest').innerHTML
										 =   'Leider ist ein Fehler aufgetreten. Bitte wenden Sie '
										   + 'sich mit Ihrer Anfrage per E-Mail an '
										   + '<a href="mailto:info@galke.com">info@galke.com</a>.'
										   + '<br>'
										   + 'Gerne k\u00F6nnen Sie auch telefonisch unter '
										   + '+49.\u00A05537.\u00A08681.\u00A00 oder per Fax an '
										   + 'die Nummer +49.\u00A05537.\u00A08681.\u00A020 '
										   + 'Kontakt zu uns aufnehmen';
									}
									else {
										document.getElementById('boxFormRequest').innerHTML
										 =   'Unfortunately an error has occured. Please refer to '
										   + '<a href="mailto:info@galke.com">info@galke.com</a> per '
										   + 'e-mail with your request.'
										   + '<br>'
										   + 'Please feel free to contact us by telephone as well at '
										   + '+49.\u00A05537.\u00A08681.\u00A00 or per fax at '
										   + '+49.\u00A05537.\u00A08681.\u00A020.';
									}
									_private.panels.conditions.show();
									yuiLoader.hide();
								}
							},
							timeout: 30000
						};
						var showSpecialConditions = function() {
							yuiLoader.show();
							YAHOO.util.Connect.asyncRequest('POST', '/_includes/' + BB.getLangPath() + 'specialConditions.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackSpecialConditions);
						};
						var keyListenerSpecialConditions = new YAHOO.util.KeyListener(specialConditions, {
							keys: [0x0a, 0x0d]
						}, {
							fn: showSpecialConditions
						});

						YAHOO.util.Event.addListener(specialConditions, 'click', showSpecialConditions);
						keyListenerSpecialConditions.enable();
					}
					if (buyFurtherItems) {
						var buyMore = function() {
							var isShop;
							var shopURL;

							// check if the current URL belongs to the shop
							if (BB.getLang() == 'de') {
								if (location.pathname.match(/^\/sortiment_shop$/)) {
									isShop = true;
								}
								else {
									isShop = false;
									shopURL = '/sortiment_shop';
								}
							}
							else {
								if (location.pathname.match(/^\/products_shop$/)) {
									isShop = true;
								}
								else {
									isShop = false;
									shopURL = '/products_shop';
								}
							}
							// determine next page to be shown
							switch (_private.displayMode) {
								case 'shop':
									Content.loadShop(_private.nodeId);
									break;
								case 'search':
									Content.loadSearchResults();
									break;
								case 'favorites':
									Content.loadFavorites();
									break;
								case 'basket':
								case 'basketFast':
								case 'order':
									if (isShop) {
										Shop.init();
									}
									else {
										window.location.replace(shopURL);
									}
									break;
								default:
									DEBUG('warning: illegal value \'' + _private.displayMode + '\' of Shop.displayMode');
							}
							_public.toggleFilters(true);
						};
						var keyListenerBuyMore = new YAHOO.util.KeyListener(buyFurtherItems, {
							keys: [0x0a, 0x0d]
						}, {
							fn: buyMore
						});

						YAHOO.util.Event.addListener(buyFurtherItems, 'click', buyMore);
						keyListenerBuyMore.enable();
					}
					if (goToBasket) {
						var keyListener = new YAHOO.util.KeyListener(goToBasket, {
							keys: [0x0a, 0x0d]
						}, {
							fn: Content.loadBasket
						});

						YAHOO.util.Event.addListener(goToBasket, 'click', Content.loadBasket);
						keyListener.enable();
					}
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting item detail template timed out - retrying');
						
						switch (_private.displayMode) {
							case 'shop':
							case 'search':
							case 'favorites':
							case 'order':
								if (data.price <= 0.0) {
									YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailRequest.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
								}
								else if (isBulk) {
									if (isLiquid) {
										YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailLiquid.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
									}
									else {
										YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailBulk.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
									}
								}
								else {
									YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailBundle.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
								}
								break;
							case 'basket':
							case 'basketFast':
								if (isBulk) {
									if (isLiquid) {
										YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailLiquidBasket.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
									}
									else {
										YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailBulkBasket.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
									}
								}
								else {
									YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailBundleBasket.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackDetail);
								}
								break;
						}
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			}

			// abort if there is no (valid) unit name
			try {
				switch (data._9.toLowerCase()) {
					case 'dos':
					case 'kar':
					case 'pck':
					case 'st':
					case 't\u00FCt':
						isBulk = false;
						isLiquid = false;
						break;
					case 'kg':
						isBulk = true;
						isLiquid = false;
						break;
					case 'ltr':
						isBulk = true;
						isLiquid = true;
						break;
					default:
						return;
				}
			}
			catch (e) {
				return;
			}
			yuiLoader.show();
			YAHOO.util.Connect.asyncRequest('POST', '/_json/itemBundles.json?code=' + _private.trim(data.code) + '&xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBundles);
		},
		/*-----------------------------------------------------------------------------------------
		 * generate detail output for bulk wares
		 *
		 * params:
		 *   data: an object containing the data required
		 *   pos: position of the item inside _private.items.current.filtered
		 * prerequisities:
		 *   the item must be sold as bulk ware, i.e. data._9 must be 'kg'
		 */
		generateItemDetailBulk: function(data, pos) {
			var units = YAHOO.util.Dom.getElementsByClassName('spanUnit', 'span', 'containerItemDetail');

			try {
				// insert unit name
				for (var i=0; i<units.length; i++) {
					units[i].innerHTML = 'kg';
				}
			}
			catch (e) {
				DEBUG('could not determine unit name for current item: ' + e.message);
				return;
			}

			if (!_private.filters) {
				_private.configureItemFilters();
			}
			// Artikelnummer
			document.getElementById('JSON_CODE').innerHTML = _private.trim(data.code);

			// _6 -> Artikelbezeichnung, _5 -> lateinische Bezeichnung
			if (data._6 && !_private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._6);
			}
			if (data._5 && _private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._5);
			}
			// Monografie, _19 -> kbA
			if (data._33 && data._19) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33) + ', ' + ((BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>');
			}
			else if (data._33) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33);
			}
			else if (data._19) {
				document.getElementById('JSON_MONO').innerHTML = (BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>';
			}
			// Info: _7 -> apthokenpflichtig, VAT
			if (data._7) {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA" title="' + ((BB.getLang() == 'de') ? 'apothekenpflichtig' : 'pharmacy-only') + '">a</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA"></span>';
			}
			if (data.VAT == 7.0) {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM" title="' + ((BB.getLang() == 'de') ? 'reduzierter Mehrwertsteuersatz' : 'reduced VAT rate') + '">m</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM"></span>';
			}
			// _17 -> Verfügbarkeit
			if (typeof data._17 != 'undefined') {
				var availBox = document.getElementById('JSON_AVAIL');

				switch (data._17) {
					case 0:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'sofort verf\u00FCgbar' : 'available immediately');
						break;
					case 1:
						availBox.className = 'iconAvail iconAvail1';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'begrenzt verf\u00FCgbar' : 'limited availability');
						break;
					case 2:
						availBox.className = 'iconAvail iconAvail2';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
						break;
					default:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
				}
			}
			// Favoritenstatus
			var obj = new Object();
			var iconFav = document.getElementById('JSON_FAV');

			if (_private.isFavorite(data.code)) {
				iconFav.className = 'iconFav iconFavOn';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'aus meinen Favoriten entfernen' : 'remove from my favourites');
			}
			else {
				iconFav.className = 'iconFav iconFavOff';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'zu meinen Favoriten hinzuf\u00FCgen' : 'add to my favourites');
			}
			obj.code = data.code;
			obj.icon = iconFav;
			YAHOO.util.Event.addListener(iconFav, 'click', _private.toggleFavorite, obj, true);
			delete obj;

			// Preisangaben
			if (typeof data.price == 'number') {
				document.getElementById('JSON_PRICE_100').innerHTML = _private.priceToString(_private.roundPrice(data.price * 0.10 + 2.10));
				document.getElementById('JSON_PRICE_250').innerHTML = _private.priceToString(_private.roundPrice(data.price * 0.25 + 2.10));
				document.getElementById('JSON_PRICE_500').innerHTML = _private.priceToString(_private.roundPrice(data.price * 0.50 + 2.10));
				document.getElementById('JSON_PRICE_1').innerHTML = _private.priceToString(_private.roundPrice(data.price + 1.50));
				document.getElementById('JSON_PRICE_5').innerHTML = _private.priceToString(_private.roundPrice(data.price));
			}
			// Zeilen für bestehende Gebinde einfügen bzw. leere Gebindezeile einfügen, falls kein
			// Gebinde im Warenkorb
			if (_private.currentItem.bundles.length > 0) {
				for (var i=0; i<_private.currentItem.bundles.length; i++) {
					_private.getItemDetailRowBulk(data, pos, _private.currentItem.bundles[i].count, _private.currentItem.bundles[i].weight);
				}
			}
			else {
				var addBundle = document.getElementById('aItemAddBundle');

				if (addBundle) {
					addBundle.parentNode.removeChild(addBundle);
				}
				_private.getItemDetailRowBulk(data, pos);
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * generate detail output for items packed in small bundles
		 *
		 * params:
		 *   data: an object containing the data required
		 *   pos: position of the item inside _private.items.current.filtered
		 * prerequisities:
		 *   the item must be sold in bundles, i.e. data._9 must be in {'Dos', 'Kar', 'Ltr', 'Pck',
		 *   'ST', 'T\u00FCt'} 
		 */
		generateItemDetailBundle: function(data, pos) {
			var units = YAHOO.util.Dom.getElementsByClassName('spanUnit', 'span', 'containerItemDetail');
			var min;
			var multiple = false;

			// set minimum amount and unit --------------------------------------------------------
			try {
				// insert unit name
				for (var i=0; i<units.length; i++) {
					switch (data._9.toLowerCase()) {
						case 'dos':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Dos.' : 'tins';
							break;
						case 'kar':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Kar.' : 'boxes';
							break;
						case 'pck':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Pck.' : 'pcks.';
							break;
						case 'st':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'St.' : 'pcs.';
							break;
						case 't\u00FCt':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'T\u00FCt.' : 'bags';
							break;
					}
				}
			}
			catch (e) {
				DEBUG('could not determine unit name for current item: ' + e.message);
				return;
			}
			if (_private.trim(data.code).match(/10$/)) {
				document.getElementById('spanMinCount').innerHTML = '10';
				min = 10;
			}
			else if (_private.trim(data.code).match(/44$/) || _private.trim(data.code).match(/55$/) || _private.trim(data.code).match(/54$/)) {
				document.getElementById('spanMinCount').innerHTML = '6';
				min = 6;
				multiple = true;

				try {
					document.getElementById('boxBiogaHint').style.display = "block";
				}
				catch (e) {}
			}
			else {
				document.getElementById('spanMinCount').innerHTML = '1';
				min = 1;
			}
			// insert JSON data -------------------------------------------------------------------
			if (!_private.filters) {
				_private.configureItemFilters();
			}
			// Artikelnummer
			document.getElementById('JSON_CODE').innerHTML = _private.trim(data.code);

			// _6 -> Artikelbezeichnung, _5 -> lateinische Bezeichnung
			if (data._6 && !_private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._6);
			}
			if (data._5 && _private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._5);
			}
			// Monografie, _19 -> kbA
			if (data._33 && data._19) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33) + ', ' + ((BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>');
			}
			else if (data._33) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33);
			}
			else if (data._19) {
				document.getElementById('JSON_MONO').innerHTML = (BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>';
			}
			// Info: _7 -> apthokenpflichtig, VAT
			if (data._7) {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA" title="' + ((BB.getLang() == 'de') ? 'apothekenpflichtig' : 'pharmacy-only') + '">a</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA"></span>';
			}
			if (data.VAT == 7.0) {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM" title="' + ((BB.getLang() == 'de') ? 'reduzierter Mehrwertsteuersatz' : 'reduced VAT rate') + '">m</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM"></span>';
			}
			// _17 -> Verfügbarkeit
			if (typeof data._17 != 'undefined') {
				var availBox = document.getElementById('JSON_AVAIL');

				switch (data._17) {
					case 0:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'sofort verf\u00FCgbar' : 'available immediately');
						break;
					case 1:
						availBox.className = 'iconAvail iconAvail1';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'begrenzt verf\u00FCgbar' : 'limited availability');
						break;
					case 2:
						availBox.className = 'iconAvail iconAvail2';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
						break;
					default:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
				}
			}
			// Favoritenstatus
			var obj = new Object();
			var iconFav = document.getElementById('JSON_FAV');

			if (_private.isFavorite(data.code)) {
				iconFav.className = 'iconFav iconFavOn';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'aus meinen Favoriten entfernen' : 'remove from my favourites');
			}
			else {
				iconFav.className = 'iconFav iconFavOff';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'zu meinen Favoriten hinzuf\u00FCgen' : 'add to my favourites');
			}
			obj.code = data.code;
			obj.icon = iconFav;
			YAHOO.util.Event.addListener(iconFav, 'click', _private.toggleFavorite, obj, true);
			delete obj;

			// Preisangaben
			if (typeof data.price == 'number') {
				document.getElementById('JSON_PRICE').innerHTML = _private.priceToString(data.price);
			}

			// remove HTML objects not required from DOM
			if (data._7 && !_private.fullAccess) {
				// pharmacy only item, no privileges
				var iconBasket = document.getElementById('iconBasket');
				var iconDelete = document.getElementById('iconDelete');

				iconBasket.parentNode.removeChild(iconBasket);
				iconDelete.parentNode.removeChild(iconDelete);
			}
			else {
				var iconQuestion = document.getElementById('iconQuestion');

				iconQuestion.parentNode.removeChild(iconQuestion);
			}

			// insert initial count/add event listeners
			var inputCount = document.getElementById('inputCount');
			var inputCountOld = document.getElementById('inputCountOld');
			var iconBasket = document.getElementById('iconBasket');
			var iconDelete = document.getElementById('iconDelete');
			var boxPriceTotal = YAHOO.util.Dom.getElementBy(function(elem) {
				return YAHOO.util.Dom.hasClass(elem, 'boxPriceTotal');
			}, 'div', 'containerContent');
			var obj = {
				code: data.code,
				bulk: false,
				inputCount: inputCount,
				inputCountOld: inputCountOld,
				boxPriceTotal: boxPriceTotal,
				min: min,
				multiple: multiple,
				pos: pos
			};

			if (_private.currentItem.bundles.length > 0) {
				inputCount.value = _public.checkInputCount(_private.currentItem.bundles[0].count, '', min, multiple);
				inputCountOld.value = inputCount.value;
				boxPriceTotal.innerHTML = _private.priceToString(inputCount.value ? (parseInt(inputCount.value) * data.price) : 0.00);

				// over labels have been applied before, so toggle label display manually
				if (inputCount.value != '') {
					Forms.toggleLabelDisplay('inputCount', true);
				}
			}
			YAHOO.util.Event.addListener(inputCount, 'change', function() {
				this.inputCount.value = _public.checkInputCount(this.inputCount.value, this.inputCountOld.value, this.min, this.multiple);
				this.inputCountOld.value = this.inputCount.value;
				this.boxPriceTotal.innerHTML = _private.priceToString(this.inputCount.value ? (parseInt(this.inputCount.value) * data.price) : 0.00);
			}, obj, true);
			YAHOO.util.Event.addListener(iconBasket, 'click', _private.addToBasket, obj, true);
			YAHOO.util.Event.addListener(iconDelete, 'click', _private.deleteFromBasket, obj, true);

			// add key listeners (or don't if the item is marked "pharmacy only" and the user is
			// not entitled to order such items)
			if (!data._7 || _private.fullAccess) {
				var keyListenerCount = new YAHOO.util.KeyListener(inputCount, {
					keys: [0x0a, 0x0d]
				}, {
					fn: _private.addToBasket,
					scope: obj,
					correctScope: true
				});
				var keyListenerBasket = new YAHOO.util.KeyListener(iconBasket, {
					keys: [0x0a, 0x0d]
				}, {
					fn: _private.addToBasket,
					scope: obj,
					correctScope: true
				});
				var keyListenerDelete = new YAHOO.util.KeyListener(iconDelete, {
					keys: [0x0a, 0x0d]
				}, {
					fn: _private.deleteFromBasket,
					scope: obj,
					correctScope: true
				});
				
				keyListenerCount.enable();
				keyListenerBasket.enable();
				keyListenerDelete.enable();
			}
			delete obj;

			inputCount.focus();
		},
		/*-----------------------------------------------------------------------------------------
		 * generate detail output for liquid wares
		 *
		 * params:
		 *   data: an object containing the data required
		 *   pos: position of the item inside _private.items.current.filtered
		 * prerequisities:
		 *   the item must be liquid ware, i.e. data._9 must be 'ltr'
		 */
		generateItemDetailLiquid: function(data, pos) {
			var units = YAHOO.util.Dom.getElementsByClassName('spanUnit', 'span', 'containerItemDetail');

			try {
				// insert unit name
				for (var i=0; i<units.length; i++) {
					units[i].innerHTML = (BB.getLang() == 'de') ? 'Ltr.' : 'l';
				}
			}
			catch (e) {
				DEBUG('could not determine unit name for current item: ' + e.message);
				return;
			}

			if (!_private.filters) {
				_private.configureItemFilters();
			}
			// Artikelnummer
			document.getElementById('JSON_CODE').innerHTML = _private.trim(data.code);

			// _6 -> Artikelbezeichnung, _5 -> lateinische Bezeichnung
			if (data._6 && !_private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._6);
			}
			if (data._5 && _private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._5);
			}
			// Monografie, _19 -> kbA
			if (data._33 && data._19) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33) + ', ' + ((BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>');
			}
			else if (data._33) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33);
			}
			else if (data._19) {
				document.getElementById('JSON_MONO').innerHTML = (BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>';
			}
			// Info: _7 -> apthokenpflichtig, VAT
			if (data._7) {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA" title="' + ((BB.getLang() == 'de') ? 'apothekenpflichtig' : 'pharmacy-only') + '">a</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA"></span>';
			}
			if (data.VAT == 7.0) {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM" title="' + ((BB.getLang() == 'de') ? 'reduzierter Mehrwertsteuersatz' : 'reduced VAT rate') + '">m</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM"></span>';
			}
			// _17 -> Verfügbarkeit
			if (typeof data._17 != 'undefined') {
				var availBox = document.getElementById('JSON_AVAIL');

				switch (data._17) {
					case 0:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'sofort verf\u00FCgbar' : 'available immediately');
						break;
					case 1:
						availBox.className = 'iconAvail iconAvail1';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'begrenzt verf\u00FCgbar' : 'limited availability');
						break;
					case 2:
						availBox.className = 'iconAvail iconAvail2';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
						break;
					default:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
				}
			}
			// Favoritenstatus
			var obj = new Object();
			var iconFav = document.getElementById('JSON_FAV');

			if (_private.isFavorite(data.code)) {
				iconFav.className = 'iconFav iconFavOn';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'aus meinen Favoriten entfernen' : 'remove from my favourites');
			}
			else {
				iconFav.className = 'iconFav iconFavOff';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'zu meinen Favoriten hinzuf\u00FCgen' : 'add to my favourites');
			}
			obj.code = data.code;
			obj.icon = iconFav;
			YAHOO.util.Event.addListener(iconFav, 'click', _private.toggleFavorite, obj, true);
			delete obj;

			// Preisangaben
			if (typeof data.price == 'number') {
				document.getElementById('JSON_PRICE').innerHTML = _private.priceToString(data.price);
			}
			// Zeilen für bestehende Gebinde einfügen bzw. leere Gebindezeile einfügen, falls kein
			// Gebinde im Warenkorb
			if (_private.currentItem.bundles.length > 0) {
				for (var i=0; i<_private.currentItem.bundles.length; i++) {
					_private.getItemDetailRowLiquid(data, pos, _private.currentItem.bundles[i].count, _private.currentItem.bundles[i].weight);
				}
			}
			else {
				var addBundle = document.getElementById('aItemAddBundle');

				if (addBundle) {
					addBundle.parentNode.removeChild(addBundle);
				}
				_private.getItemDetailRowLiquid(data, pos);
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * generate detail output for bulk wares
		 *
		 * params:
		 *   data: an object containing the data required
		 *   pos: position of the item inside _private.items.current.filtered
		 * prerequisities:
		 *   the item must be sold as bulk ware, i.e. data._9 must be 'kg'
		 */
		generateItemDetailRequest: function(data, pos) {
			var units = YAHOO.util.Dom.getElementsByClassName('spanUnit', 'span', 'containerItemDetail');

			try {
				// insert unit name
				for (var i=0; i<units.length; i++) {
					switch (data._9.toLowerCase()) {
						case 'kg':
							units[i].innerHTML = 'kg';
							break;
						case 'ltr':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Ltr.' : 'l';
							break;
						case 'dos':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Dos.' : 'tins';
							break;
						case 'kar':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Kar.' : 'boxes';
							break;
						case 'pck':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'Pck.' : 'pcks.';
							break;
						case 'st':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'St.' : 'pcs.';
							break;
						case 't\u00FCt':
							units[i].innerHTML = (BB.getLang() == 'de') ? 'T\u00FCt.' : 'bags';
							break;
						default:
							DEBUG('could not determine unit name for current item: ' + e.message);
							return;
					}
				}
			}
			catch (e) {
				DEBUG('could not determine unit name for current item: ' + e.message);
				return;
			}

			if (!_private.filters) {
				_private.configureItemFilters();
			}
			// Artikelnummer
			document.getElementById('JSON_CODE').innerHTML = _private.trim(data.code);

			// _6 -> Artikelbezeichnung, _5 -> lateinische Bezeichnung
			if (data._6 && !_private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._6);
			}
			if (data._5 && _private.filters.latin) {
				document.getElementById('JSON_NAME').innerHTML = _private.trim(data._5);
			}
			// Monografie, _19 -> kbA
			if (data._33 && data._19) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33) + ', ' + ((BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>');
			}
			else if (data._33) {
				document.getElementById('JSON_MONO').innerHTML = _private.getMonographString(data._33);
			}
			else if (data._19) {
				document.getElementById('JSON_MONO').innerHTML = (BB.getLang() == 'de') ? '<span title="kontrolliert biologischer Anbau">kbA</span>' : '<span title="certified organic">eco</span>';
			}
			// Info: _7 -> apthokenpflichtig, VAT
			if (data._7) {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA" title="' + ((BB.getLang() == 'de') ? 'apothekenpflichtig' : 'pharmacy-only') + '">a</span>';

				if (!_private.fullAccess) {
					try {
						var request = document.getElementById('aItemRequest');

						request.parentNode.removeChild(request);
					}
					catch (e) {}
				}
			}
			else {
				document.getElementById('JSON_INFO').innerHTML = '<span class="infoA"></span>';
			}
			if (data.VAT == 7.0) {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM" title="' + ((BB.getLang() == 'de') ? 'reduzierter Mehrwertsteuersatz' : 'reduced VAT rate') + '">m</span>';
			}
			else {
				document.getElementById('JSON_INFO').innerHTML += '<span class="infoM"></span>';
			}
			// _17 -> Verfügbarkeit
			if (typeof data._17 != 'undefined') {
				var availBox = document.getElementById('JSON_AVAIL');

				switch (data._17) {
					case 0:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'sofort verf\u00FCgbar' : 'available immediately');
						break;
					case 1:
						availBox.className = 'iconAvail iconAvail1';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'begrenzt verf\u00FCgbar' : 'limited availability');
						break;
					case 2:
						availBox.className = 'iconAvail iconAvail2';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
						break;
					default:
						availBox.className = 'iconAvail iconAvail0';
						availBox.setAttribute('title', (BB.getLang() == 'de') ? 'auf Anfrage verf\u00FCgbar' : 'available upon request');
				}
			}
			// Favoritenstatus
			var obj = new Object();
			var iconFav = document.getElementById('JSON_FAV');

			if (_private.isFavorite(data.code)) {
				iconFav.className = 'iconFav iconFavOn';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'aus meinen Favoriten entfernen' : 'remove from my favourites');
			}
			else {
				iconFav.className = 'iconFav iconFavOff';
				iconFav.setAttribute('title', (BB.getLang() == 'de') ? 'zu meinen Favoriten hinzuf\u00FCgen' : 'add to my favourites');
			}
			obj.code = data.code;
			obj.icon = iconFav;
			YAHOO.util.Event.addListener(iconFav, 'click', _private.toggleFavorite, obj, true);
			delete obj;
		},
		/*-----------------------------------------------------------------------------------------
		 * generate a place-holder item, if one of the user's favorites has been removed from the
		 * shop
		 *
		 * params:
		 *   code: the ERP code of the item removed from the shop
		 */
		generateItemRemovedFromShop: function(code) {
			/*
			 * {
			 *   name: null,
			 *   latin: null,
			 *   organic: null,
			 *   code: (string),
			 *   row: (HTML object)
			 * }
			 *
			 * <tr class="trItem">
			 *   <td class="tdCode">(Art.-Nr.)</td>
			 *   <td class="tdName"></td>
			 *   <td class="tdMono"></td>
			 *   <td class="tdOrganic"></td>
			 *   <td class="tdInfo"></td>
			 *   <td class="tdAction">(Action)</td>
			 *   <td class="tdPrice"></t>
			 * </tr>
			*/
			var icons = {
				favorite: (BB.getLang() == 'de') ? '<span title="aus meinen Favoriten entfernen" class="iconFavRemoved"></span>' : '<span title="remove from my favourites" class="iconFavRemoved"></span>',
				info: '<span title="" class="iconInfo noIcon"></span>',
				availability: '<span title="" class="iconAvail noIcon"></span>'
			};
			var result = {
				code: code,
				name: '',
				latin: '',
				organic: false,
				row: null
			};
			var row;
			var cell;

			// create table row containing item data
			row = document.createElement('tr');
			row.className = 'trItem colorSetFGHover favoriteRemoved colorSetFG';

			cell = document.createElement('td');
			cell.className = 'tdCode';
			cell.innerHTML = code;
			row.appendChild(cell);

			cell = document.createElement('td');
			cell.className = 'tdName';
			//cell.innerHTML = (BB.getLang() == 'de') ? 'Diesen Artikel bieten wir derzeit nicht an.' : 'We currently do not offer this item.';
			row.appendChild(cell);

			cell = document.createElement('td');
			cell.className = 'tdMono';
			row.appendChild(cell);

			cell = document.createElement('td');
			cell.className = 'tdOrganic';
			row.appendChild(cell);

			cell = document.createElement('td');
			cell.className = 'tdInfo';
			row.appendChild(cell);

			cell = document.createElement('td');
			cell.className = 'tdAction';
			cell.innerHTML = icons.favorite + icons.info + icons.availability;
			YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('iconFavRemoved', 'span', cell), 'click', _private.toggleFavorite, {
				code: code
			}, true);
			row.appendChild(cell);

			cell = document.createElement('td');
			cell.className = 'tdPrice';
			row.appendChild(cell);

			// save data to result object
			result.row = row;

			return result;
		},
		/*-----------------------------------------------------------------------------------------
		 * generate shop items linked to selected node
		 *
		 * return value:
		 *   number of items generated, -1 in case of an error
		 */
		generateItems: function() {
			var count = 0;

			// generate items
			_private.items.node.all.length = 0;

			try {
				if (_private.nodeId > 0) {
					// filter items by node ID
					for (var i=0; i<_private.data.items.length; i++) {
						if (   _private.data.items[i].CG
						    && _private.findValueInList(_private.nodeId, _private.data.items[i].CG) >= 0) {
							//_private.displayMode = 'shop';
							_private.items.node.all[count++] = _private.generateItem(_private.data.items[i]);
						}
					}
				}
				else if (_private.nodeId == 0) {
					// display all items
					for (var i=0; i<_private.data.items.length; i++) {
						//_private.displayMode = 'shop';
						_private.items.node.all[count++] = _private.generateItem(_private.data.items[i]);
					}
				}
			}
			catch (e) {
				DEBUG('Error: could not generate items for this node (' + e.message + ')');
			}
			return count;
		},
		/*-----------------------------------------------------------------------------------------
		 * load template file for item detail rows (bulk ware), add IDs and event listeners, and
		 * insert the row into the DOM
		 *
		 * params:
		 *   data: an object containing item data
		 *   pos: position of the item inside _private.items.current.filtered
		 *   count: (optional) a value to be inserted as 'count'
		 *   weight: (optional) a value to be inserted as 'weight'
		 * prerequisities:
		 *   there must be an HTML element having the ID 'formBundles' in the DOM
		 */
		getItemDetailRowBulk: function(data, pos, count, weight) {
			var form = document.getElementById('formBundles');
			var callback = {
				success: function(o) {
					// get row and add ID
					var container = document.createElement('div');
					var rowNo = YAHOO.util.Dom.getElementsByClassName('inputCount', 'input', form).length + 1;

					container.innerHTML = o.responseText;

					var row = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'boxItemBundlesRow');
					}, 'div', container);

					if (!row) {
						delete container;
						yuiLoader.hide();
						return;
					}
					container.removeChild(row);
					delete container;
					row.setAttribute('id', 'boxItemBundlesRow' + rowNo);

					// add IDs to certain elements of the row
					var labelCount = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'labelCount');
					}, 'label', row);
					var inputCount = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'inputCount');
					}, 'input', row);
					var inputCountOld = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'inputCountOld');
					}, 'input', row);
					var labelWeight = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'labelWeight');
					}, 'label', row);
					var inputWeight = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'inputWeight');
					}, 'input', row);
					var inputWeightOld = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'inputWeightOld');
					}, 'input', row);
					var inputWeightInitial = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'inputWeightInitial');
					}, 'input', row);
					var iconBasket = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'iconBasket');
					}, 'a', row);
					var iconDelete = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'iconDelete');
					}, 'a', row);
					var iconQuestion = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'iconQuestion');
					}, 'span', row);
					var spanWeightTotal = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'spanWeightTotal');
					}, 'span', row);
					var boxPriceSingle = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'boxPriceSingle');
					}, 'div', row);
					var boxPriceTotal = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'boxPriceTotal');
					}, 'div', row);

					if (!(labelCount && inputCount && inputCountOld && labelWeight && inputWeight && inputWeightOld && inputWeightInitial &&
					      iconBasket && iconDelete && iconQuestion && spanWeightTotal && boxPriceSingle && boxPriceTotal)) {
						yuiLoader.hide();
						return;
					}
					labelCount.setAttribute('for', 'inputCount' + rowNo);
					inputCount.setAttribute('id', 'inputCount' + rowNo);
					inputCountOld.setAttribute('id', 'inputCountOld' + rowNo);
					labelWeight.setAttribute('for', 'inputWeight' + rowNo);
					inputWeight.setAttribute('id', 'inputWeight' + rowNo);
					inputWeightOld.setAttribute('id', 'inputWeightOld' + rowNo);
					inputWeightInitial.setAttribute('id', 'inputWeightInitial' + rowNo);
					iconBasket.setAttribute('id', 'iconBasket' + rowNo);
					iconDelete.setAttribute('id', 'iconDelete' + rowNo);
					iconQuestion.setAttribute('id', 'iconQuestion' + rowNo);
					spanWeightTotal.setAttribute('id', 'spanWeightTotal' + rowNo);
					boxPriceSingle.setAttribute('id', 'boxPriceSingle' + rowNo);
					boxPriceTotal.setAttribute('id', 'boxPriceTotal' + rowNo);

					// remove HTML objects not required from DOM
					if (data._7 && !_private.fullAccess) {
						// pharmacy only item, no privileges
						var addBundle = document.getElementById('aItemAddBundle');
						var specialConditions = document.getElementById('aItemSpecialConditions');
						
						iconBasket.parentNode.removeChild(iconBasket);
						iconDelete.parentNode.removeChild(iconDelete);

						if (addBundle) {
							addBundle.parentNode.removeChild(addBundle);
						}
						if (specialConditions) {
							specialConditions.parentNode.removeChild(specialConditions);
						}
					}
					else {
						iconQuestion.parentNode.removeChild(iconQuestion);
					}

					// add event listeners
					YAHOO.util.Event.addListener(inputCount, 'change', function() {
						this.value = _public.checkInputCount(this.value, inputCountOld.value);
						inputCountOld.value = this.value;
						_public.updateItemDetailRowBulk(rowNo, inputCount.value, inputWeight.value, data.price);
					});
					YAHOO.util.Event.addListener(inputWeight, 'change', function() {
						this.value = _public.checkInputWeight(this.value, inputWeightOld.value);
						inputWeightOld.value = this.value;
						_public.updateItemDetailRowBulk(rowNo, inputCount.value, inputWeight.value, data.price);
					});
					var obj = {
						code: data.code,
						bulk: true,
						inputCount: inputCount,
						inputWeight: inputWeight,
						inputWeightInitial: inputWeightInitial,
						pos: pos
					};

					YAHOO.util.Event.addListener(iconBasket, 'click', _private.addToBasket, obj, true);
					YAHOO.util.Event.addListener(iconDelete, 'click', _private.deleteFromBasket, obj, true);

					// add key listeners (or don't if the item is marked "pharmacy only" and the
					// user is not entitled to order such items)
					if (!data._7 || _private.fullAccess) {
						var keyListenerCount = new YAHOO.util.KeyListener(inputCount, {
							keys: [0x0a, 0x0d]
						}, {
							fn: _private.addToBasket,
							scope: obj,
							correctScope: true
						});
						var keyListenerWeight = new YAHOO.util.KeyListener(inputWeight, {
							keys: [0x0a, 0x0d]
						}, {
							fn: _private.addToBasket,
							scope: obj,
							correctScope: true
						});
						var keyListenerBasket = new YAHOO.util.KeyListener(iconBasket, {
							keys: [0x0a, 0x0d]
						}, {
							fn: _private.addToBasket,
							scope: obj,
							correctScope: true
						});
						var keyListenerDelete = new YAHOO.util.KeyListener(iconDelete, {
							keys: [0x0a, 0x0d]
						}, {
							fn: _private.deleteFromBasket,
							scope: obj,
							correctScope: true
						});
						
						keyListenerCount.enable();
						keyListenerWeight.enable();
						keyListenerBasket.enable();
						keyListenerDelete.enable();
					}
					delete obj;

					// insert row into DOM
					form.appendChild(row);

					// insert initial values
					if (typeof count != 'undefined') {
						inputCount.value = _public.checkInputCount(count, '');
						inputCountOld.value = inputCount.value;
						_public.updateItemDetailRowBulk(rowNo, inputCount.value, inputWeight.value, data.price);
					}
					if (typeof weight != 'undefined') {
						inputWeight.value = _public.checkInputWeight(weight, '');
						inputWeightOld.value = inputWeight.value;
						inputWeightInitial.value = inputWeight.value;
						_public.updateItemDetailRowBulk(rowNo, inputCount.value, inputWeight.value, data.price);
					}
					// insert unit name
					var units = YAHOO.util.Dom.getElementsByClassName('spanUnit', 'span', row);

					for (var i=0; i<units.length; i++) {
						units[i].innerHTML = 'kg';
					}
					Forms.initOverLabels();
					inputCount.focus();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('adding row to item details timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailRowBulk.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};

			if (!form) {
				return;
			}
			yuiLoader.show();
			YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailRowBulk.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
		},
		/*-----------------------------------------------------------------------------------------
		 * load template file for item detail rows (liquid ware), add IDs and event listeners, and
		 * insert the row into the DOM
		 *
		 * params:
		 *   data: an object containing item data
		 *   pos: position of the item inside _private.items.current.filtered
		 *   count: (optional) a value to be inserted as 'count'
		 *   weight: (optional) a value to be inserted as 'volume'
		 * prerequisities:
		 *   there must be an HTML element having the ID 'formBundles' in the DOM
		 */
		getItemDetailRowLiquid: function(data, pos, count, weight) {
			var form = document.getElementById('formBundles');
			var callback = {
				success: function(o) {
					// get row and add ID
					var container = document.createElement('div');
					var rowNo = YAHOO.util.Dom.getElementsByClassName('inputCount', 'input', form).length + 1;

					container.innerHTML = o.responseText;

					var row = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'boxItemBundlesRow');
					}, 'div', container);

					if (!row) {
						delete container;
						yuiLoader.hide();
						return;
					}
					container.removeChild(row);
					delete container;
					row.setAttribute('id', 'boxItemBundlesRow' + rowNo);

					// add IDs to certain elements of the row
					var labelCount = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'labelCount');
					}, 'label', row);
					var inputCount = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'inputCount');
					}, 'input', row);
					var inputCountOld = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'inputCountOld');
					}, 'input', row);
					var labelWeight = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'labelWeight');
					}, 'label', row);
					var inputWeight = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'inputWeight');
					}, 'input', row);
					var inputWeightOld = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'inputWeightOld');
					}, 'input', row);
					var inputWeightInitial = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'inputWeightInitial');
					}, 'input', row);
					var iconBasket = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'iconBasket');
					}, 'a', row);
					var iconDelete = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'iconDelete');
					}, 'a', row);
					var iconQuestion = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'iconQuestion');
					}, 'span', row);
					var spanWeightTotal = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'spanWeightTotal');
					}, 'span', row);
					var boxPriceSingle = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'boxPriceSingle');
					}, 'div', row);
					var boxPriceTotal = YAHOO.util.Dom.getElementBy(function(elem) {
						return YAHOO.util.Dom.hasClass(elem, 'boxPriceTotal');
					}, 'div', row);

					if (!(labelCount && inputCount && inputCountOld && labelWeight && inputWeight && inputWeightOld && inputWeightInitial &&
					      iconBasket && iconDelete && iconQuestion && spanWeightTotal && boxPriceSingle && boxPriceTotal)) {
						yuiLoader.hide();
						return;
					}
					labelCount.setAttribute('for', 'inputCount' + rowNo);
					inputCount.setAttribute('id', 'inputCount' + rowNo);
					inputCountOld.setAttribute('id', 'inputCountOld' + rowNo);
					labelWeight.setAttribute('for', 'inputWeight' + rowNo);
					inputWeight.setAttribute('id', 'inputWeight' + rowNo);
					inputWeightOld.setAttribute('id', 'inputWeightOld' + rowNo);
					inputWeightInitial.setAttribute('id', 'inputWeightInitial' + rowNo);
					iconBasket.setAttribute('id', 'iconBasket' + rowNo);
					iconDelete.setAttribute('id', 'iconDelete' + rowNo);
					iconQuestion.setAttribute('id', 'iconQuestion' + rowNo);
					spanWeightTotal.setAttribute('id', 'spanWeightTotal' + rowNo);
					boxPriceSingle.setAttribute('id', 'boxPriceSingle' + rowNo);
					boxPriceTotal.setAttribute('id', 'boxPriceTotal' + rowNo);

					// remove HTML objects not required from DOM
					if (data._7 && !_private.fullAccess) {
						// pharmacy only item, no privileges
						var addBundle = document.getElementById('aItemAddBundle');
						var specialConditions = document.getElementById('aItemSpecialConditions');
						
						iconBasket.parentNode.removeChild(iconBasket);
						iconDelete.parentNode.removeChild(iconDelete);

						if (addBundle) {
							addBundle.parentNode.removeChild(addBundle);
						}
						if (specialConditions) {
							specialConditions.parentNode.removeChild(specialConditions);
						}
					}
					else {
						iconQuestion.parentNode.removeChild(iconQuestion);
					}

					// add event listeners
					YAHOO.util.Event.addListener(inputCount, 'change', function() {
						this.value = _public.checkInputCount(this.value, inputCountOld.value);
						inputCountOld.value = this.value;
						_public.updateItemDetailRowLiquid(rowNo, inputCount.value, inputWeight.value, data.price);
					});
					YAHOO.util.Event.addListener(inputWeight, 'change', function() {
						this.value = _public.checkInputCount(this.value, inputWeightOld.value);
						inputWeightOld.value = this.value;
						_public.updateItemDetailRowLiquid(rowNo, inputCount.value, inputWeight.value, data.price);
					});
					var obj = {
						code: data.code,
						bulk: true,
						inputCount: inputCount,
						inputWeight: inputWeight,
						inputWeightInitial: inputWeightInitial,
						pos: pos
					};

					YAHOO.util.Event.addListener(iconBasket, 'click', _private.addToBasket, obj, true);
					YAHOO.util.Event.addListener(iconDelete, 'click', _private.deleteFromBasket, obj, true);

					// add key listeners (or don't if the item is marked "pharmacy only" and the
					// user is not entitled to order such items)
					if (!data._7 || _private.fullAccess) {
						var keyListenerCount = new YAHOO.util.KeyListener(inputCount, {
							keys: [0x0a, 0x0d]
						}, {
							fn: _private.addToBasket,
							scope: obj,
							correctScope: true
						});
						var keyListenerWeight = new YAHOO.util.KeyListener(inputWeight, {
							keys: [0x0a, 0x0d]
						}, {
							fn: _private.addToBasket,
							scope: obj,
							correctScope: true
						});
						var keyListenerBasket = new YAHOO.util.KeyListener(iconBasket, {
							keys: [0x0a, 0x0d]
						}, {
							fn: _private.addToBasket,
							scope: obj,
							correctScope: true
						});
						var keyListenerDelete = new YAHOO.util.KeyListener(iconDelete, {
							keys: [0x0a, 0x0d]
						}, {
							fn: _private.deleteFromBasket,
							scope: obj,
							correctScope: true
						});
						
						keyListenerCount.enable();
						keyListenerWeight.enable();
						keyListenerBasket.enable();
						keyListenerDelete.enable();
					}
					delete obj;

					// insert row into DOM
					form.appendChild(row);

					// insert initial values
					if (typeof count != 'undefined') {
						inputCount.value = _public.checkInputCount(count, '');
						inputCountOld.value = inputCount.value;
						_public.updateItemDetailRowLiquid(rowNo, inputCount.value, inputWeight.value, data.price);
					}
					if (typeof weight != 'undefined') {
						inputWeight.value = _public.checkInputCount(weight, '');
						inputWeightOld.value = inputWeight.value;
						inputWeightInitial.value = inputWeight.value;
						_public.updateItemDetailRowLiquid(rowNo, inputCount.value, inputWeight.value, data.price);
					}
					// insert unit name
					var units = YAHOO.util.Dom.getElementsByClassName('spanUnit', 'span', row);

					for (var i=0; i<units.length; i++) {
						units[i].innerHTML = (BB.getLang() == 'de') ? 'Ltr.' : 'l';
					}
					Forms.initOverLabels();
					inputCount.focus();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('adding row to item details timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailRowLiquid.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};

			if (!form) {
				return;
			}
			yuiLoader.show();
			YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getItemDetailRowLiquid.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
		},
		/*-----------------------------------------------------------------------------------------
		 * create a monograph string like '<span title="Deutsches Arzneibuch">DAB</span>' from
		 * an input string like 'DAB'
		 * 
		 * params:
		 *   mono: input string (monograph)
		 * return value:
		 *   a span containing the monograph and having the full name of the monograph as title, if
		 *   mono is known; otherwise parameter mono will be returned
		 */
		getMonographString: function(mono) {
			if (typeof mono == 'string') {
				mono = _private.trim(mono);
			}
			else {
				return '';
			}
			if (BB.getLang() == 'de') {
				switch (mono.toLowerCase()) {
					case 'dab':
						return '<span title="Deutsches Arzneibuch">DAB</span>';
					case 'dac':
						return '<span title="Deutscher Arzneicodex">DAC</span>';
					case 'epv':
						return '<span title="eigene Pr\u00FCfvorschrift">ePV</span>';
					case 'erg. b 6':
						return '<span title="Erg\u00E4nzungsband sechs">Erg. B 6</span>';
					case 'hab':
						return '<span title="Hom\u00F6opathisches Arzneibuch">HAB</span>';
					case 'ph. eur.':
						return '<span title="Europ\u00E4isches Arzneibuch (Pharmacopoea Europaea)">Ph. Eur.</span>';
					case '\u00D6ab':
					case '\u00F6ab':
						/* ? */
					default:
						return mono;
				}
			}
			else {
				switch (mono.toLowerCase()) {
					case 'dab':
						return '<span title="German pharmacopoeia">DAB</span>';
					case 'dac':
						return '<span title="German medicine codex">DAC</span>';
					case 'epv':
						return '<span title="own test specification">ePV</span>';
					case 'erg. b 6':
						return '<span title="Supplemental volume six">Erg. B 6</span>';
					case 'hab':
						return '<span title="Homeopathic pharmacopoeia">HAB</span>';
					case 'ph. eur.':
						return '<span title="European pharmacopoeia (Pharmacopoea Europaea)">Ph. Eur.</span>';
					case '\u00D6ab':
					case '\u00F6ab':
						/* ? */
					default:
						return mono;
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * display a certain page of items
		 *
		 * params:
		 *   no: the number of the page to be shown
		 * prerequisites:
		 *   _private.filters must have been initialized
		 */
		goToPage: function(no) {
			var table = document.getElementById('tbodyShop');
			var max;

			if (!BB.isInteger(no) || !table || !_private.items.current) {
				return;
			}
			_private.filters = Filter.getSettings();
			max = Math.ceil(_private.items.current.filtered.length / _private.filters.items);

			if (max < 1) {
				max = 1
			}
			if (no < 1) {
				no = 1;
			}
			if (no > max) {
				no = max;
			}
			_private.pageNo = no;
			_private.removeItems();

			// render items to screen
			for (var i=(no-1)*_private.filters.items; i<no*_private.filters.items && i<_private.items.current.filtered.length; i++) {
				// insert name into DOM
				if (_private.items.current.filtered[i].data) {
					// item in shop
					if (!_private.filters.latin) {
						YAHOO.util.Dom.getElementBy(function(elem) {
							return YAHOO.util.Dom.hasClass(elem, 'tdName');
						}, 'td', _private.items.current.filtered[i].row).innerHTML = '<span class="showDetails">' + _private.items.current.filtered[i].name + '</span>';
					}
					else {
						YAHOO.util.Dom.getElementBy(function(elem) {
							return YAHOO.util.Dom.hasClass(elem, 'tdName');
						}, 'td', _private.items.current.filtered[i].row).innerHTML = '<span class="showDetails">' + _private.items.current.filtered[i].latin + '</span>';
					}
				}
				else {
					// favorite removed from shop
					if (BB.getLang() == 'de') {
						YAHOO.util.Dom.getElementBy(function(elem) {
							return YAHOO.util.Dom.hasClass(elem, 'tdName');
						}, 'td', _private.items.current.filtered[i].row).innerHTML = (BB.getLang() == 'de') ? 'Diesen Artikel bieten wir derzeit nicht an.' : 'We currently do not offer this item.';
					}
					else {
						YAHOO.util.Dom.getElementBy(function(elem) {
							return YAHOO.util.Dom.hasClass(elem, 'tdName');
						}, 'td', _private.items.current.filtered[i].row).innerHTML = '';
					}
				}
				// add listeners
				var showDetails = YAHOO.util.Dom.getElementsByClassName('showDetails', 'span', _private.items.current.filtered[i].row);
				var iconInfo = YAHOO.util.Dom.getElementsBy(function(elem) {
					return YAHOO.util.Dom.hasClass(elem, 'iconInfo') && !YAHOO.util.Dom.hasClass(elem, 'noIcon');
				}, 'span', _private.items.current.filtered[i].row);

				var iconFav = YAHOO.util.Dom.getElementsByClassName('iconFav', 'span', _private.items.current.filtered[i].row);
				var obj = _private.items.current.filtered[i];

				YAHOO.util.Event.removeListener(showDetails, 'click');
				YAHOO.util.Event.removeListener(iconInfo, 'click');
				YAHOO.util.Event.removeListener(iconFav, 'click');

				YAHOO.util.Event.addListener(showDetails, 'click', function() {
					_private.generateItemDetail(this.data);  // don't use a pos parameter here
				}, obj, true);
				YAHOO.util.Event.addListener(iconInfo, 'click', function() {
					if (this.data._7) {
						document.getElementById('boxInfoPharmacyOnly').style.display = 'block';
					}
					else {
						document.getElementById('boxInfoPharmacyOnly').style.display = 'none';
					}
					if (this.data._19) {
						document.getElementById('boxInfoOrganic').style.display = 'block';
					}
					else {
						document.getElementById('boxInfoOrganic').style.display = 'none';
					}
					_private.panels.info.show();
				}, obj, true);
				YAHOO.util.Event.addListener(iconFav, 'click', _private.toggleFavorite, obj, true);

				// mark favorites/unmark non-favorites
				if (_private.isFavorite(_private.items.current.filtered[i].code)) {
					YAHOO.util.Dom.addClass(_private.items.current.filtered[i].row, 'favorite');
					YAHOO.util.Dom.addClass(_private.items.current.filtered[i].row, 'colorSetFG');

					if (iconFav.length > 0) {
						iconFav[0].setAttribute('title', (BB.getLang() == 'de') ? 'aus meinen Favoriten entfernen' : 'remove from my favourites');
					}
				}
				else {
					YAHOO.util.Dom.removeClass(_private.items.current.filtered[i].row, 'favorite');
					YAHOO.util.Dom.removeClass(_private.items.current.filtered[i].row, 'colorSetFG');

					if (iconFav.length > 0) {
						iconFav[0].setAttribute('title', (BB.getLang() == 'de') ? 'zu meinen Favoriten hinzuf\u00FCgen' : 'add to my favourites');
					}
				}
				// render item to screen
				if (i % 2) {
					YAHOO.util.Dom.removeClass(_private.items.current.filtered[i].row, 'trOdd');
					YAHOO.util.Dom.addClass(_private.items.current.filtered[i].row, 'trEven');
				}
				else {
					YAHOO.util.Dom.removeClass(_private.items.current.filtered[i].row, 'trEven');
					YAHOO.util.Dom.addClass(_private.items.current.filtered[i].row, 'trOdd');
				}
				table.appendChild(_private.items.current.filtered[i].row);
			}
			// update items per page
			var itemsPerPage = YAHOO.util.Dom.getElementsByClassName('aItemsPerPage', 'a');

			YAHOO.util.Dom.removeClass(itemsPerPage, 'selected');
			YAHOO.util.Event.removeListener(itemsPerPage, 'click');

			for (var i=1; i<=4; i++) {
				itemsPerPage = YAHOO.util.Dom.getElementsByClassName('a' + (i * 10) + 'ItemsPerPage');
				YAHOO.util.Event.addListener(itemsPerPage, 'click', function() {
					_private.filters.items = this.itemsPerPage;
					Filter.setSettings(_private.filters);

					if (   _private.items && _private.items.current && _private.items.current.all
					    && _private.items.current.all.length > _private.maxPagesUnfiltered * _private.filters.items
						&& !BB.isAlpha(_private.letter)) {
						_private.setLetterAndRenderItems('A');
					}
					else {
						_private.goToPage(1);
					}
				}, {
					itemsPerPage: i * 10
				}, true);
			}
			itemsPerPage = YAHOO.util.Dom.getElementsByClassName('a' + _private.filters.items + 'ItemsPerPage', 'a');
			YAHOO.util.Dom.addClass(itemsPerPage, 'selected');

			// update pager
			var pageNo    = YAHOO.util.Dom.getElementsByClassName('spanPageNo', 'span');
			var pageMax   = YAHOO.util.Dom.getElementsByClassName('spanPageMax', 'span');
			var firstPage = YAHOO.util.Dom.getElementsByClassName('aFirstPage', 'a');
			var prevPage  = YAHOO.util.Dom.getElementsByClassName('aPrevPage', 'a');
			var nextPage  = YAHOO.util.Dom.getElementsByClassName('aNextPage', 'a');
			var lastPage  = YAHOO.util.Dom.getElementsByClassName('aLastPage', 'a');

			for (var i=0; i<pageNo.length; i++) {
				pageNo[i].innerHTML = no.toString();
			}
			for (var i=0; i<pageMax.length; i++) {
				pageMax[i].innerHTML = max;
			}
			YAHOO.util.Event.removeListener(firstPage, 'click');
			YAHOO.util.Event.addListener(firstPage, 'click', function() {
				_private.goToPage(1);
			});
			YAHOO.util.Event.removeListener(prevPage, 'click');
			YAHOO.util.Event.addListener(prevPage, 'click', function() {
				_private.goToPage(no - 1);
			});
			YAHOO.util.Event.removeListener(nextPage, 'click');
			YAHOO.util.Event.addListener(nextPage, 'click', function() {
				_private.goToPage(no + 1);
			});
			YAHOO.util.Event.removeListener(lastPage, 'click');
			YAHOO.util.Event.addListener(lastPage, 'click', function() {
				_private.goToPage(max);
			});
			// update item counters
			var counters = YAHOO.util.Dom.getElementsByClassName('spanCounter', 'span');

			for (var i=0; i<counters.length; i++) {
				counters[i].innerHTML = _private.items.current.filtered.length;
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * check if an item with a given ERP code is a member of the user's favorites
		 *
		 * params:
		 *   code: the ERP code of the item
		 * return value:
		 *   boolean indicating, if the item is a favorite or not
		 */
		isFavorite: function(code) {
			try {
				for (var i=0; i<_private.items.favorites.codes.length; i++) {
					if (code == _private.items.favorites.codes[i]) {
						return true;
					}
				}
			}
			catch (e) {
				DEBUG('Error: could not determine if item ' + code + ' is a favorite (list of favorites not yet initialized?).');
			}
			return false;
		},
		/*-----------------------------------------------------------------------------------------
		 * create a string like "<euro>,<cent>" with two digits to the right-hand-side of the comma
		 *
		 * params:
		 *   price: a numeric value
		 * return value:
		 *   a string representation with two digits at the right-hand side of the comma (German)
		 *   or point (English) - if the param is missing or a non-numeric value the function will
		 *   return '0,00' or '0.00'
		 */
		priceToString: function(price) {
			var string;

			if (typeof price == 'number') {
				string = price.toFixed(2);
			}
			else {
				string = '0.00';
			}
			if (BB.getLang() == 'de') {
				string = string.replace('.', ',');
			}
			return string;
		},
		/*-----------------------------------------------------------------------------------------
		 * remove filter settings for cultivation
		 */
		removeCultFilters: function() {
			var organic = document.getElementById('aCultOrganic');
			var conventional = document.getElementById('aCultConventional');
			var all = document.getElementById('aCultAll');

			if (organic) {
				YAHOO.util.Dom.removeClass(organic, 'selected');
			}
			if (conventional) {
				YAHOO.util.Dom.removeClass(conventional, 'selected');
			}
			if (all) {
				YAHOO.util.Dom.addClass(all, 'selected');
			}
			_private.filters.organic = false;
			_private.filters.conventional = false;
			Filter.setSettings(_private.settings);
		},
		/*-----------------------------------------------------------------------------------------
		 * remove shop items
		 */
		removeItems: function() {
			var items = YAHOO.util.Dom.getElementsByClassName('trItem', 'tr', 'tbodyShop');

			for (var i=0; i<items.length; i++) {
				items[i].parentNode.removeChild(items[i]);
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * round price to nearest multiple of 5 cents
		 *
		 * params:
		 *   price: the price to be rounded up
		 * return value:
		 *   0.00 if some kind of error was detected, the price rounded up otherwise
		 * prerequisities:
		 *   price must be a number
		 */
		roundPrice: function(price) {
			if (typeof price != 'number' || price < 0) {
				return 0.00;
			}
			return Math.ceil(price * 20) / 20;
		},
		/*-----------------------------------------------------------------------------------------
		 * set a letter item names have to start with if they are to be displayed (if no valid
		 * value is given, the letter will be unset)
		 *
		 * params:
		 *   letter: a letter
		 * prerequisities:
		 *   * product teasers must have been initialized
		 */
		setLetter: function(letter) {
			YAHOO.util.Dom.removeClass(YAHOO.util.Dom.getElementsByClassName('selected', 'a', 'spanFirstLetterFilters'), 'selected');
			_private.letter = null;

			if (BB.isAlpha(letter)) {
				var link = document.getElementById('aFirstLetter' + letter.toUpperCase());

				if (link) {
					YAHOO.util.Dom.addClass(link, 'selected');
				}
				_private.letter = letter.toUpperCase();
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * set a letter item names have to start with if they are to be displayed (if no valid
		 * value is given, the letter will be unset) and render the items afterwards
		 */
		setLetterAndRenderItems: function(letter) {
			_private.setLetter(letter);
			_private.filterItems(_private.items.current);
			_private.goToPage(1);
		},
		/*-----------------------------------------------------------------------------------------
		 * initialize/update content
		 *
		 * params:
		 *   id: either an object containing the ID (as attribute .id) or a numerical ID - if not
		 *       given the last node viewed (_private.nodeID) will be shown
		 */
		showNode: function(id) {
			var table = document.getElementById('tbodyShop');
			var row;

			if (!table) {
				Content.loadShop(id);
				return;
			}
			_public.setDisplayMode('shop');

			// check parameter
			if (typeof id == 'object') {
				if (typeof this.id == 'number' && BB.isNatural(this.id)) {
					if (_private.nodeId != this.id) {
						_private.nodeId = this.id;
						_private.pageNo = 1;
						_private.setLetter();
					}
				}
			}
			else if (typeof id == 'number' && BB.isNatural(id)) {
				if (_private.nodeId != id) {
					_private.nodeId = id;
					_private.pageNo = 1;
					_private.setLetter();
				}
			}
			// mark corresponding navigation link as selected and generate list of items for this
			// node
			YAHOO.util.Dom.removeClass(document.getElementById('aFastOrder'), 'selected');
			YAHOO.util.Dom.removeClass(YAHOO.util.Dom.getElementsByClassName('aNavi', 'a', 'boxNaviLevel2Products'), 'selected');

			if (_private.nodeId > 0) {
				YAHOO.util.Dom.addClass(document.getElementById('CG_' + _private.nodeId), 'selected');
			}
			if (_private.letter) {
				YAHOO.util.Dom.addClass(document.getElementById('aFirstLetter' + _private.letter), 'selected');
			}
			// generate and filter items
			if (_private.generateItems() < 0) {
				DEBUG('Error: encountered problems when loading items for this node.');
				return;
			}
			_private.filterItems(_private.items.node);
			_private.items.current = _private.items.node;

			// render items
			_private.goToPage(_private.pageNo);
			Content.setPersNaviSelected();
		},
		/*-----------------------------------------------------------------------------------------
		 * toggle favorite status of an item
		 *
		 * prerequisities:
		 *   This function is to be used as an event handler only. Its context must be overwritten
		 *   by an object containing a member .code (ERP code) and a member .row (an HTML object
		 *   representing a table row) to be used on index pages, or by an object containing a
		 *   member .code and a member .icon (the HTML object invoking this event handler) for
		 *   detail pages/fast order respectively.
		 */
		toggleFavorite: function() {
			// index page (table containing multiple items)
			if (!document.getElementById('containerItemDetail') && !document.getElementById('boxFastOrderItem')) {
				var iconFav = YAHOO.util.Dom.getElementsByClassName('iconFav', 'span', this.row);
				var pageNo;

				// remove favorite and update current page when viewing list of favorites
				if (_private.displayMode == 'favorites') {
					pageNo = Math.ceil((_private.findValueInList(this.code, _private.items.favorites.codes) + 1) / _private.filters.items);
					Favorites.deleteFavorite(this.code);
					_private.updateFavorites(this.code, false);
					_private.goToPage(pageNo);
				}
				// general removal of a favorite
				else if (YAHOO.util.Dom.hasClass(this.row, 'favorite')) {
					YAHOO.util.Dom.removeClass(this.row, 'favorite');
					YAHOO.util.Dom.removeClass(this.row, 'colorSetFG');

					if (iconFav.length > 0) {
						iconFav[0].setAttribute('title', (BB.getLang() == 'de') ? 'zu meinen Favoriten hinzuf\u00FCgen' : 'add to my favourites');
					}
					Favorites.deleteFavorite(this.code);
					_private.updateFavorites(this.code, false);
				}
				// add a favorite
				else {
					YAHOO.util.Dom.addClass(this.row, 'favorite');
					YAHOO.util.Dom.addClass(this.row, 'colorSetFG');

					if (iconFav.length > 0) {
						iconFav[0].setAttribute('title', (BB.getLang() == 'de') ? 'aus meinen Favoriten entfernen' : 'remove from my favourites');
					}
					Favorites.setFavorite(this.code);
					_private.updateFavorites(this.code, true);
				}
			}
			// detail page (table containing units and prices of a single item)
			else {
				// remove favorite and return to list of favorites
				if (_private.displayMode == 'favorites') {
					Favorites.deleteFavorite(this.code);
					_private.updateFavorites(this.code, false);
					Content.loadFavorites();
				}
				// general removal of a favorite
				else if (YAHOO.util.Dom.hasClass(this.icon, 'iconFavOn')) {
					YAHOO.util.Dom.removeClass(this.icon, 'iconFavOn');
					YAHOO.util.Dom.addClass(this.icon, 'iconFavOff');

					this.icon.setAttribute('title', (BB.getLang() == 'de') ? 'zu meinen Favoriten hinzuf\u00FCgen' : 'add to my favourites');
					Favorites.deleteFavorite(this.code);
					_private.updateFavorites(this.code, false);
				}
				// add a favorite
				else {
					YAHOO.util.Dom.removeClass(this.icon, 'iconFavOff');
					YAHOO.util.Dom.addClass(this.icon, 'iconFavOn');

					this.icon.setAttribute('title', (BB.getLang() == 'de') ? 'aus meinen Favoriten entfernen' : 'remove from my favourites');
					Favorites.setFavorite(this.code);
					_private.updateFavorites(this.code, true);
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * remove leading and trailing white space from a string
		 *
		 * params:
		 *   string: the string to trim
		 * return value:
		 *   the trimmed string
		 */
		trim: function(string) {
			if (typeof string == 'undefined') {
				return '';
			}
			return string.toString().replace(/^\s*/, '').replace(/\s*$/, '');
		},
		/*-----------------------------------------------------------------------------------------
		 * update list of favorites and no. of favorites
		 *
		 * params:
		 *   code: ERP code of favorite to be added to/removed from list
		 *   add:  boolean (true: add favorite, false: remove favorite)
		 * prerequisities:
		 *   * The arrays _private.items.favorites.all and _private.items.favorites.codes must
		 *     match.
		 */
		updateFavorites: function(code, add) {
//try { console.clear(); } catch (e) {}
//DEBUG('Anzahl Objekte in Favoritenliste: ' + _private.items.favorites.all.length);
//DEBUG(_private.items.favorites.all);
//DEBUG('Anzahl Art.-Nrn. in Favoritenliste: ' + _private.items.favorites.codes.length);
//DEBUG(_private.items.favorites.codes);
			if (_private.items.favorites.all.length != _private.items.favorites.codes.length) {
				DEBUG('Error: something is seriously wrong with your favorites!');
				return;
			}
			if (add) {
				// append item to list of favorites
				var item = null;

				if (_private.findValueInList(code, _private.items.favorites.codes) >= 0) {
					return;
				}
				for (var i=0; i<_private.data.items.length; i++) {
					if (code == _private.data.items[i].code) {
						item = _private.data.items[i];
						break;
					}
				}
				if (item) {
					_private.items.favorites.codes[_private.items.favorites.codes.length] = code;
					_private.items.favorites.all[_private.items.favorites.all.length] = _private.generateItem(item);
//DEBUG('');
//DEBUG('Art.-Nr. ' + code + ' an ' + _private.items.favorites.all.length + '. Stelle eingef\u00FCgt');
//DEBUG('Anzahl Objekte in Favoritenliste: ' + _private.items.favorites.all.length);
//DEBUG(_private.items.favorites.all);
//DEBUG('Anzahl Art.-Nrn. in Favoritenliste: ' + _private.items.favorites.codes.length);
//DEBUG(_private.items.favorites.codes);
				}
			}
			else {
				// remove item from list of favorites
				for (var i=0; i<_private.items.favorites.codes.length; i++) {
					if (code == _private.items.favorites.codes[i]) {
						for (var j=i; j<_private.items.favorites.codes.length-1; j++) {
							_private.items.favorites.codes[j] = _private.items.favorites.codes[j+1];
							_private.items.favorites.all[j] = _private.items.favorites.all[j+1];
						}
						_private.items.favorites.codes.length--;
						_private.items.favorites.all.length--;
//DEBUG('');
//DEBUG('Art.-Nr. ' + code + ' von ' + (i + 1) + '. Stelle gel\u00F6scht');
//DEBUG('Anzahl Objekte in Favoritenliste: ' + _private.items.favorites.all.length);
//DEBUG(_private.items.favorites.all);
//DEBUG('Anzahl Art.-Nrn. in Favoritenliste: ' + _private.items.favorites.codes.length);
//DEBUG(_private.items.favorites.codes);
						break;
					}
				}
			}
			_private.filterItems(_private.items.favorites);
//DEBUG('');
//DEBUG('Anzahl Objekte in Favoritenliste nach Filterung: ' + _private.items.favorites.all.length);
			document.getElementById('spanNoOfFavorites').innerHTML = _private.items.favorites.codes.length;
		}
	};

	// public functions ===========================================================================
	var _public = {
		/*-----------------------------------------------------------------------------------------
		 * calculate price for a single bundle of bulk ware
		 *
		 * params:
		 *   count: number of bundles ordered (number or string)
		 *   weight: the weight of a single bundle (number or string)
		 *   price5: price per kg if at least 5kg ordered (number (!))
		 * return value:
		 *   the price calculated as string ('0.00'/'0,00' in case of invalid weight)
		 */
		calculatePriceSingleBulk: function(count, weight, price5) {
			var price = '0.00';

			// create numerical values from strings passed to the function
			if (typeof count == 'string') {
				count = _public.checkInputCount(count, '');

				if (count == '') {
					count = 1;
				}
				else {
					count = parseInt(count);
				}
			}
			if (typeof weight == 'string') {
				weight = _public.checkInputWeight(weight, '').replace(',', '.');

				if (weight == '') {
					weight = 0.0;
				}
				else {
					weight = parseFloat(weight);
				}
			}
 			// handle numerical values
/*
			if (   typeof weight == 'number' && weight >= 0.1
			    && typeof count == 'number' && count >= 1
			    && typeof price5 == 'number' && price5 > 0.00) {
*/
			if (   typeof weight == 'number' && weight >= 0.1
			    && typeof count == 'number' && count >= 1
			    && typeof price5 == 'number' && price5 >= 0.00) {
				if (weight < 1.0) {
					price = weight * price5 + 2.10;
				}
				else if (weight < 5.0) {
					//if (count == 1) {
						price = weight * (price5 + 1.50);
					//}
					//else {
					//	price = weight * (price5 + 1.50) + 0.50;
					//}
				}
				else {
					price = weight * price5;
				}
				price = _private.roundPrice(price).toFixed(2);
			}
			if (BB.getLang() == 'de') {
				price = price.replace('.', ',');
			}
			return price;
		},
		/*-----------------------------------------------------------------------------------------
		 * check the input of a count field
		 *
		 * params:
		 *   value: the value to validate
		 *   valueOld: the value successfully validated last (empty string if there was none)
		 *   min: (optional) the minimum required for value (defaults to 1)
		 *   multiple: (optional) indicates whether a multiple of <min> items must be ordered
		 *             (defaults to false)
		 * return value:
		 *   trimmed value, if value is valid, valueOld otherwise 
		 */
		checkInputCount: function(value, valueOld, min, multiple) {
			if (typeof value == 'string') {
				value = _private.trim(value);
			}
			else if (typeof value == 'number') {
				value = value.toString();
			}
			else {
				return valueOld;
			}
			if (typeof min != 'number' || min < 1) {
				min = 1;
			}
			if (parseInt(value).toString() == value) {
				if (parseInt(value) >= min) {
					if (!multiple) {
						return value;
					}
					else if (parseInt((parseFloat(value) + .5) / min) * min == parseInt(value)) {
						return value;
					}
					else {
						return valueOld;
					}
				}
				if (parseInt(value) == 0) {
					return '';
				}
			}
			if (value == '') {
				return '';
			}
			return valueOld;
		},
		/*-----------------------------------------------------------------------------------------
		 * check the input of a weight field: value must be an integer or decimal (comma or point
		 * may be used to separate decimal places), the minimum value must be 0.1
		 *
		 * params:
		 *   value: the value to validate
		 *   valueOld: the value successfully validated last (empty string if there was none)
		 * return value:
		 *   trimmed value (for integers) or trimmed value with decimal separator depending on
		 *   current language and three decimal places (for decimals), if value is valid, valueOld
		 *   otherwise 
		 */
		checkInputWeight: function(value, valueOld) {
			if (typeof value == 'string') {
				value = _private.trim(value).replace(/,/g, '.');
			}
			else if (typeof value == 'number') {
				value = value.toString();
			}
			else {
				return valueOld;
			}
			if (value.match(/^[^\.]*\.[^\.]*$/)) {
				// remove trailing zeros from decimal places if value contains exactly one decimal
				// point (this is required for the next if condition to work)
				value = value.replace(/0+$/, '').replace(/\.$/, '');
			}
			if (parseFloat(value).toString() == value) {
				if (parseFloat(value) >= 0.1) {
					value = parseFloat(value).toFixed(3);
					value = value.replace(/0+$/, '');
					value = value.replace(/\.$/, '');

					if (BB.getLang() == 'de') {
						value = value.replace('.', ',');
					}
					return value;
				}
				if (parseFloat(value) == 0.0) {
					return '';
				}
			}
			if (value == '') {
				return '';
			}
			return valueOld;
		},
		/*-----------------------------------------------------------------------------------------
		 * display favorites instead of all shop items
		 *
		 * Since this function may be called from any node once a user is logged in, _private.data
		 * and _private.items.favorites.all are not necessarily set. Thus these values must be
		 * evaluated and set to adequate values if they are not yet set.
		 */
		displayFavorites: function() {
			var callbackItems = {
				success: function(o) {
					try {
						_private.data = YAHOO.lang.JSON.parse(o.responseText);

						if (_private.items.favorites.initialized) {
							_private.removeCultFilters();
							_private.items.current = _private.items.favorites;
							_private.goToPage(_private.pageNo);
							yuiLoader.hide();
						}
						else {
							YAHOO.util.Connect.asyncRequest('POST', '/_json/favorites.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackFavorites);
						}
					}
					catch (e) {
						DEBUG('failed to parse JSON file containing item data: ' + e.message);
						yuiLoader.hide();
						return;
					}
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('reading JSON file containing item data timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/' + BB.getLangPath() + 'shopData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackItems);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};
			var callbackFavorites = {
				success: function(o) {
					try {
						_private.items.favorites.codes = YAHOO.lang.JSON.parse(o.responseText).items;
						_private.generateFavorites();

						if (_private.filters) {
							_private.removeCultFilters();
							_private.filterItems(_private.items.favorites);
						}
						else {
							// don't filter items, if favorites are called from a non-shop node
							_private.items.favorites.filtered = _private.items.favorites.all;
						}
						_private.items.favorites.initialized = true;
						_private.items.current = _private.items.favorites;
						_private.goToPage(_private.pageNo);
						yuiLoader.hide();
					}
					catch (e) {
						DEBUG('failed to parse JSON file containing favorites: ' + e.message);
						yuiLoader.hide();
						return;
					}
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting JSON file containing favorites timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/favorites.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackFavorites);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};

			// Artikeldaten und Favoriten laden
			if (document.getElementById('tbodyShop')) {
				yuiLoader.show();
				_public.setDisplayMode('favorites');
				_public.toggleFilters(true);

				if (_private.data) {
					if (_private.items.favorites.initialized) {
						_private.removeCultFilters();
						_private.filterItems(_private.items.favorites);
						_private.items.current = _private.items.favorites;
						_private.goToPage(_private.pageNo);
						yuiLoader.hide();
					}
					else {
						YAHOO.util.Connect.asyncRequest('POST', '/_json/favorites.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackFavorites);
					}
				}
				else {
					YAHOO.util.Connect.asyncRequest('POST', '/_json/' + BB.getLangPath() + 'shopData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackItems);
				}
			}
			else {
				Content.loadFavorites();
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * display details of an item in the basket
		 *
		 * params:
		 *   pos: the position of the item to show - position starts from 0, items with the same
		 *        ERP code, that have been added to the basket directly after each other are
		 *        considered to have the same position (if there are items with the same ERP code
		 *        and an item has been added in between them, those items are considered to have
		 *        different positions)
		 */
		displayItemDetailBasket: function(pos) {
			var callbackShop = {
				success: function(o) {
					try {
						_private.data = YAHOO.lang.JSON.parse(o.responseText);
					}
					catch (e) {
						DEBUG('could not parse JSON file containing shop data: ' + e.message);
						yuiLoader.hide();
						return;
					}
					YAHOO.util.Connect.asyncRequest('POST', '/_json/favorites.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackFavorites);
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting shop data timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/' + BB.getLangPath() + 'shopData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};
			var callbackFavorites = {
				success: function(o) {
					try {
						_private.items.favorites.codes = YAHOO.lang.JSON.parse(o.responseText).items;
						_private.generateFavorites();
					}
					catch (e) {
						DEBUG('failed to parse JSON file containing favorites: ' + e.message);
						yuiLoader.hide();
						return;
					}
					YAHOO.util.Connect.asyncRequest('POST', '/_json/basketData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBasket);
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting JSON file containing favorites timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/favorites.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackFavorites);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};
			var callbackBasket = {
				success: function(o) {
					try {
						_private.items.basket.codes = YAHOO.lang.JSON.parse(o.responseText).items;
						_private.generateBasketItems();
						_private.items.current = _private.items.basket;

						if (pos >= 0 && pos < _private.items.basket.all.length) {
							_private.generateItemDetail(_private.items.basket.all[pos].data, pos);
						}
						yuiLoader.hide();
					}
					catch (e) {
						DEBUG('could not parse JSON file containing basket items: ' + e.message);
						yuiLoader.hide();
						return;
					}
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting JSON file containing basket items timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/basketData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBasket);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};

			yuiLoader.show();

			if (_private.data) {
				if (_private.items.favorites.all) {
					YAHOO.util.Connect.asyncRequest('POST', '/_json/basketData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBasket);
				}
				else {
					YAHOO.util.Connect.asyncRequest('POST', '/_json/favorites.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackFavorites);
				}
			}
			else {
				YAHOO.util.Connect.asyncRequest('POST', '/_json/' + BB.getLangPath() + 'shopData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackShop);
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * display details of an item previously ordered
		 *
		 * params:
		 *   code: the ERP code of the item to be shown
		 */
		displayItemDetailOrder: function(code) {
			var links = YAHOO.util.Dom.getElementsByClassName('aShowItem', 'a', 'containerOrdersDetail');
			var callback = {
				success: function(o) {
					try {
						_private.items.favorites.codes = YAHOO.lang.JSON.parse(o.responseText).items;
						_private.items.favorites.all = _private.generateFavorites();
					}
					catch (e) {
						DEBUG('failed to parse JSON file containing favorites: ' + e.message);
						yuiLoader.hide();
						return;
					}
					//_private.displayMode = 'order';
					//_private.getItemDetail(code);       This function has been removed!
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting JSON file containing favorites timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/favorites.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};

			yuiLoader.show();

			if (_private.items.favorites.all) {
				//_private.displayMode = 'order';
				//_private.getItemDetail(code);       This function has been removed!
				yuiLoader.hide();
			}
			else {
				YAHOO.util.Connect.asyncRequest('POST', '/_json/favorites.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * display the items a search returned
		 *
		 * params:
		 *   results: an array of item data objects
		 * prerequisities:
		 *   _private.items.search must have been initialized using _public.searchItems()
		 */
		displaySearchResults: function(results) {
			var shop = document.getElementById('containerShop');
			var table = document.getElementById('tbodyShop');
			var selected;
			var text;

			_public.toggleFilters(true);

			// display search results even if there a none (this will update the pager)
			_private.items.current = _private.items.search;
			_private.goToPage(_private.pageNo);

			if (_private.items.search.all.length == 0) {
				if (shop) {
					if (BB.getLang() == 'de') {
						shop.innerHTML =
							  '<div class="box6ColInner">'
							+   '<div class="box3Col">'
							+     '<h1>Leider konnten keine Produkte gefunden werden</h1>'
							+     'Bitte beachten Sie, dass nur solche Produkte angezeigt werden, '
							+     'die alle Suchbegriffe enthalten, die Sie angegeben haben.'
							+   '</div>'
							+ '</div>'
							+ '<div class="spacer"></div>';
					}
					else {
						shop.innerHTML =
							  '<div class="box6ColInner">'
							+   '<div class="box3Col">'
							+     '<h1>Unfortunately no product could be found</h1>'
							+     'Please note that only such products are displayed that '
							+     'correspond to all the search terms that you have entered.'
							+   '</div>'
							+ '</div>'
							+ '<div class="spacer"></div>';
					}
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * search for item code in _private.data and insert the form required for the corresponding
		 * item into the DOM
		 */
		fastOrderSearch: function() {
			var input = document.getElementById('inputFastOrderCode');
			var code = (input != null) ? _private.trim(input.value.toString()) : '';
			var container = document.getElementById('boxFastOrderItem');
			var results = [];
			var data;
			var errType = '';
			var callbackErr = {
				success: function(o) {
					var errBox;

					container.innerHTML = o.responseText;
					errBox = YAHOO.util.Dom.getElementsByClassName('boxErr', 'div', container);

					for (var i=0; i<errBox.length; i++) {
						errBox[i].style.display = 'none';
					}
					errBox = document.getElementById('boxErr' + errType);

					if (errBox != null) {
						errBox.style.display = 'block';
					}
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting form timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFastErr.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackErr);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};
			var callbackBulk = {
				success: function(o) {
					container.innerHTML = o.responseText;
					_private.fastOrderBulk(data);
					Forms.initOverLabels();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting form timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFastBulk.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBulk);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};
			var callbackLiquid = {
				success: function(o) {
					container.innerHTML = o.responseText;
					_private.fastOrderLiquid(data);
					Forms.initOverLabels();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting form timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFastLiquid.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackLiquid);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};
			var callbackBundle = {
				success: function(o) {
					container.innerHTML = o.responseText;
					_private.fastOrderBundle(data);
					Forms.initOverLabels();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting form timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFastBundle.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBundle);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};
			var callbackRequest = {
				success: function(o) {
					container.innerHTML = o.responseText;
					_private.fastOrderRequest(data);
					Forms.initOverLabels();
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting form timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFastRequest.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackRequest);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};

			if (input != null) {
				// overwrite value by trimmed value
				input.value = code;
				input.blur();
			}
			if (code != '' && container != null) {
				// search item(s) with the given ERP code
				try {
					for (var i=0; i<_private.data.items.length; i++) {
						if (_private.data.items[i].code == code) {
							results[results.length] = _private.data.items[i];
						}
					}
				}
				catch (e) {}
				// evaluate search result
				if (results.length < 1) {
					errType = 'NotFound';
					YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFastErr.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackErr);
				}
				else if (results.length > 1) {
					errType = 'TooMany';
					YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFastErr.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackErr);
				}
				else {
					data = results[0];

					if (typeof data._7 == 'number' && data._7 != 0 && !Shop.getFullAccess()) {
						errType = 'PharmacyOnly';
						YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFastErr.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackErr);
					}
					else {
						yuiLoader.show();

						if (data.price <= 0.00) {
							YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFastRequest.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackRequest);
						}
						else {
							switch (data._9.toLowerCase()) {
								case 'kg':
									YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFastBulk.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBulk);
									break;
								case 'ltr':
									YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFastLiquid.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackLiquid);
									break;
								default:
									YAHOO.util.Connect.asyncRequest('POST', '/' + BB.getLangPath() + '_shop/getBasketFastBundle.html?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackBundle);
							}
						}
					}
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * get _private.displayMode
		 *
		 * return value:
		 *   the current value of _private.displayMode
		 */
		getDisplayMode: function() {
			return _private.displayMode;
		},
		/*-----------------------------------------------------------------------------------------
		 * determine whether the user is allowed access to pharmacy only items
		 *
		 * return value:
		 *   the value of _private.fullAccess
		 */
		getFullAccess: function() {
			return _private.fullAccess;
		},
		/*-----------------------------------------------------------------------------------------
		 * get ID of the current Node
		 *
		 * return value: ID of the current node
		 */
		getNodeId: function() {
			return _private.nodeId;
		},
		/*-----------------------------------------------------------------------------------------
		 * get JSON data, build array of items, and init shop display
		 *
		 * params:
		 *   nodeId (optional): ID of the node to be shown, if missing _private.nodeId will be used
		 *                      as ID
		 */
		init: function(nodeId) {
			var callbackItems = {
				success: function(o) {
					try {
						_private.data = YAHOO.lang.JSON.parse(o.responseText);

						if (_private.items.favorites.initialized) {
							if (nodeId) {
								_private.showNode(nodeId);
							}
							else {
								_private.showNode();
								//Content.loadBasketFast(); // initially load the fast order form
							}
							yuiLoader.hide();
						}
						else {
							YAHOO.util.Connect.asyncRequest('POST', '/_json/favorites.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackFavorites);
						}
					}
					catch (e) {
						DEBUG('failed to parse JSON file containing item data: ' + e.message);
						yuiLoader.hide();
						return;
					}
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('reading JSON file containing item data timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/' + BB.getLangPath() + 'shopData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackItems);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};
			var callbackFavorites = {
				success: function(o) {
					try {
						_private.items.favorites.codes = YAHOO.lang.JSON.parse(o.responseText).items;
						_private.generateFavorites();
						_private.filterItems(_private.items.favorites);
						_private.items.favorites.initialized = true;

						if (nodeId) {
							_private.showNode(nodeId);
						}
						else {
							_private.showNode();
							//Content.loadBasketFast(); // initially load the fast order form
						}
						yuiLoader.hide();
					}
					catch (e) {
						DEBUG('failed to parse JSON file containing favorites: ' + e.message);
						yuiLoader.hide();
						return;
					}
					yuiLoader.hide();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('getting JSON file containing favorites timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/favorites.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackFavorites);
					}
					else {
						yuiLoader.hide();
					}
				},
				timeout: 30000
			};

			if (!isShop) {
				return;
			}
			// register event listeners
			var searchField = document.getElementById('inputSearchItem');
			var favorites = document.getElementById('aPersNaviFavorites');
			var orders = document.getElementById('aPersNaviOrders');

			if (searchField) {
				YAHOO.util.Event.removeListener(searchField, 'change');
				YAHOO.util.Event.addListener(searchField, 'change', function() {
					searchField.value = _private.trim(searchField.value);

					if (searchField.value == '') {
						// load default node (remove comment signs to load previous node)
						_private.showNode(/*_private.nodeId*/);
					}
				});
			}
			if (favorites) {
				// add _public.toggleFilters() to default onclick event
				YAHOO.util.Event.removeListener(favorites, 'click');
				YAHOO.util.Event.addListener(favorites, 'click', function() {
					Content.loadFavorites();
					_public.toggleFilters(true);
				});
			}
			if (orders) {
				// add _public.toggleFilters() to default onclick event
				YAHOO.util.Event.removeListener(orders, 'click');
				YAHOO.util.Event.addListener(orders, 'click', function() {
					Content.loadOrders();
					_public.toggleFilters(true);
				});
			}
			_private.configureNavi();
			_private.configureItemFilters();

			// load item data and favorites
			if (document.getElementById('tbodyShop')) {
				yuiLoader.show();

				if (_private.data) {
					if (_private.items.favorites.initialized) {
						if (typeof nodeId != 'undefined') {
							_private.showNode(nodeId);
						}
						else {
							_private.showNode();
							//Content.loadBasketFast(); // initially load the fast order form
						}
						yuiLoader.hide();
					}
					else {
						YAHOO.util.Connect.asyncRequest('POST', '/_json/favorites.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackFavorites);
					}
				}
				else {
					YAHOO.util.Connect.asyncRequest('POST', '/_json/' + BB.getLangPath() + 'shopData.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callbackItems);
				}
			}
			else {
				Content.loadShop();
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * initialize panel used for showing information on special items, handling fees, and
		 * special conditions for large orders
		 */
		initPanels: function() {
			// general info panel used for special items, i.e. pharmacy only or organic items -----
			_private.panels.info = new YAHOO.widget.Panel('boxInfoPanel', {
				width: '400px',
				autofillheight: 'body',
				constraintoviewport: true,
				fixedcenter: true,
				close: false,
				visible: false,
				draggable: false,
				modal: true,
				underlay: 'none',
				zindex: 102
			});
			if (BB.getLang() == 'de') {
				_private.panels.info.setBody(
					  '<div id="boxInfoPharmacyOnly">'
					+   '<h1>Apothekenpflichtige Produkte</h1>'
					+   'Bitte beachten Sie, dass die Abgabe von apothekenpflichtigen '
					+   'Arzneidrogen nur erlaubt ist an: Apotheken, F\u00E4rbereien (bitte '
					+   'Best\u00E4tigung vorlegen, dass der jeweilige Artikel nur zum '
					+   'F\u00E4rben eingesetzt wird), Kosmetikhersteller (bitte Best\u00E4tigung '
					+   'vorlegen), Hochschulen f\u00FCr die Ausbildung von Studierenden der '
					+   'Pharmazie und Veterin\u00E4rmedizin, Pharmazeutische Unternehmer und '
					+   'Gro\u00DFh\u00E4ndler, Tier\u00E4rzte.'
					+   '<div class="doublespacer"></div>'
					+ '</div>'

					+ '<div id="boxInfoOrganic">'
					+   '<h1>Produkte aus kontrolliert-biologischem Anbau</h1>'
					+   'Kontrolliert-biologischer Anbau gem\u00E4\u00DF Artikel\u00A029, '
					+   'Absatz\u00A01 der Verordnung (EG) Nr.\u00A0834/2007, Codenummer '
					+   'DE-\u00D6ko-001'
					+   '<div class="doublespacer"></div>'
					+ '</div>'

					+ '<a class="aArrow" id="aInfoPanelClose">Fenster schlie\u00DFen</a>'

					+ '<div class="spacer"></div>'
				);
			}
			else {
				_private.panels.info.setBody(
					  '<div id="boxInfoPharmacyOnly">'
					+   '<h1>Pharmacy-only products</h1>'
					+   'Please note that the sale of pharmacy-only medicinal drugs is only '
					+   'offered to: pharmacies, dye works (please show confirmation that the '
					+    'corresponding item is for dyeing only), cosmetics manufacturers (please '
					+    'show confirmation), colleges for the further education of pharmacy and '
					+    'vetinary medicine, pharmaceutical entrepreneurs and distributors, vets.'
					+   '<div class="doublespacer"></div>'
					+ '</div>'

					+ '<div id="boxInfoOrganic">'
					+   '<h1>Products from controlled organic plantations</h1>'
					+   'Controlled organic plantation according to Article\u00A029, '
					+   'Paragraph\u00A01 of the Regulation (EU) No.\u00A0834/2007, Code Number '
					+   'DE-\u00D6ko-001'
					+   '<div class="doublespacer"></div>'
					+ '</div>'

					+ '<a class="aArrow" id="aInfoPanelClose">Close window</a>'

					+ '<div class="spacer"></div>'
				);
			}
			//_private.panels.info.render(YAHOO.util.Dom.get('containerPage'));
			_private.panels.info.render(document.body);
			YAHOO.util.Event.addListener('aInfoPanelClose', 'click', function() {
				_private.panels.info.hide();
			});
/*
			// handling fees panel ----------------------------------------------------------------
			_private.panels.fees = new YAHOO.widget.Panel('boxFeesPanel', {
				width: '400px',
				autofillheight: 'body',
				constraintoviewport: true,
				fixedcenter: true,
				close: false,
				visible: false,
				draggable: false,
				modal: true,
				underlay: 'none',
				zindex: 102
			});
			if (BB.getLang() == 'de') {
				_private.panels.fees.setBody(
					  '<h1>Handling Fee</h1>'
					+ 'Bei den auf den \u00DCbersichtsseiten angegebenen Preisen handelt es sich '
					+ 'um Kilopreise ab einer Gebindegr\u00F6\u00DFe von 5kg (ausgenommen '
					+ 'abgepackte Ware – hier gilt der Preis je Packung – und Fl\u00FCssigkeiten '
					+ '– hier gilt der Preis je Liter).'
					+ '<br>'
					+ '<br>'
					+ 'F\u00FCr kleinere Gebindegr\u00F6\u00DFen ab 1kg berechnen wir '
					+ 'zus\u00E4tzlich 1,50\u00A0\u20AC je kg. F\u00FCr Gebindegr\u00F6\u00DFen '
					+ 'von weniger als 1kg berechnen wir 2,10\u00A0\u20AC je Packung, – der '
					+ 'Zuschlag je kg entf\u00E4llt. Bitte beachten Sie auch, dass die '
					+ 'Mindestgr\u00F6\u00DFe f\u00FCr ein Gebinde 100g betr\u00E4gt.'
					+ '<br>'
					+ '<br>'
					+ 'Um Ihnen die \u00DCbersicht zu erleichtern haben wir f\u00FCr jeden '
					+ 'Artikel die Preise einiger h\u00E4ufiger Gebindegr\u00F6\u00DFen angegeben.'
					+ '<div class="doublespacer"></div>'

					+ '<a class="aArrow" id="aFeesPanelClose">Fenster schlie\u00DFen</a>'

					+ '<div class="spacer"></div>'
  				);
			}
			else {
				_private.panels.fees.setBody(
					  '<h1>Handling Fee</h1>'
					+ 'Prices shown on the overview pages are kilo prices for batch sizes from '
					+ '5kg (except packed goods – here the price is per pack – and liquids – here '
					+ 'the price is per liter).'
					+ '<br>'
					+ '<br>'
					+ 'For smaller batch sizes from 1kg we charge a further 1.50\u00A0\u20AC per '
					+ 'kg. For batches smaller than 1kg we charge  2.10\u00A0\u20AC per pack – '
					+ 'the surcharge per kg does not apply. Please note that the minimum batch '
					+ 'size is 100g.'
					+ '<br>'
					+ '<br>'
					+ 'To simplify the overview we have included the prices of some often chosen '
					+ 'batch sizes for each item.'
					+ '<div class="doublespacer"></div>'

					+ '<a class="aArrow" id="aFeesPanelClose">Close window</a>'

					+ '<div class="spacer"></div>'
  				);
			}
			//_private.panels.fees.render(YAHOO.util.Dom.get('containerPage'));
			_private.panels.fees.render(document.body);
			YAHOO.util.Event.addListener('aFeesPanelClose', 'click', function() {
				_private.panels.fees.hide();
			});
*/
			// price request panel ----------------------------------------------------------------
			_private.panels.request = new YAHOO.widget.Panel('boxRequestPanel', {
				width: '400px',
				autofillheight: 'body',
				constraintoviewport: true,
				fixedcenter: true,
				close: false,
				visible: false,
				draggable: false,
				modal: true,
				underlay: 'none',
				zindex: 102
			});
			_private.panels.request.setBody(
				  '<div id="boxFormRequest"></div>'
  			);
			//_private.panels.request.render(YAHOO.util.Dom.get('containerPage'));
			_private.panels.request.render(document.body);
			// special conditions panel -----------------------------------------------------------
			_private.panels.conditions = new YAHOO.widget.Panel('boxSpecialConditionsPanel', {
				width: '400px',
				autofillheight: 'body',
				constraintoviewport: true,
				fixedcenter: true,
				close: false,
				visible: false,
				draggable: false,
				modal: true,
				underlay: 'none',
				zindex: 102
			});
			_private.panels.conditions.setBody(
				  '<div id="boxFormSpecialConditions"></div>'
  			);
			//_private.panels.conditions.render(YAHOO.util.Dom.get('containerPage'));
			_private.panels.conditions.render(document.body);
		},
		/*-----------------------------------------------------------------------------------------
		 * execute an AND search for shop items
		 */
		searchItems: function() {
			var patterns = document.getElementById('inputSearchItem').value.toLowerCase().split(/\s+/);
			var _searchItems = function() {
				var count = 0;
				var found;

				YAHOO.util.Dom.removeClass(YAHOO.util.Dom.getElementsByClassName('selected', 'a', 'boxNaviLevel2Products'), 'selected');
				_private.items.search.all.length = 0;

				// search in item data
				for (var i=0; i<_private.data.items.length; i++) {
					var synonyms = [];

					found = true;

					for (var j=0; j<patterns.length; j++) {
						if (   (!_private.data.items[i]._6 || _private.data.items[i]._6.toLowerCase().indexOf(patterns[j]) < 0)   // _6 -> English/German name
						    && (!_private.data.items[i]._5 || _private.data.items[i]._5.toLowerCase().indexOf(patterns[j]) < 0)   // _5 -> Latin name
						    && (!_private.data.items[i].code || _private.data.items[i].code.toLowerCase().indexOf(patterns[j]) < 0)) {
							found = false;

							// search in synonyms
							if (_private.data.items[i].synonyms) {
								for (var k=0; k<_private.data.items[i].synonyms.length; k++) {
									if (_private.data.items[i].synonyms[k].toLowerCase().indexOf(patterns[j]) >= 0) {
										found = true;

										if (_private.findValueInList(_private.data.items[i].synonyms[k], synonyms) < 0) {
											synonyms[synonyms.length] = _private.data.items[i].synonyms[k];
										}
									}
								}
							}
							if (!found) {
								break;
							}
						}
					}
					if (found) {
						if (synonyms.length > 0) {
							_private.items.search.all[count++] = _private.generateItem(_private.data.items[i], synonyms);
						}
						else {
							_private.items.search.all[count++] = _private.generateItem(_private.data.items[i]);
						}
					}
					delete synonyms;
				}
				// display search results
				_public.setDisplayMode('search');
				_private.filterItems(_private.items.search);
				_private.items.current = _private.items.search;
				Content.loadSearchResults();
			};
			var callback = {
				success: function(o) {
					try {
						_private.synonyms = YAHOO.lang.JSON.parse(o.responseText).synonyms;

						// extend item data by synonyms
						for (var i=0; i<_private.synonyms.length; i++) {
							if (_private.synonyms[i].synonym) {
								for (var j=0; j<_private.synonyms[i].codes.length; j++) {
									for (var k=0; k<_private.data.items.length; k++) {
										if (_private.synonyms[i].codes[j] == _private.data.items[k].code) {
											if (_private.data.items[k].synonyms) {
												_private.data.items[k].synonyms[_private.data.items[k].synonyms.length] = _private.synonyms[i].synonym;
											}
											else {
												_private.data.items[k].synonyms = [];
												_private.data.items[k].synonyms[0] = _private.synonyms[i].synonym;
											}
										}
									}
								}
							}
						}
					}
					catch (e) {
						DEBUG('failed to parse JSON file containing favorites: ' + e.message);
						_private.synonyms = [];
					}
					yuiLoader.hide();
					_searchItems();
				},
				failure: function(o) {
					if (o.status == -1) {
						DEBUG('reading JSON file containing synonyms timed out - retrying');
						YAHOO.util.Connect.asyncRequest('POST', '/_json/' + BB.getLangPath() + 'synonyms.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
					}
					else {
						_private.synonyms = [];
						yuiLoader.hide();
						_searchItems();
					}
				},
				timeout: 30000
			};

			if (   !_private.data
				|| !patterns.length
			    || (patterns.length == 1 && !patterns[0])
				|| (patterns.length == 2 && !patterns[0] && !patterns[1])) {
				return;
			}
			if (_private.synonyms == null) {
				yuiLoader.show();
				YAHOO.util.Connect.asyncRequest('POST', '/_json/' + BB.getLangPath() + 'synonyms.json?xhr=' + BB.xhrCount() + '&' + _private.cookie, callback);
			}
			else {
				_searchItems();
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * set _private.displayMode
		 *
		 * params:
		 *   str: the value to set (allowed values: 'shop', 'search', 'favorites', 'basket',
		 *        'basketFast', 'order') - if the parameter is missing or if it has an another
		 *        value, the display mode will be set to 'shop'
		 */
		setDisplayMode: function(str) {
			if (typeof str == 'string') {
				if (_private.displayMode != str) {
					// keep paging settings to be used when returning from basket to shop
					if (   !(   _private.displayMode == 'shop'
					         || _private.displayMode == 'basket'
					         || _private.displayMode == 'basketFast')
					    || !(   str == 'shop'
					         || str == 'basket'
					         || str == 'basketFast')
					) {
						_private.pageNo = 1;
						_private.setLetter();
					}
					switch (str) {
						case 'shop':
						case 'search':
						case 'favorites':
						case 'basket':
						case 'basketFast':
						case 'order':
							_private.displayMode = str;
							break;
						default:
							_private.displayMode = 'shop';
					}
				}
			}
			else {
				if (_private.displayMode != 'shop') {
					_private.pageNo = 1;
					_private.setLetter();
					_private.displayMode = 'shop';
				}
			}
DEBUG(_private.displayMode);
		},
		/*-----------------------------------------------------------------------------------------
		 * allow/disallow access to pharmacy only items
		 *
		 * params:
		 *   fullAccess: flag indicating if full access is granted or not
		 */
		setFullAccess: function(fullAccess) {
			if (typeof fullAccess == 'boolean' && fullAccess == true) {
				_private.fullAccess = true;
			}
			else {
				_private.fullAccess = false;
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * toggle display of filters
		 *
		 * params:
		 * 		show: flag indicating if filters are to be shown or if they are to be hidden
		 */
		toggleFilters: function(show) {
			var boxGroups = document.getElementById('boxNaviLevel2Products');
			var boxGroupsBG = document.getElementById('boxNaviLevel2BGProducts');
			var boxGroupsContent = document.getElementById('boxNaviLevel2ContentProducts');
			var boxFilters = document.getElementById('boxNaviLevel3Products');

			if (typeof show == 'boolean' && boxGroups && boxGroupsBG && boxGroupsContent && boxFilters) {
				if (show) {
					boxGroupsContent.style.width = '499px';
					boxGroupsBG.style.width = '511px';
					boxGroups.style.width = '511px';
					boxFilters.style.display = 'block';
				}
				else {
					boxFilters.style.display = 'none';
					boxGroups.style.width = '639px';
					boxGroupsBG.style.width = '639px';
					boxGroupsContent.style.width = '627px';
				}
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * update a single row of an item detail page (total weight, single price, total price)
		 *
		 * params:
		 *   rowNo: the no of the row to update
		 *   count: number of bundles ordered (number or string)
		 *   weight: the weight of a single bundle (number or string)
		 *   price5: price per kg if at least 5kg ordered (number (!))
		 */
		updateItemDetailRowBulk: function(rowNo, count, weight, price5) {
			if (!BB.isNatural(rowNo)) {
				return;
			}
			// create numerical values from strings passed to the function
			var weightTotal;
			var priceSingle = parseFloat(_public.calculatePriceSingleBulk(count, weight, price5).replace(',', '.'));
			var priceTotal;

			if (typeof count == 'string') {
				count = _public.checkInputCount(count, '');

				if (count == '') {
					count = 0;
				}
				else {
					count = parseInt(count);
				}
			}
			else if (typeof count != 'number') {
				count = 0;
			}
			if (typeof weight == 'string') {
				weight = _public.checkInputWeight(weight, '').replace(',', '.');

				if (weight == '') {
					weight = 0.0;
				}
				else {
					weight = parseFloat(weight);
				}
			}
			else if (typeof weight != 'number' || weight < 0.1) {
				weight = 0.0;
			}
			if (typeof price5 != 'number' || price5 < 0) {
				price5 = 0.0;
			}
 			// handle numerical values
			weightTotal = weight * count;
			priceTotal = priceSingle * count;

			// create strings from numerical values for output to HTML
			weightTotal = weightTotal.toFixed(3);

			if (weightTotal.match(/\./)) {
				weightTotal = weightTotal.replace(/0+$/, '').replace(/\.$/, '');
			}
			priceSingle = priceSingle.toFixed(2);
			priceTotal = priceTotal.toFixed(2);

			if (BB.getLang() == 'de') {
				weightTotal = weightTotal.replace('.', ',');
				priceSingle = priceSingle.replace('.', ',');
				priceTotal = priceTotal.replace('.', ',');
			}
			// output of values calculated above
			var spanWeightTotal = document.getElementById('spanWeightTotal' + rowNo);
			var boxPriceSingle = document.getElementById('boxPriceSingle' + rowNo);
			var boxPriceTotal = document.getElementById('boxPriceTotal' + rowNo);

			if (spanWeightTotal) {
				spanWeightTotal.innerHTML = weightTotal;
			}
			if (boxPriceSingle) {
				boxPriceSingle.innerHTML = priceSingle;
			}
			if (boxPriceTotal) {
				boxPriceTotal.innerHTML = priceTotal;
			}
		},
		/*-----------------------------------------------------------------------------------------
		 * update a single row of an item detail page (total weight, single price, total price)
		 *
		 * params:
		 *   rowNo: the no of the row to update
		 *   count: number of bundles ordered (number or string)
		 *   weight: the volume of a single bundle (number or string)
		 *   price: price per liter (number (!))
		 */
		updateItemDetailRowLiquid: function(rowNo, count, weight, price) {
			if (!BB.isNatural(rowNo)) {
				return;
			}
			// create numerical values from strings passed to the function
			var weightTotal;
			var priceSingle;
			var priceTotal;

			if (typeof count == 'string') {
				count = _public.checkInputCount(count, '');

				if (count == '') {
					count = 0;
				}
				else {
					count = parseInt(count);
				}
			}
			else if (typeof count != 'number') {
				count = 0;
			}
			if (typeof weight == 'string') {
				weight = _public.checkInputCount(weight, '').replace(',', '.');

				if (weight == '') {
					weight = 0;
				}
				else {
					weight = parseInt(weight);
				}
			}
			else if (typeof weight != 'number' || weight < 1) {
				weight = 0;
			}
			if (typeof price != 'number' || price < 0) {
				price = 0.0;
			}
			priceSingle = _private.roundPrice(weight * price);

 			// handle numerical values
			weightTotal = weight * count;
			priceTotal = priceSingle * count;

			// create strings from numerical values for output to HTML
			weightTotal = weightTotal.toFixed(3);

			if (weightTotal.match(/\./)) {
				weightTotal = weightTotal.replace(/0+$/, '').replace(/\.$/, '');
			}
			priceSingle = priceSingle.toFixed(2);
			priceTotal = priceTotal.toFixed(2);

			if (BB.getLang() == 'de') {
				weightTotal = weightTotal.replace('.', ',');
				priceSingle = priceSingle.replace('.', ',');
				priceTotal = priceTotal.replace('.', ',');
			}
			// output of values calculated above
			var spanWeightTotal = document.getElementById('spanWeightTotal' + rowNo);
			var boxPriceSingle = document.getElementById('boxPriceSingle' + rowNo);
			var boxPriceTotal = document.getElementById('boxPriceTotal' + rowNo);

			if (spanWeightTotal) {
				spanWeightTotal.innerHTML = weightTotal;
			}
			if (boxPriceSingle) {
				boxPriceSingle.innerHTML = priceSingle;
			}
			if (boxPriceTotal) {
				boxPriceTotal.innerHTML = priceTotal;
			}
		}
	};
	return _public;
}();

YAHOO.util.Event.onDOMReady(function() {
	Shop.initPanels();
	Shop.init();
});

