// mmsuggest.js: Suggestion dropdown box for HTML.
// Copyright exorbyte GmbH, 2005, 2006. All rights reserved.
// Author: Leo Meyer, leo.meyer_at_exorbyte.com
// Version: 5.5, 15.02.07
//
// Usage: see demo.
//
// Veraenderungen an diesem Code sind nur mit ausdruecklicher Zustimmung der 
// exorbyte GmbH gestattet.
// Modification of this code only with explicit permission by exorbyte GmbH.

// Flexform-Suggest:
// Author: Martin Schall, martin.schall_at_exorbyte.com

var exorbyteLogo = "exlogo_tiny.gif";		// path to exorbyte logo, not affected by iconPath
var mm_refcnt = 0;
var mm_inputs = new Array();
var flex_orig_input = null;
var flex_known_params = new Array();
var flex_main_span = null;
var flex_tab_span = null;
var flex_count_span = null;
var flex_num_hits = -1;
var flex_ab_price = -1;
var flex_cursor_pos = 0;
var flex_redirect = false;
var flex_sugg_mode = false;
var flex_timeout;
var flex_free_suggest_timer; 
var flex_special_status = null;
var flex_nothing_found = false;
var flex_moved_tabs = false;
var flex_act_max_tab_width = 0;
var flex_autoselect_only = false;

var mmUA = navigator.userAgent;
var firefox = mmUA.search(/firefox/i) >= 0;
var opera = mmUA.search(/Opera/i) >= 0;
var ie = mmUA.search(/MSIE/i);
if (ie >= 0) {
	var ieVersion = mmUA.substr(ie + 4, 3);
	ie = true;
} else ie = false;
var ieZIndexBug = ie && (parseInt(ieVersion) < 7.0);
var mm_flashtime = 50; 		// ms
var mm_qtime = 0;
var mmIgnoreFirstMouseEnter = false;	// firefox behaviour workaround: ignore first mouse enter when displaying

// bit flags
var AS_NOLOGO = 1;			// don't display exorbyte logo - only for licensed users
var AS_NOSUBMIT = 2;		// don't do a form commit on return while the suggest box is open
var AS_TABSELECTS = 4;		// pressing TAB activates the selected entry
var AS_DISPLAY_INPUT = 8;	// adds a header row containing the user's input
var AS_HIERARCHICAL = 16;	// display suggestions in hierarchical mode
var AS_GROUPED = 32;		// display suggestions in grouped mode (category grouping), useful for one-level categories
var AS_GROUPED_DISPLAYCAT = 64;		// display suggestions in grouped mode (category grouping) plus category labels, useful for |-separated subcategory labels
var AS_GENERATED_CATEGORIES_NOT_SELECTABLE = 128;	// HIERARCHICAL and GROUPED categories are not selectable if this flag is set
var AS_NO_ROOT = 256;		// don't display root node in grouped and hierarchical modes

/** Parameter container for flexible parameter handling */
function mmSuggestParams() {
	// normal suggest parameters
	this.requestURL = "";			// URL for the server side part	
	this.iconPath = "images/";		// relative or absolute path to icons, must end with "/" if not empty; override this in template modules
	this.width = -1;				// if -1, width is calculated automatically; can be set in the setup function
	this.top = -1;				// fixed absolute top position (-1: dynamic)
	this.left = 0;				// left position (dependent on the value of align)
	this.align = "left";			// default: left alignment	
	this.letterLimit = 2;				// if the length of the input is less than this number, no request is made
	this.searchDelay = 750;			// ms until search is started
	this.normalfg = "black";			// default normal foreground
	this.normalbg = "white";			// default normal background
	this.highlightfg = "white";			// default highlighted foreground
	this.highlightbg = "navy";			// default highlighted background
	this.debug = false;				// enables component-specific debugging
	this.overlappedObjects = null;		// Array of SELECT combobox objects that may be hidden by the dropdown (IE display bug workaround)
	this.document = window.document;		// target document object, default: current document
	this.valueField = 0;					// field containing the search value
	this.searchValueObject = null;			// object for search value
	this.beforeRequest = function(target) { return true; };		// function that is called before a request is made; cancel request by returning false
	this.inputTitle = "Ihre Eingabe";		// text that is displayed with the input if AS_DISPLAY_INPUT is set
	this.clickoutField = 0;			// field that contains the value that should be used for the clickout logging	
	this.hierarchicalSearchTermIndicator = "Suchbegriff";		// abstract search term denominator
	this.suggBoxTop = function(iDiv) { return false; };		// function that sets HTML for the top DIV in the suggestion box
	this.suggBoxBottom = function(iDiv) { return false; };		// function that sets HTML for the bottom DIV in the suggestion box
	this.suggBoxLeftFrameHTML = function() { return ""; };		// function that returns HTML for the left frame part of a suggRow
	this.suggBoxRightFrameHTML = function() { return ""; };		// function that returns HTML for the right frame part of a suggRow	
	this.navigationBar = null;			// function(target, nDiv, page, maxPages) to generate navigation bar HTML
	this.nocache = false;				// bypass browser caching
	
	// parameters overwritten by the flexform-suggest (USELESS TO SET MANUALLY!!!)
	this.onActivate = null;			// custom function(input, row) that is activated onClick or on enter key; return false to suppress commit
	this.clickout = false;			// set to true for clickout logging
	this.flags = 0;				// a combination of the AS_ flags
	this.oneColumn = false;			// force one column display
	this.headerFunction = null;			// custom function(target, iDiv, rows) returns header HTML for a set of rows. Parent is the parent DIV element to be filled
	this.rowFunction = null;			// custom function(target, rows, fieldIndex, row, iDiv) sets iDiv's inner HTML. fieldIndex is an array that contains the indexes of field names. Returns (boolean)addEvents.
	this.footerFunction = null;			// custom function(target, iDiv, rows) returns footer HTML for a set of rows. Parent is the parent DIV element to be filled
	this.preFunction = null;			// preprocessing function(target, rows) returns rowArray
	this.navigationPos = "top";		// place to show the navigation bar. possible values are "top", "bottom" and "both"
	this.pageSize = 0;					// if this value is greater than 0, page navigation is activated
	this.initialWidth = 0;
	
	// flexform-suggest parameters
	this.flexTreshold = 500;	// how many hits should there be to allow redirect
	this.flexCursorColor = "#000000";	// the color of the cursor
	this.flexCountDisplay = "suggest";	// where the count-display should be drawed (suggest, input, both, none)
	this.flexCountTitle = "Number of hits:"; // the title to show in the count view
	this.flexCountTreshold = 5000;	// the threshold under which we show the exact context size
	this.flexSortTabs = false;	// sort the tabs before displaying them
	this.flexCompressTabs = true; // compress tabs
	this.flexMinWidth = 350;	// the minimum width of the suggest box
	this.flexMaxWidth = 500;	// the maximum width of the suggest box
	this.flexFreeSuggestModeTime = 3000;	// ms until the free suggest mode ("Verfeinerung") is activated, 0 deactivates it
	this.flexRedirectFunction = null;	// the redirect function for the flexform-suggest: submit(fieldnames, rows, totalSuggCount)
	this.flexHelpText = null; // the help text to show in the help suggest box
	this.flexHelpUrl = null; // the help url to redirect
	this.flexTranslationTable = null; // the table with all translations for this language
	this.flexInboxHelpTreshold = 0; // threshold over which we show the inbox-help
	this.flexShowHitsText = "Show hits"; // the text to display when redirecting to hits
	this.flexFooterHelpText = null; // help text to show in the footer of the suggest box
	this.flexHighlightFirst = false; // Hightlight the first entry of the suggest box
	this.flexStatusBar = true; // activate the status bar in the suggest
	this.flexHighlightColor = null; // the color to highlight localized input
	this.flexBeginnerTimeout = 0; // timeout in ms until the beginners help appears (0 deactivates it)
	this.flexBeginnerText = null; // text to display in the beginners help (null deactivates it)
	this.flexTabColor = "#000000"; // the font-color of the tabs
	this.flexTabBackgroundColor = "#D4D0C8"; // the background-color of tabs (only when not using inline tabs)
	this.flexMaxTabWidth = 300; // max width of the tab span in px
	this.flexNoHitsText = null; // text to show in status bar when no hits found (null deactivates)
	this.flexInlineTabs = false; // tabs are displayed inline
	this.flexAlwaysHierarchical = true; // always use the hierarchical display
	this.flexProgressbar = true; // show a animated progress bar while waiting for a response
	this.flexPageSize = 0; // display hits in pages. 0 deactivates it
	this.flexNoContext = "No hit in context: "; // prefix for hits with no context
	this.flexZIndex=1; //
	return 0;
}

// consts used in navig and selection contexts
var MM_DOWNDIR = false;
var MM_UPDIR = true;

////////// helper functions ///////////

// String extensions
String.prototype.startsWith = function(s) {
	if (Number(s.length) > Number(this.length)) return false;
	return this.substring(0, s.length) == s;
}
String.prototype.trim = function() {
	return this.replace(/^\s+/, "").replace(/\s+$/, "");
};

String.prototype.ltrim = function(chars) {
	chars = chars || "\\s";
	return this.replace(new RegExp("^[" + chars + "]+", "g"), "");
};

function sortFirst(a, b) {
	// expects a, b to be arrays
	if (a[0] > b[0]) 
		return 1;
	else if (a[0] < b[0])
		return -1;
	else return 0;
}

function sortLengthLonger(a, b) {
	// expects a, b to be strings
	// sort longer ones first
	return b.length - a.length;
}

function sortLengthShorter(a, b) {
	// expects a, b to be strings
	// sort shorter ones first
	return a.length - b.length;
}

//////// helper functions end //////////

////////// flexform suggest (GENERIC FUNCTIONS) ///////////

// workaround to set attributes on different browsers
function flexSetAttribute(node, name, value) {
	var uAgent = navigator.userAgent;
	if (uAgent.search(/firefox/i) >= 0) {
		// Mozilla Firefox
		var tmpAttr = document.createAttribute(name);
		tmpAttr.nodeValue = value;
		node.setAttributeNode(tmpAttr);
	}
	if (uAgent.search(/Opera/i) >= 0) {
		// Opera
		var tmpAttr = document.createAttribute(name);
		tmpAttr.nodeValue = value;
		node.setAttributeNode(tmpAttr);
	}
	if (uAgent.search(/MSIE/i) >= 0) {
		// Internet Explorer
		
		// on internet explorer we can't set the style-attribute directly
		// another workaround...
		if (name.toLowerCase() == "style") {
			node.style.cssText = value;
		}
		else {
			node.setAttribute(name, value);
		}
	}
}

// inits the flexform-suggest and builds up extra html-structures
function flexInit(target) {
	var tabStyle = "";	
	var uAgent = navigator.userAgent;
	if (uAgent.search(/firefox/i) >= 0) {
		// Mozilla Firefox
		tabStyle = "float:left;";
	}
	if (uAgent.search(/Opera/i) >= 0) {
		// Opera (same as Firefox until Workaround is needed...)
		tabStyle = "float:left;";
	}
	if (uAgent.search(/MSIE/i) >= 0) {
		// Internet Explorer
	}
	
	flex_orig_input = target;
	target.blur();
	
	// disable autocomplete on the input
	flexSetAttribute(target, "autocomplete", "off");
	
	// search for the form-tag
	var parent = target.parentNode;
	while (parent.nodeName.toLowerCase() != "form") {
		parent = parent.parentNode;
	}
	
	// get the position and size of the input field
	//var top = mmGetParentProps(target, "offsetTop")+2;
	//var left = mmGetParentProps(target, "offsetLeft")+3;
	var top = 3;
	var left = 3;
	var height = target.offsetHeight;
	var width = target.offsetWidth;
	
	// remove the action-attribute from parent
	parent.removeAttribute("action");
	flexSetAttribute(parent, "action", "javascript:flexSubmit();");
	
	// get the attributes set in the input field
	var css = null;
	var size = null;
	for (var z = 0; z < target.attributes.length; z++) {
		if (target.attributes[z].nodeName.toLowerCase() == "size") {
			size = target.attributes[z].nodeValue;
		}
	}
	
	// create and add the main span
	var mainSpan = document.createElement("span");
	parent.appendChild(mainSpan);
	mainSpan.className = "fMainSp";
	flexSetAttribute(mainSpan, "style", "top:" + top + "px;left:" + left + "px;height:" + (height*2) + "px;width:" + width + "px;");
	flex_main_span = mainSpan;
	
	// add a larger span tag
	var largeSpanTag = document.createElement("span");
	mainSpan.appendChild(largeSpanTag);
	largeSpanTag.className = "fSearchSp";
	flexSetAttribute(largeSpanTag, "style", "height:" + (height*2) + "px;width:" + (width*10) + "px;");
	
	// create and add the tab span
	var tabSpan = document.createElement("span");
	largeSpanTag.appendChild(tabSpan);
	tabSpan.className = "fTabSp";
	flexSetAttribute(tabSpan, "style", tabStyle);
	flex_tab_span = tabSpan;
	
	// create and add the flex input
	var flexInput = document.createElement("input");
	largeSpanTag.appendChild(flexInput);
	flexInput.id = "flexinput";
	flexSetAttribute(flexInput, "autocomplete", "off");
	flexSetAttribute(flexInput, "type", "text");
	flexSetAttribute(flexInput, "size", size);
	
	// create and add the count span
	var countSpan = document.createElement("span");
	mainSpan.appendChild(countSpan);
	countSpan.className = "fCountSp";

	flex_count_span = countSpan;
	
	mmSetDivSize(flexInput);
	
	flexInput.fieldnames = new Array();
	
	var qpos = location.href.search(/[\\?&]/);
	if(qpos >= 0) {
		sDecode(location.href.substr(qpos+1));
	}
	
	return flexInput;
}

// scrolls the tabs left
function flexScrollTabsLeft() {
	var old_params = flex_known_params;
	flex_known_params = new Array();
	
	if (old_params.length <= 0) return;
	
	for (var z = 1; z < old_params.length; z++) {
		flex_known_params.push(old_params[z]);
	}
	flex_known_params.push(old_params[0]);
	
	flexFillTabs();
}

// scrolls the tabs right
function flexScrollTabsRight() {
	var old_params = flex_known_params;
	flex_known_params = new Array();
	
	if (old_params.length <= 0) return;
	
	flex_known_params.push(old_params[old_params.length-1]);
	for (var z = 0; z < (old_params.length-1); z++) {
		flex_known_params.push(old_params[z]);
	}	
	
	flexFillTabs();
}

// moves the tab-span left if it's too large
function flexMoveTabs() {
	var tabs = flex_tab_span;
	var input = mm_inputs[0];
	var uAgent = navigator.userAgent;
	
	if (flex_cursor_pos >= 0 && flex_cursor_pos < flex_tab_span.childNodes.length) {
		var debug_cursor = flex_cursor_pos;
		var debug_selected_tab = mmGetParentProps(flex_tab_span.childNodes[flex_cursor_pos], "offsetLeft");
		var debug_all_tabs = mmGetParentProps(flex_main_span, "offsetLeft");
	}
	
	// calculate the required width of the tab span
	var tabOffset = 104;
	var width = (tabOffset + 4) * flex_tab_span.childNodes.length;
/* 	for (var z = 0; z < flex_tab_span.childNodes.length; z++) { */
/* 		width += flex_tab_span.childNodes[z].offsetWidth + 4; */
/* 	} */

	// calculate the offset for moving the tabs
	var offset = 0;
	if (width > flex_act_max_tab_width) {
		offset = width - flex_act_max_tab_width;
	}
	
	// check if we need to move and set a flag to use this info in other functions
	flex_moved_tabs = false;
	if (offset != 0) {
		flex_moved_tabs = true;
	}	
	
   // calculate the new width of the input field
   var newWidth = 5 + Math.min(tabs.offsetWidth, flex_act_max_tab_width) + flex_count_span.offsetWidth;
   newWidth = input.parameters.initialWidth - newWidth;
   
	// now move the tabs
	if (uAgent.search(/firefox/i) >= 0) {
		// Mozilla Firefox
		flexSetAttribute(tabs, "style", "width:" + width + "px;display:inline;float:left;position:relative;right:" + offset + "px;");
		flexSetAttribute(input, "style", "display:inline;border:none;background-color:transparent;position:relative;right:" + offset + "px;max-width:" + newWidth + "px;");
	}
	if (uAgent.search(/Opera/i) >= 0) {
		// Opera (same as Firefox until Workaround is needed...)
		flexSetAttribute(tabs, "style", "width:" + width + "px;display:inline;float:left;position:relative;right:" + offset + "px;");
		flexSetAttribute(input, "style", "display:inline;border:none;background-color:transparent;position:relative;right:" + offset + "px;max-width:" + newWidth + "px;");
	}
	if (uAgent.search(/MSIE/i) >= 0) {
		// Internet Explorer
		if (input.parameters.flexInlineTabs == false) {
			flexSetAttribute(tabs, "style", "width:" + width + "px;display:inline;position:absolute;left:" + (offset*-1) + "px;");
			flexSetAttribute(input, "style", "display:inline;border:none;background-color:transparent;position:relative;left:" + (tabs.offsetWidth-offset) + "px;max-width:" + newWidth + "px;");
		} else {
			flexSetAttribute(tabs, "style", "width:" + width + "px;display:inline;position:relative;right:" + offset + "px;");
			flexSetAttribute(input, "style", "display:inline;border:none;background-color:transparent;position:relative;right:" + offset + "px;max-width:" + newWidth + "px;");
		}
	}
}

// generates the count text to show
function flexGenCountText() {	
	var target = mm_inputs[0];
	var cText = "";
	if (Number(flex_num_hits) == -1) {
		cText = "";
	}
	else if (Number(flex_num_hits) > target.parameters.flexCountTreshold) {
		cText = target.parameters.flexCountTitle + " >" + target.parameters.flexCountTreshold;
	}
	else {
		cText = target.parameters.flexCountTitle + " " + flex_num_hits;
	}
	return cText;
}

// generates the count text to show
function flexGenPriceText() {	
	var target = mm_inputs[0];
	var cText = "";
	if (Number(flex_ab_price) == -1) {
		cText = "";
	}
	else {
		cText = "ab " + flex_ab_price + " &euro;";
	}
	return cText;
}

// draws the context-size in html view
function flexShowCount() {
	// remove all children from the count span
	while (flex_count_span.hasChildNodes()) {
		flex_count_span.removeChild(flex_count_span.lastChild);
	}
	
	// generate the count text
	//var cText = flexGenCountText();

	// generate the 'ab-preis' text
	var cText = flexGenPriceText();

	// create and add the count text
	if (target.parameters.flexCountDisplay == "input" || target.parameters.flexCountDisplay == "both") {
		var cNode = document.createElement("span");
		cNode.innerHTML = "<nobr>" + cText + "<nobr>";
		flex_count_span.appendChild(cNode);
	}
}

// sorts the fixed values with bubblesort
// bubblesort should be no problem, because it runs on client and with very small arrays
function flexSortFixed() {
	var swapped;
	for (var i = 1; i < flex_known_params.length; i++) {
		swapped = false;
		
		for (var j = 0; j < (flex_known_params.length-i); j++) {
			var first = flex_known_params[j];
			var second = flex_known_params[j+1];
			
			// swap if needed
			if (first[0] > second[0] || (first[0] == second[0] && first[1] > second[1])) {
				flex_known_params[j] = second;
				flex_known_params[j+1] = first;
				swapped = true;
			}
		}
		
		// break if we have a sorted heap
		if (!swapped) {
			break;
		}
	}
}

// generates the html-tag for a tab
function flexDrawInlineTab(kategorie, name, drawshort, drawcursor) {
	var target = mm_inputs[0];
	
	var style = "color:" + target.parameters.flexTabColor + ";";
	if (drawcursor) {
		style += "background-color:" + target.parameters.flexCursorColor + ";";
	}
	
	var text = "";
	re = /\_/g;
	if (!drawshort) {
		text = kategorie.replace(re, " ") + "=" + name;
	}
	else {
		text = "+" + name;
	}
	
	var tmpNode = document.createElement("span");	
	tmpNode.innerHTML = "<nobr><span style='" + style + "'>"
		+"<a href=\"javascript:flexRemoveFixed('" + kategorie + "')\">"
		+"<span style='color:" + target.parameters.flexTabColor + ";font-weight:bold;'>" + text + "</span>"
		+"</a></span></nobr>&nbsp;";
	
	return tmpNode;
}

// generates the html-tag for a tab
function flexDrawTab(kategorie, name, drawshort, drawcursor) {
	var target = mm_inputs[0];	
	
	var tmpDiv = document.createElement("div");
	tmpDiv.className = "fTabDv";
	
	var kategorieColor = target.parameters.flexTabBackgroundColor;
	if (drawcursor) {
		kategorieColor = target.parameters.flexCursorColor;
	}
	
	var uAgent = navigator.userAgent;
	var nameDiv = document.createElement("div");
	var nameStyle = "color:" + target.parameters.flexTabColor + ";";
	nameDiv.className = "fTabValueDv";
	if (uAgent.search(/firefox/i) >= 0) {
		// Mozilla Firefox
		flexSetAttribute(nameDiv, "style", "color:" + target.parameters.flexTabColor + ";");
	}
	if (uAgent.search(/Opera/i) >= 0) {
		// Opera
		flexSetAttribute(nameDiv, "style", "color:" + target.parameters.flexTabColor + ";");
	}
	if (uAgent.search(/MSIE/i) >= 0) {
		// Internet Explorer
		flexSetAttribute(nameDiv, "style", "color:" + target.parameters.flexTabColor + ";" + "padding-bottom:2px;");
	}
	nameDiv.innerHTML = removeAliases(name);

	re = /\_/g;

	var kategorieDiv = document.createElement("div");
	kategorieDiv.className = "fTabNameDv";
	kategorieDiv.innerHTML = kategorie.replace(re, " ") + "&nbsp;" + "<a href=\"javascript:flexRemoveFixed('" + kategorie + "')\"><img alt='x' align='right' style='margin-top:-14px;' src='" + target.parameters.iconPath + "close.png'></a>";
	
	var kategorieStyle = "background-color:" + kategorieColor + ";";
	flexSetAttribute(kategorieDiv, "style", kategorieStyle);
	
	tmpDiv.appendChild(nameDiv);
	tmpDiv.appendChild(kategorieDiv);
	
	return tmpDiv;
}

// fills the tab span with all selected values
function flexFillTabs() {
	// remove all childs from the tab span
	while (flex_tab_span.hasChildNodes()) {
		flex_tab_span.removeChild(flex_tab_span.lastChild);
	}
	
	// sort the fixed values
	var target = mm_inputs[0];
	if (target.parameters.flexSortTabs == true) {
		flexSortFixed();
	}
		
	// create and add the tabs
	for (var z = 0; z < flex_known_params.length; z++) {
		// draw the tab
		var param = flex_known_params[z];
		var drawshort = (target.parameters.flexCompressTabs==true && z>=1 && flex_known_params[z-1][0]==param[0]);
		var drawcursor = (flex_cursor_pos == z);
		
		var tab = null;
		if (target.parameters.flexInlineTabs) {
			tab = flexDrawInlineTab(param[0], param[1], drawshort, drawcursor);
		} else {
			tab = flexDrawTab(param[0], param[1], drawshort, drawcursor);
		}
		
		if (tab != null) {
			flex_tab_span.appendChild(tab);
		}
	}
	
	flexMoveTabs();
	mmSetDivSize(target);	
	target.focus();
}

// removes a selected value by a given key (kategorie)
// redraws the tab- and count-views
function flexRemoveFixed(kategorie) {
	var target = mm_inputs[0];
	var old_params = flex_known_params;
	flex_known_params = new Array();
	
	// reset the tab scrolling
	flex_act_max_tab_width = target.parameters.flexMaxTabWidth;
	
	// copy all params but the selected one
	for (var z = 0; z < old_params.length; z++) {
		var param = old_params[z];
		if (param[0] != kategorie) {
			flex_known_params.push(param);
		}
		else {
			if (flex_cursor_pos > z) {
				flex_cursor_pos--;
			}
			flex_num_hits = -1;
			flex_ab_price = -1;
		}
	}
	
	flexShowCount();
	flexFillTabs();
	flexSetInputValue();
	target.focus();	
	flexDoFreeSuggest(true);
}

// removes a selected value by a given position within the array
// redraws the tab- and count-views
function flexRemoveFixedPos(pos) {
	var target = mm_inputs[0];
	var old_params = flex_known_params;
	flex_known_params = new Array();
	
	// reset the tab scrolling
	flex_act_max_tab_width = target.parameters.flexMaxTabWidth;
	
	// copy all params but the selected one
	for (var z = 0; z < old_params.length; z++) {
		if (Number(pos) != Number(z)) {
			flex_known_params.push(old_params[z]);
		}
	}
	
	// move the cursor if necessary
	if (flex_cursor_pos > pos) {
		flex_cursor_pos--;
	}
	
	if (pos < old_params.length) {
		flex_num_hits = -1;
		flex_ab_price = -1;
	}
	
	flexShowCount();
	flexFillTabs();
	flexSetInputValue();
	target.focus();
	flexDoFreeSuggest(true);
}

// checks if a given field is already fixed
function flexCheckFixed(kategorie) {
	// check if a kategorie has allready been fixed
	for (var z = 0; z < flex_known_params.length; z++) {
		var param = flex_known_params[z];
		if (param[0] == kategorie) {
			return true;
		}
	}
	
	// check with the translated kategorie
	var trans = flexTranslate(kategorie, "internal");
	for (var z = 0; z < flex_known_params.length; z++) {
		var param = flex_known_params[z];
		if (param[0] == trans) {
			return true;
		}
	}
	
	return false;
}

// return the first value relating to a given key
function flexGetFixed(kategorie) {
	for (var z = 0; z < flex_known_params.length; z++) {
		var param = flex_known_params[z];
		if (param[0] == kategorie) {
			return param[1];
		}
	}	
	return null;
}

// returns the first alias from a given field within a matchmaker result
function flexGetField(fieldnames, rows, field) {
	for (var z = 0; z < fieldnames.length; z++) {
		if (fieldnames[z].toLowerCase() == field.toLowerCase()) {
			return rows[0][z].split("|")[0];
		}
	}
	
	var trans = flexTranslate(field, "internal");
	for (var z = 0; z < fieldnames.length; z++) {
		if (fieldnames[z].toLowerCase() == trans.toLowerCase()) {
			return rows[0][z].split("|")[0];
		}
	}
	
	return null;
}

// returns an array with all the values relating to a given key
function flexGetFixedValue(field) {
	var tmp = new Array();
	for (var z = 0; z < flex_known_params.length; z++) {
		var param = flex_known_params[z];
		if (param[0] == field) {
			tmp.push(param[1]);
		}
	}
	
	var trans = flexTranslate(field, "internal");
	for (var z = 0; z < flex_known_params.length; z++) {
		var param = flex_known_params[z];
		if (param[0] == trans) {
			tmp.push(param[1]);
		}
	}
	
	return tmp;
}

// parses the remaining unknowns sequence and builds up a clean list
function flexParseRemainingUnknowns(r_string) {
	var r_list = r_string.split(" ");
	var unknowns = new Array();
	var target = mm_inputs[0];
	var i_list = target.value.split(" ");
	
	for (var z = 0; z < i_list.length; z++) {
		if (z < r_list.length) {
			if (r_list[z] == "{}") {
				continue;
			}		
			if (r_list[z].match(/\d+/)) {
				var end = Number(r_list[z]);
				if (end < r_list.length) {
					var start = Number(r_list[end]);
					if (start == z) {
						z = end;
						continue;
					}
				}
			}
		}
		unknowns.push(i_list[z]);
	}
	
	return unknowns.join(" ");
}

// function for handling the autoselect-values
function flexFastSelect(rows) {
	var target = mm_inputs[0];
	var new_rows = new Array();
	var remaining_unknowns = "";
	
	// iterate over results to find autoselect-values
	var autoselect = false;
	for (var z = 0; z < rows.length; z++) {
		var row = rows[z];
		
		if (row[0].search(/[\<\>]\d+/g) == -1) {
			row[0] = row[0].replace(/[\<\>]/g, "");
		}
		
		// check for autoselect
		if (row[3] == "true") {
			if (Number(flex_num_hits) == -1 || Number(row[2]) < Number(flex_num_hits)) {
				flex_num_hits = Number(row[2]);
			}
			flex_known_params.push(new Array(row[1], row[0].split("|").join("")));
			autoselect = true;
			
			// get the remaining unknown from the result
			if (row[4] != "") {
				remaining_unknowns = flexParseRemainingUnknowns(row[4]);
			}
		}
		else {
			new_rows.push(row);
		}
	}
	
	if (autoselect) {
		target.value = remaining_unknowns;
	}
	target.rows = null;
	
	
	// update the general view
	flex_cursor_pos = flex_known_params.length;		
	flexFillTabs();
	flexShowCount();	
	
	// set the timeout for the suggest mode
	flex_sugg_mode = false;
	clearTimeout(flex_timeout);
	
	// reinit the free suggest mode if we have a lot of hits
	if (new_rows.length <= 0 && Number(flex_num_hits) > target.parameters.flexTreshold) {
		flex_timeout = setTimeout("flexTimeout(false)", target.parameters.flexFreeSuggestModeTime);
	}
	
	// show a suggest to submit the form
	if (new_rows.length <= 0 && autoselect && Number(flex_num_hits) != -1 && Number(flex_num_hits) <= target.parameters.flexTreshold) {		
		var genRows = new Array();
		target.rows = genRows;		
		mmFillDiv(target, genRows);
		mmShowSuggBox(target);		
		return null;
	}
	
	return new_rows;
}

// the flexform-suggest onactivate-handler
// reads the value from matchmaker-result and handles it
function flexOnActivate(input, row) {
	var target = mm_inputs[0];
	
	// reset the tab scrolling
	flex_act_max_tab_width = target.parameters.flexMaxTabWidth;
	
	// check if it's a fieldname
	if (row[1] == "FLEXFIELDNAME") {
		flex_sugg_mode = false;
		clearTimeout(flex_timeout);
		target.value = row[0];
		mmCallSearch(target, 10);
		return false;
	}
	
	// check if it is the help suggest
	if (row[1] == "FLEXHELP") {
		target.rows = null;
		flexSetInputValue();
		
		// set the timeout for the suggest mode
		flex_sugg_mode = false;
		clearTimeout(flex_timeout);
		return false;
	}
	
	// get the values
	var name = row[0];
	var kategorie = row[1];
	var treffer = row[2];
	var preis = row[5];
	
	// set the num hits
	if (Number(treffer) != -1)
		flex_num_hits = treffer;
	
	// set the num hits
	if (Number(preis) != -1)
		flex_ab_price = preis;
	
	// fix the new value
	flex_known_params.push(new Array(kategorie, name));
	
	// update the general view
	flex_cursor_pos = flex_known_params.length;		
	flexFillTabs();
	flexShowCount();
	
	target.rows = null;
	flexSetInputValue();
	
	// set the timeout for the suggest mode
	flex_sugg_mode = false;
	clearTimeout(flex_timeout);
	
	// reinit the free suggest mode if we have a lot of hits
	if (Number(flex_num_hits) != -1 && Number(flex_num_hits) > target.parameters.flexTreshold) {
		flex_timeout = setTimeout("flexTimeout(false)", target.parameters.flexFreeSuggestModeTime);
	}
	
	// show a suggest to submit the form
	if (Number(flex_num_hits) != -1 && Number(flex_num_hits) <= target.parameters.flexTreshold) {
		var mr1 = new Array("F&uuml;r diese Kombination gibt es "+flex_num_hits+" passende Ange-", "FLEXHELP");
		var mr2 = new Array("bote.", "FLEXHELP");
		var mr3 = new Array("Bitte klicken Sie hier f&uuml;r die Ergebnisse!", "FLEXSHOW");
		var genRows = new Array(mr1, mr2, mr3);
		target.rows = genRows;		
		mmFillDiv(target, genRows);
		mmShowSuggBox(target);
	}
	
	//target.rows = null;
	target.value = target.value.trim();
	target.focus();
	return false;
}

// do a free suggest recall
function flexDoFreeSuggest(use_timeout) {
	var target = mm_inputs[0];
	clearTimeout(flex_free_suggest_timer);
	if (typeof use_timeout != "undefined" && use_timeout == true) {
		flex_free_suggest_timer = setTimeout(function() {
			clearTimeout(flex_timeout);
			flex_sugg_mode = true;
			mmDoSuggest(0, false);
		}, target.parameters.searchDelay);
	} else {
		clearTimeout(flex_timeout);
		flex_sugg_mode = true;
		mmDoSuggest(0, false);
	}
}

// init's the input-timeout for the free suggest mode
function flexTimeout(ignore_input) {
	var target = mm_inputs[0];
	clearTimeout(flex_timeout);
	
	// check if it's an timeout
	if (target.parameters.flexFreeSuggestModeTime > 0 && (ignore_input || target.value.trim() == "")) {	
		flexDoFreeSuggest();
	}
}

// get-method for a litte selfmade map
function flexMapGet(table, key) {
	for (var z = 0; z < table.length; z++) {
		var set = table[z];
		if (set[0] == key) {
			return set[1];
		}
	}
	return null;
}

// set-method for a litte selfmade map
function flexMapSet(table, key, value) {
	var newtable = new Array();
	for (var z = 0; z < table.length; z++) {
		var set = table[z];
		if (set[0] != key) {
			newtable.push(set);
		}
	}
	var set = new Array();
	set.push(key);
	set.push(value);
	newtable.push(set);
	return newtable;
}

// builds up a new array with all fixed values
// but in here values with the same key are concatinated with an ampersand
function flexConcat() {
	var map = new Array();
	for (var z = 0; z < flex_known_params.length; z++) {
		var param = flex_known_params[z];
		var mapvalue = flexMapGet(map, param[0]);
		if (mapvalue == null) {
			mapvalue = new Array();
		}
		mapvalue.push(param[1]);
		map = flexMapSet(map, param[0], mapvalue);
	}
	var concatkeys = new Array();
	for (var z = 0; z < map.length; z++) {
		var set = new Array();
		set.push(map[z][0]);
		set.push(map[z][1].join("&"));
		concatkeys.push(set);
	}
	return concatkeys;
}

// generates aliases for the given input
function flexInputAlias(str) {
	if (str.split(" ").length > 1) {
		var aliases = str.split(" ").join("|");
		return str + "|" + aliases;
	}
	return str;
}

// builds up the url for the flexform-suggest server-side-script
function flexBuildUrl() {
	var target = mm_inputs[0];
	var sParams = "";
	
	// reset the tab scrolling
	flex_act_max_tab_width = target.parameters.flexMaxTabWidth;
	
	// build the url with the fixed values
	var keys = flexConcat();
	for (var z = 0; z < keys.length; z++) {
		var param = keys[z];
		//url += "&" + escape(param[0]) + "=" + escape(param[1]);
		sParams += "&" + escape(flexTranslate(param[0], "local")) + "=" + escape(flexTranslate(param[1], "local"));
	}
	
	// check which mode we need to use
	if (flex_redirect == true) {
		// we want to get the normal search result
		//sParams += "&FlexUnknown=FLEXSHOWHITS";
		return "/exorbyte/sdecode.php?convert=to" + sParams;
	}
	else if (flex_sugg_mode == true) {
		// we want to get free suggestions from the server
		sParams += "&FlexUnknown=FLEXSUGGEST";
	}
	else {
		// we want to get suggestions based on the input value
		var old_value = target.value;
		sParams += "&FlexUnknown=" + escape(flexTranslate(target.value.trim(), "local"));
		if (old_value.trim().length < old_value.length) {
			sParams += escape(" ");
		}
	}
	
	return target.parameters.requestURL + sParams;
}

// eventhandler for the flexform suggest keys
function flexCheckKey(event, target) {
	var input = mm_inputs[0];
	
	// reset the free suggest mode
	clearTimeout(flex_timeout);
	flex_sugg_mode = false;
	
	// SCROLL LEFT
	/*if (event.keyCode == 33 && flex_known_params.length > 0) {
		flexScrollTabsLeft();
	}
	
	// SCROLL RIGHT
	if (event.keyCode == 34 && flex_known_params.length > 0) {
		flexScrollTabsRight();
	}*/
	
	// ARROW LEFT
	if (event.keyCode == 37 && target.value.length == 0) {
		mmHideSuggBox(target);
		flexScrollTabsLeft();
		/*if (flex_cursor_pos > 0) {
			flex_cursor_pos--;
			if (mmGetParentProps(flex_tab_span.childNodes[flex_cursor_pos], "offsetLeft") < mmGetParentProps(flex_main_span, "offsetLeft")) {
				flex_act_max_tab_width += flex_tab_span.childNodes[flex_cursor_pos].offsetWidth;
			}
			flexFillTabs();
		}*/
	}
	
	// ARROW RIGHT
	if (event.keyCode == 39 && target.value.length == 0) {
		mmHideSuggBox(target);
		flexScrollTabsRight();
		/*if (flex_cursor_pos < (flex_known_params.length-1)) {
			if (flex_act_max_tab_width != input.parameters.flexMaxTabWidth) {
				flex_act_max_tab_width -= flex_tab_span.childNodes[flex_cursor_pos].offsetWidth;
			}
			flex_cursor_pos++;			
			flexFillTabs();
		}*/
	}
	
	// BACKSPACE AND DELETE
	if ((event.keyCode == 8 || event.keyCode == 46) && target.value.length == 0 && flex_known_params.length > 0) {
		mmHideSuggBox(target);
		if (flex_cursor_pos >= 0 && flex_cursor_pos < flex_known_params.length) {
			flexRemoveFixedPos(flex_cursor_pos);
		}
		else if (flex_cursor_pos == flex_known_params.length) {
			flexRemoveFixedPos(flex_known_params.length-1);
		}
		flex_num_hits = -1;
		flex_ab_price = -1;
		flexShowCount();
		flexSetInputValue();
	}
	
	// SPACE
	if (event.keyCode == 32 && target.suggVisible && target.rows != null && target.rows.length == 1 && target.rows[0][1] != "FLEXPROGRESSBAR") {
		mmHideSuggBox(target);
		flexOnActivate(target, target.rows[0]);
	}
	
	// '?' --> SHOW HELP BOX
	if (event.keyCode == 219 && event.shiftKey) {
		flexShowHelp();
	}
		
	return false;
}

// shows the last suggest box again if the input is empty and returns true, else false
function flexRecallLastBox() {
	var target = mm_inputs[0];
	if (target.value.trim() == "" && target.rows != null) {
		//mmFillDiv(target, target.rows);
		mmShowSuggBox(target);
		return true;
	}
	return false;
}

// calls the custom submit function
function flexSubmit() {
	var target = mm_inputs[0];

	// try to select before sending the final search
	if (target.value.trim().length > 0) {
		target.value += " ";
		
		flex_redirect = false;
		flex_autoselect_only = true;
		mmDoSuggest(0, false);
		mmHideSuggBox(mm_inputs[0]);
	} else {
		// do the final search
		flex_redirect = true;
		mmDoSuggest(0, false);
		mmHideSuggBox(mm_inputs[0]);
	}
}

// show the help suggest
function flexShowHelp() {
	var target = mm_inputs[0];
	var genRows = new Array();
	
	if (target.suggBox == null) {
		mmCreateBox(target);
	}
	
	// open a new window with the help page
	if (target.parameters.flexHelpUrl != null) {
		window.open(target.parameters.flexHelpUrl, "");
	}
	
	// show the help text in suggest box
	else if (target.parameters.flexHelpText != null) {
		var genRows = new Array();	
		var words = target.parameters.flexHelpText.split(" ");
		var linelength = Number(target.suggBox.style.width.substring(0, target.suggBox.style.width.length-2))/7;
		
		// generate the lines to display
		var line = "";
		for (var i = 0; i <= words.length; i++) {
			line = line.trim();
			var word = words[i];
			if (i < words.length && (line.length+word.length+1) <= linelength) {
				line += " " + word;
			}
			else {
				var genRow = new Array();
				genRow.push(line);
				genRow.push("FLEXHELP");
				genRows.push(genRow);
				line = word;
			}
		}
	}

	// show the suggest-box
	target.rows = genRows;
	mmFillDiv(target, genRows);
	mmShowSuggBox(target);
}

// show the beginners help
function flexBeginnerHelp() {
	var target = mm_inputs[0];
	var genRows = new Array();
	
	if (target.suggBox == null) {
		mmCreateBox(target);
	}
	
	target.parameters.flexBeginnerText = 'Hallo';

	if (target.value.length > 0 || flex_known_params.length > 0 || target.parameters.flexBeginnerText == null) {
		return 0;
	}
		
	genRows.push(new Array("Bitte geben Sie die Abflug- und Zielorte ein. Bitte", "FLEXNOSELECT"));
	genRows.push(new Array("geben Sie ebenfalls die gew&uuml;nschten Reisedaten", "FLEXNOSELECT"));
	genRows.push(new Array("ein, z.B.:", "FLEXNOSELECT"));
	genRows.push(new Array("", "FLEXNOSELECT"));
	genRows.push(new Array("&ldquo;Antalya Frankfurt Alles inklusive&rdquo;", "FLEXNOSELECT"));
	
	// show the suggest-box
	target.rows = genRows;
	mmFillDiv(target, genRows);
	mmShowSuggBox(target);
}

// translates a input-string using the translation table
// strLang gives the language of str: "local" or "internal"
function flexTranslate(str, strLang) {
	var target = mm_inputs[0];
	
	if (target.parameters.flexTranslationTable != null) {
		// check if str is divided by | and do recursive call
		if (str.match(/\w+=\d+/) == null && str.split("|").length > 1) {
			var strSplit = str.split("|");
			var strTrans = new Array();
			for (var z = 0; z < strSplit.length; z++) {
				strTrans.push(flexTranslate(strSplit[z], strLang));
			}
			return strTrans.join("|");
		}
		
		// check if str is divided by & and do recursive call
		if (str.match(/\w+=\d+/) == null && str.split("&").length > 1) {
			var strSplit = str.split("&");
			var strTrans = new Array();
			for (var z = 0; z < strSplit.length; z++) {
				strTrans.push(flexTranslate(strSplit[z], strLang));
			}
			return strTrans.join("&");
		}
		
		// normal translation
		for (var z = 0; z < target.parameters.flexTranslationTable.length; z++) {
			// input matches translation exactly
			var trans = target.parameters.flexTranslationTable[z].split("#");
			if (strLang == "local" && trans[1].toLowerCase() == str.toLowerCase()) {
				return trans[0];
			}
			if (strLang == "internal" && trans[0].toLowerCase() == str.toLowerCase()) {
				return trans[1];
			}
			
			// input matches more than half of the translation, just for local input
			if (0 && strLang == "local" && str.length > (trans[1].length/2)) {
				var subTrans = trans[1].substring(0, str.length);
				if (subTrans.toLowerCase() == str.toLowerCase()) {
					return trans[0];
				}
			}
		}
	}
	
	return str;
}

// set the right inbox text
function flexSetInputValue() {
	var target = mm_inputs[0];
	target.value = "";
	target.focus();
	return 0;
}

// delocalizes the rows
function flexDelocalize(rows) {
	for (var z = 0; z < rows.length; z++) {
		rows[z][0] = rows[z][0].split("|").join("");
	}
	return rows;
}

// localizes the intext in rowtext and highlights it
function flexLocalizeSuggRow(intext, rowtext, color) {
	if (color == null) {
		return rowtext;
	}
	
	// split the rowtext
	var rowsplit = rowtext.split("|");
	
	// generate the new html code to highlight the text
	var html = "";
	for (var z = 0; z < rowsplit.length; z++) {
		if (z == 1) {
			rowsplit[z] = "<font color='" + color + "'>" + rowsplit[z] + "</font>";
		}
		html += rowsplit[z];
	}
	
	return html;
}

// generates the status div in the suggest footer
function flexGetStatusDiv() {
	var target = mm_inputs[0];
	
	var iDiv = target.parameters.document.createElement("div");
	iDiv.style.cursor = "pointer";
	
	var html = "";
	if (flex_special_status != null) {
		html = flex_special_status;
		flex_special_status = null;
	}
	else if (Number(flex_num_hits) != -1 && Number(flex_num_hits) <= target.parameters.flexTreshold) {
		iDiv.onmousedown = new Function("", "flexSubmit();");
		html = target.parameters.flexShowHitsText;
	}
	else if (target.parameters.flexStatusBar == true) {
		if (target.parameters.flexFooterHelpText != null) {
			html = target.parameters.flexFooterHelpText;
		}
		else {
			html = flexGenCountText();
		}
	}
	
	if (html != null) {
		iDiv.innerHTML = "<div align=left style='float: left; padding: 0; margin: 0; vertical-align: middle;'><nobr><b><font color='#DE0108' size=1 style='font-family: Verdana, Arial, Helvetica, Sans-Serif; vertical-align: middle;'>" + html + "</font></b></nobr></div>";
		return iDiv;
	}
	return null;
}

// generates hierachical suggests for the flat suggest
function flexGenerateHierachical(rows) {
	var new_rows = new Array();
	var old_fieldname = null;
	
	// Sort by 'ab' price
	// bubble sort should be no problem, because it runs on client and with small arrays
	var swapped;
	for (var i = 1; i < rows.length; i++) {
		swapped = false;

		for (var j = 0; j < (rows.length-i); j++) {
			var first = rows[j];
			var second = rows[j+1];

			if (first[1] == "Abflug_Datum") {
				continue;
			}
			
			// swap if needed
			if (first[1] != "Abflug_Tag") {
				if ((Number(first[5]) > Number(second[5]) && first[1] == second[1]) ||
					 (Number(first[5]) == Number(second[5]) && first[1] == second[1]
					  && Number(first[2]) < Number(second[2]))) {
					rows[j] = second;
					rows[j+1] = first;
					swapped = true;
				}
			} else { // sort by day-of-the-month instead
				if ((Number(first[0]) > Number(second[0]) && first[1] == second[1]) ||
					 (Number(first[0]) == Number(second[0]) && first[1] == second[1]
					  && Number(first[2]) < Number(second[2]))) {
					rows[j] = second;
					rows[j+1] = first;
					swapped = true;
				}
			}
		}
		
		// break if we have a sorted heap
		if (!swapped) {
			break;
		}
	}
	
	for (var z = 0; z < rows.length; z++) {
		var row = rows[z];
		if (old_fieldname != row[1] && row[1] != "FLEXNOSELECT") {
			var new_row = new Array();
			new_row.push(row[1]);
			new_row.push("FLEXFIELDNAME");			
			new_row.push("1");
			new_row.push("false");
			new_rows.push(new_row);
		}
		new_rows.push(row);
		old_fieldname = row[1];
	}
	
	return new_rows;
}

////////// flexform suggest end ///////////

function mmDoBlur(event) {
	if (!event && window.event) {
		event = window.event;
	}
	// hide suggestion boxes
	for (var i = 0; i < mm_inputs.length; i++) {
		mmHideSuggBox(mm_inputs[i]);
	}
	target = (typeof(event.srcElement) == "undefined" ? event.target : event.srcElement);
	// target lost focus
	target.lostFocus = true;
	if ((typeof target.oldBlur != "undefined") && (target.oldBlur != null))
		target.oldBlur(event);
		
	return 0;
}

function mmDoFocus(event) {
	if (!event && window.event) {
		event = window.event;
	}
	target = (typeof(event.srcElement) == "undefined" ? event.target : event.srcElement);
	// target has focus
	target.lostFocus = false;
	if ((typeof target.oldFocus != "undefined") && (target.oldFocus != null))
		target.oldFocus(event);
		
	return 0;
}

function mmPageNavig(target_id, mmDirection) {
//	alert("pageNavig!");
	var target = mm_inputs[target_id];
	if (Number(target.parameters.pageSize) <= 0) return 0;
	if (mmDirection == MM_DOWNDIR) {
		if (Number(target.mmPageOffset) + Number(target.parameters.pageSize) >= Number(target.rows.length)) return 0;
		// increase pageOffset and redisplay
		var newOfs = Number(target.mmPageOffset) + Number(target.parameters.pageSize);
		mmFillDiv(target, target.rows, newOfs);
	} else {
		// MM_UPDIR
		if (Number(target.mmPageOffset) <= 0) return 0;
		// decrease pageOffset and redisplay
		var newOfs = Number(target.mmPageOffset) - Number(target.parameters.pageSize);
		if (newOfs < 0) newOfs = 0;
		mmFillDiv(target, target.rows, newOfs);
	}
	// ensure that box is focused and div is shown; IE/Opera event behaviour workaround
	setTimeout(function() {
		target.focus();
		mmShowSuggBox(target);	
	}, 10);
	mmCancelEvent(false);
	return false;
}

function mmGetPageNavig(target, dir) {
	if (dir == MM_UPDIR)
		return "mmPageNavig(" + target.mm_refcnt + ", MM_UPDIR);";
	else
		return "mmPageNavig(" + target.mm_refcnt + ", MM_DOWNDIR);";
}

function mmCheckKey(event, target) {
	
	if (event.ctrlKey && (event.altKey || event.shiftKey) && (event.keyCode == 120)) {
		target.dynamicNotification = !target.dynamicNotification;
		return true;
	}
	if (event.ctrlKey && (event.altKey || event.shiftKey) && (event.keyCode == 119)) {
		mmDoSearch(target.targetIndex, true);
		return true;
	}
	if (event.ctrlKey && (event.altKey || event.shiftKey) && (event.keyCode == 118)) {
		target.parameters.debug = !target.parameters.debug;
		return true;
	}
	
	return flexCheckKey(event, target);
}

function mmCancelEvent(event) {
	if (ie) {
		event = window.event;
		window.event.returnValue = false;
	}
	event.cancelBubble = true;
	event.returnValue = false;
	event.cancel = true;
	return false;
}

function mmDoFieldKeyDown(event) {
	if (!event && window.event) {
		event = window.event;
	}
	target = (typeof(event.srcElement) == "undefined" ? event.target : event.srcElement);
	
	if (!target.xmlhttp) return 0;
	switch (Number(event.keyCode)) {
		case 40: {
			// down
			if (!target.suggVisible && !flexRecallLastBox()) {
				mmCallSearch(target, 10);
				return false;
			} else if (target.suggVisible) {
				if (target.suggCount > 0) {
					if (target.lastHighlightedId < target.lastDisplayedRow) {
						mmSelectRow(target, target.lastHighlightedId + 1, MM_DOWNDIR);
					}
					return mmCancelEvent(event);
				}
			}
			break;
		}
		case 38: {
			// up
			if (target.suggCount > 0) {
				if (target.lastHighlightedId > target.firstDisplayedRow) {
					mmSelectRow(target, target.lastHighlightedId - 1, MM_UPDIR);
					mmShowSuggBox(target);
				}
				return mmCancelEvent(event);
			}
			break;
		}
		case 33: {
			// PgUp
			if (target.suggVisible && (target.suggCount > 0)) {
				mmPageNavig(target.mm_refcnt, MM_UPDIR);
				mmSelectRow(target, target.firstDisplayedRow, MM_UPDIR);
				return mmCancelEvent(event);
			}
			break;
		}
		case 34: {
			// PgDn
			if (target.suggVisible && (target.suggCount > 0)) {
				mmPageNavig(target.mm_refcnt, MM_DOWNDIR);
				mmSelectRow(target, target.lastDisplayedRow, MM_DOWNDIR);
				return mmCancelEvent(event);
			}
			break;
		}
		case 35: {
			// End
			if (target.suggVisible && (target.suggCount > 0)) {
				mmSelectRow(target, target.lastDisplayedRow, MM_DOWNDIR);
				// don't cancel event				
				return true;
			}
			break;
		}
		case 36: {
			// Home
			if (target.suggVisible && (target.suggCount > 0)) {
				mmSelectRow(target, target.firstDisplayedRow, MM_UPDIR);
				// don't cancel event				
				return true;
			}
			break;
		}
		case 13: {
			// Enter
			if (target.suggVisible && (target.lastHighlightedId >= 0)) {
				mmHideSuggBox(target);
				var row = target.parameters.document.getElementById("suggRow" + target.mm_refcnt + "_" + target.lastHighlightedId);
				// row selected?
				if (row) {					
					var fx = row.mmMouseDown;
					fx();				
				}
				return mmCancelEvent(event);
			} else {
				// no box visible or nothing selected, submit input
				mmSubmitString(target, target.value);
				return mmCancelEvent(event);
			}
			break;
		}
		case 9: {
			// TAB
			if (target.suggVisible && (target.lastHighlightedId >= 0) && ((target.parameters.flags & AS_TABSELECTS) == AS_TABSELECTS)) {
				mmHideSuggBox(target);
				var row = target.parameters.document.getElementById("suggRow" + target.mm_refcnt + "_" + target.lastHighlightedId);
				if (row) {
					var fx = row.mmMouseDown;
					if (!fx()) return mmCancelEvent(event);
				}
				return true;
			}
			break;
		}
		case 27: {
			// Escape
			if (target.suggVisible) {
				mmSelectRow(target, -1);
				mmHideSuggBox(target);
				return mmCancelEvent(event);
			}
			break;
		}
		case 116: {
			// F5, block search on refresh
			return true;
		}
	}
	if (mmCheckKey(event, target)) return false;
	if ((event.keyCode == 8) || (event.keyCode == 32) || (event.keyCode >= 46)) {
		mmCallSearch(target, target.parameters.searchDelay);
	}
	
	return 0;
}

function mmGetXMLHTTP() {
  var result = false;
  if(typeof XMLHttpRequest != "undefined") {
    result = new XMLHttpRequest();
  } else {
	try {
		result = new ActiveXObject("Msxml2.XMLHTTP");
	} catch (e) {
		try {
			result = new ActiveXObject("Microsoft.XMLHTTP");
		} catch (e) {}
	}
  }
  return result;
}

function mmGetParentProps(elem, prop) {
	// returns the sum of the property "prop" along the offsetParent row of elem
	var result = 0;
	while (elem != null) {
		result += elem[prop];
		elem = elem.offsetParent;
	}
	return result;
}

function mmSelectRow(target, row, direction) {
	var rowDiv;
	if (target.lastHighlightedId > -1) {
		rowDiv = target.parameters.document.getElementById("suggRow" + target.mm_refcnt + "_" + target.lastHighlightedId);
		if (rowDiv) {
			rowDiv.style.backgroundColor = rowDiv.oldBackgroundColor;
			rowDiv.style.color = rowDiv.oldColor;
			var children = rowDiv.childNodes;
			for (i = 0; i < children.length; i++) {
				children[i].style.backgroundColor = children[i].oldBackgroundColor;
				children[i].style.color = children[i].oldColor;
			}
		}
	}		
	var selectable = false;
	var safeCount = 0;
	// find next selectable element for the given direction, if possible
	while (target.rows && !selectable && (safeCount < 2 * Number(target.rows.length))) {
		rowDiv = target.parameters.document.getElementById("suggRow" + target.mm_refcnt + "_" + row);
		if (!rowDiv) break;
		selectable = typeof (rowDiv.selectable) != 'undefined';
		if (!selectable) {
			if (direction == MM_UPDIR) {
				row--;
				safeCount++;
			} else {
				row++;
				safeCount++;
			}
			if (row <= 0) {
				direction = !direction;
				row = 0;
			}
 			if (row >= Number(target.rows.length)) {
				direction = !direction;
				row = Number(target.rows.length) - 1;
			}
		}
	}
	if (rowDiv) {
		target.lastHighlightedId = row;
		if (rowDiv.oldBackgroundColor != rowDiv.style.backgroundColor)
			rowDiv.oldBackgroundColor = rowDiv.style.backgroundColor;
		if (rowDiv.oldColor != rowDiv.style.color)
			rowDiv.oldColor = rowDiv.style.color;
		if (target.parameters.highlightbg != '')
			rowDiv.style.backgroundColor = target.parameters.highlightbg;
		if (target.parameters.highlightfg != '')
			rowDiv.style.color = target.parameters.highlightfg;
		var children = rowDiv.childNodes;
		for (i = 0; i < Number(children.length); i++) {
			if (children[i].oldBackgroundColor != children[i].style.backgroundColor)
				children[i].oldBackgroundColor = children[i].style.backgroundColor;
			if (children[i].oldColor != children[i].style.color)
				children[i].oldColor = children[i].style.color;
			if (target.parameters.highlightbg != '')
				children[i].style.backgroundColor = target.parameters.highlightbg;
			if (target.parameters.highlightfg != '')
				children[i].style.color = target.parameters.highlightfg;
		}
	}
	
	return 0;
}

function mmMouseEnter(target_id, id) {
	// firefox selection bug workaround
	if (mmIgnoreFirstMouseEnter) {
		mmIgnoreFirstMouseEnter = false;
		return 0;
	}
	var target = mm_inputs[target_id];
	mmSelectRow(target, id);
	
	return 0;
}

function mmSubmitString(target, string) {
	// if an alternative search field has been specified, use it
	if (target.parameters.searchValueObject != null) 
		target.parameters.searchValueObject.value = string;
	else
		target.value = string;
	if ((target.form.action != "") && ((target.parameters.flags & AS_NOSUBMIT) != AS_NOSUBMIT)) {
		target.form.submit();
	}
	
	return 0;
}

function mmSetDivSize(target) {
	if (target.suggBox) {
		var x, y;
		if (self.innerHeight) {
			x = Number(self.innerWidth);
			y = Number(self.innerHeight);
		} else if (target.parameters.document.documentElement && target.parameters.document.documentElement.clientHeight) {
			x = Number(target.parameters.document.documentElement.clientWidth);
			y = Number(target.parameters.document.documentElement.clientHeight);
		} else if (target.parameters.document.body) {
			x = Number(target.parameters.document.body.clientWidth);
			y = Number(target.parameters.document.body.clientHeight);
		}
		if (Number(target.parameters.top) < 0)
			target.suggBox.style.top = 
				(Number(mmGetParentProps(target, "offsetTop")) + Number(target.offsetHeight)) + "px";
		else 
			target.suggBox.style.top = target.parameters.top + "px";
		var w = Number(target.offsetWidth);
		if (!isNaN(target.parameters.width)) {
			var w = (target.parameters.width <= 0 ? w : target.parameters.width);
		}
		
		// flexform-suggest: set the div width
		var flex_width = flex_orig_input.offsetWidth - flex_tab_span.offsetWidth;
		if (flex_width < target.parameters.flexMinWidth) {
			flex_width = target.parameters.flexMinWidth;
		}
		if (flex_width > target.parameters.flexMaxWidth) {
			flex_width = target.parameters.flexMaxWidth;
		}
		flex_width -= 5; // box seems to be offset by 5px, fix this
		target.suggBox.style.width = flex_width + "px";
		
		var l = 0;
		if (target.parameters.align == "right")
			l = Number(mmGetParentProps(target, "offsetLeft")) - w + Number(target.offsetWidth);
		else if (target.parameters.align == "left")
			l = Number(mmGetParentProps(target, "offsetLeft"));
		else
			// align = free
			l = 0;
		// PCL: disabled this code to prevent suggest from popping up under
      // other tabs.

		//if (l + w > x) l = x - w;
		//if (l < 0) l = 0;
		// take value of left into account
		target.suggBox.style.left = (l + Number(target.parameters.left)) + "px";			
	}
	
	return 0;
}

function mmUnflash(target_id, oCol, nCol, count) {
	var target = mm_inputs[target_id];
	target.style.backgroundColor = oCol;
	count--;
	if (count > 0) {
		setTimeout("mmFlash(" + target_id + ", \"" + nCol + "\", " + count + ");", mm_flashtime);
	}
	
	return 0;
}
function mmFlash(target_id, nCol, count) {
	var target = mm_inputs[target_id];
	var oCol = target.style.backgroundColor;
	target.style.backgroundColor = nCol;
	var cmd = "mmUnflash(" + target_id + ", \"" + oCol + "\", \"" + nCol + "\", " + count + ");";
	setTimeout(cmd, mm_flashtime);
	
	return 0;
}

function replaceHTMLEntities(str) {
	if (typeof str != "String")
		str = String(str);
	var result = str.replace(/&/g, "&amp;");	
	result = result.replace(/</g, "&lt;");	
	result = result.replace(/>/g, "&gt;");	
	result = result.replace(/\'/g, "&#39;");	// ie apos bug!
	result = result.replace(/\"/g, "&quot;");	
	return result;
}

function mmRedirectClick(target_id, row, coBypass) {
	var target = mm_inputs[target_id];
	if (target.parameters.clickout && (typeof coBypass == "undefined")) {
		url = mmQReplace(target.parameters.requestURL, target) + "&click=" + escape(target.rows[row][target.parameters.clickoutField]) + "&coID=" + escape(target.coID) + "&hash=" + Math.random();
		target.xmlhttp.open("GET", url, true);
		target.xmlhttp.send(null);
		window.setTimeout("mmRedirectClick(" + target_id + ", " + row + ", true)", 100);
		return 0;
	}
	if ((typeof target.parameters.onActivate != "undefined") && (target.parameters.onActivate != null) && (target.parameters.onActivate != "")) {
		if (target.parameters.onActivate(target, target.rows[row]))
			mmSubmitString(target, target.rows[row][target.parameters.valueField]);
	} else {
		mmSubmitString(target, target.rows[row][target.parameters.valueField]);
	}
	
	return 0;
}

function mmFillDiv(target, rows, pageOffset) {
	if (target.parameters.flexPageSize > 0 && rows.length > target.parameters.flexPageSize) {
		target.parameters.pageSize = target.parameters.flexPageSize;
	}
	else {
		target.parameters.pageSize = 0;
	}
	
	if (target.suggBox == null) {
		mmCreateBox(target);
	}
	
	// remove previous elements
	while (target.suggBox.hasChildNodes()) {
		target.suggBox.removeChild(target.suggBox.firstChild);
	}
	var po = 0;	// local pageOffset
	var pe = Number(rows.length);	// local page end
	// consider pageOffset?
	if (!isNaN(pageOffset)) {
		po = pageOffset;
		target.mmPageOffset = pageOffset;
	}
	if (target.parameters.pageSize > 0) {
		pe = po + Number(target.parameters.pageSize);
		if (pe > Number(rows.length)) pe = Number(rows.length);
	}
	target.firstDisplayedRow = po;
	target.lastDisplayedRow = pe - 1;
	var fieldnames = target.fieldnames;
	// top div
	if ((typeof target.parameters.suggBoxTop != "undefined") && (target.parameters.suggBoxTop != null) && (target.parameters.suggBoxTop != "")) {
		var tDiv = target.parameters.document.createElement("div");
		tDiv.style.width = "100%";
		if (target.parameters.suggBoxTop(tDiv))
			target.suggBox.appendChild(tDiv);
	}
	var localize = (fieldnames.length != 2) || (fieldnames[0] != "Name") || (fieldnames[1] != "Key");
	if (localize && (typeof target.parameters.headerFunction != "undefined") && (target.parameters.headerFunction != null) && (target.parameters.headerFunction != "")) {
		var iDiv = target.parameters.document.createElement("div");
		var ih = target.parameters.headerFunction(target, iDiv, rows);
		iDiv.innerHTML = ih;
		iDiv.style.width = "100%";
		target.suggBox.appendChild(iDiv);
	}
	// nav div? (top/both)
	if (target.parameters.pageSize > 0 && (target.parameters.navigationPos == "top" || target.parameters.navigationPos == "both")) {
		var nDiv = target.parameters.document.createElement("div");
		var page = Math.round(po / target.parameters.pageSize) + 1;
		var maxPages = Math.ceil(Number(rows.length) / Number(target.parameters.pageSize));
		// custom nav div?
		if ((typeof target.parameters.navigationBar != "undefined") && (target.parameters.navigationBar != null) && (target.parameters.navigationBar != "")) {
			if (target.parameters.navigationBar(target, nDiv, page, maxPages))
				target.suggBox.appendChild(nDiv);
		} else {
			// predefined nav div
			var l_inactive = (page <= 1);
			var r_inactive = (page >= maxPages);
			nDiv.onmousedown = function(event) {
				mmCancelEvent(event);
				return false;
			}
			nDiv.innerHTML = "<div class='navRow'>" +
			"<a href='#' onmousedown='" + mmGetPageNavig(target, MM_UPDIR) + "'>" +
				"<img valign='absmiddle' border=0 id='navleft' alt='<' title='Zur&uuml;ckbl&auml;ttern (PgUp)' src='" + target.parameters.iconPath + "leftarrow" + (l_inactive ? "_inactive" : "") + ".png'></a>" +
			 	"<sup>Seite " + page + " von " + maxPages + "</sup>" +
			"<a href='#' onmousedown='" + mmGetPageNavig(target, MM_DOWNDIR) + "'>" + 
				"<img valign='absmiddle' border=0 name='navright' alt='>' title='Vorbl&auml;ttern (PgDown)' src='" + target.parameters.iconPath + "rightarrow" + (r_inactive ? "_inactive" : "") + ".png'></a>" +
			"</div>";
			// use different event model in IE
			if (ie) {
				var e = nDiv.firstChild.childNodes[0];
				e.onmousedown = new Function("", mmGetPageNavig(target, MM_UPDIR));
				var e = nDiv.firstChild.childNodes[2];
				e.onmousedown = new Function("", mmGetPageNavig(target, MM_DOWNDIR));
			}
			target.suggBox.appendChild(nDiv);
		}		
	}
	
	// flexform-suggest: show the number of hits
	if (target.parameters.flexCountDisplay == "suggest" || target.parameters.flexCountDisplay == "both") {
		var eDiv = target.parameters.document.createElement("div");
		eDiv.innerHTML = "<div align=right style='padding: 0 2px; margin: 0; border-top:thin solid gray; vertical-align: middle;'><nobr><font size='1' style='font-family: Arial, Helvetica, Sans-Serif; vertical-align: middle;'>" + flexGenCountText() + "</font></nobr></div>";
		target.suggBox.appendChild(eDiv);
	}
	
	if (localize && (typeof target.parameters.rowFunction != "undefined") && (target.parameters.rowFunction != null) && (target.parameters.rowFunction != "")) {
		// prepare field names array
		var field_index = new Array();
		for (j = 0; j < Number(fieldnames.length); j++) {
			field_index[fieldnames[j]] = j;
		}
	}
	var flex_hierarchical = (po == 0) ? false : true;
	for (var i = po; i < pe; i++) {
		var rowDiv = target.parameters.document.createElement("div");
		rowDiv.style.width = "100%";
		var leftSpan = target.parameters.document.createElement("span");
		leftSpan.innerHTML = target.parameters.suggBoxLeftFrameHTML();		
		rowDiv.appendChild(leftSpan);

		var iDiv = target.parameters.document.createElement("div");
		iDiv.id = "suggRow"  + target.mm_refcnt + "_" + i;
		iDiv.className = "suggRow";
		iDiv.style.cursor = "pointer";
		iDiv.style.width = "100%";
		if (target.parameters.normalbg != '')
			iDiv.style.backgroundColor = target.parameters.normalbg;
		if (target.parameters.normalfg != '')
			iDiv.style.color = target.parameters.normalfg;


		var addEvents = true;
		if (localize && (typeof target.parameters.rowFunction != "undefined") && (target.parameters.rowFunction != null) && (target.parameters.rowFunction != "")) {
			addEvents = target.parameters.rowFunction(target, rows, field_index, i, iDiv);
		} else {
			if (rows[i].length > 1) {
				var rowtext = flexLocalizeSuggRow(target.value.trim(), rows[i][0], target.parameters.flexHighlightColor);
				rowtext = addMarking(rowtext);

				if (isNaN(rows[i][2]) == false && Number(rows[i][2]) <= 0) {
					addEvents = false;
					rowtext = target.parameters.flexNoContext + rowtext;
				}
				if (rows[i][1] == "FLEXNOSELECT" || rows[i][1] == "FLEXHELP") {
					addEvents = false;
					iDiv.innerHTML = "<span class='suggProduct' style='max-width:100%;'><nobr><b>" + rowtext + "</b>&nbsp;</nobr></span>";
				} else if (rows[i][1] == "FLEXSHOW") {
					addEvents = true;
					iDiv.innerHTML = "<span class='suggProduct' style='max-width:100%; color:#DE0108;'><nobr><b>" + rowtext + "</b>&nbsp;</nobr></span>";

				} else if (rows[i][1] == "FLEXFIELDNAME") {
					addEvents = false;
					flex_hierarchical = true;
					re = /\_/g;
					iDiv.innerHTML = "<span class='suggProduct' style='max-width:100%;'><img style='margin-top:-2px;' alt='' src='" + target.parameters.iconPath + "ordner.gif'><nobr><b style='vertical-align:super;'>" + rowtext.replace(re, " ") + "</b>&nbsp;</nobr></span>";
				} else if (rows[i][1] == "FLEXPROGRESSBAR") {
					if ((flex_special_status != target.parameters.flexNoHitsText) || (target.parameters.flexNoHitsText == null)) {
						addEvents = false;
						iDiv.innerHTML = "<img alt='' src='" + target.parameters.iconPath + "pleasewait.gif'>";
					} else {
						addEvents = false;
						iDiv = flexGetStatusDiv();
					}
				} else if (flex_hierarchical == true) {
					var count = "";
					var price = "";
					if (isNaN(rows[i][2]) == false && Number(rows[i][2]) > 0) {
						count = rows[i][2];
					}
					if (isNaN(rows[i][5]) == false && Number(rows[i][5]) > 0) {
						price = "ab " + rows[i][5] + " &euro;";
					}
					var icon = "child.gif";
					if (i == (pe-1) || rows[i+1][1] != rows[i][1]) {
						icon = "lastchild.gif";
					}
					iDiv.innerHTML = "<span class='suggProduct'><img alt='' src='" + target.parameters.iconPath + icon + "'><nobr style='vertical-align:super;'>"
						+ rowtext + "&nbsp;</nobr></span><span class='suggMid'><nobr>"
						+ price + "</nobr></span><span class='suggCat'><nobr>"
						+ count + "</nobr></span>";
				} else {
					if (isNaN(rows[i][2]) == false && Number(rows[i][2]) > 0) {
						rowtext += " (" + rows[i][2] + ")";
					}
					iDiv.innerHTML ="<span class='suggProduct'><nobr>" + rowtext 
						+ "&nbsp;&nbsp;</nobr></span><span class='suggCat'><nobr>"
						+ replaceHTMLEntities(rows[i][1]) + "</nobr></span>";
				}				
			}
		}

		if (addEvents) {
			iDiv.selectable = true;
			// take different browser event models into account
			// store function in a separate property because it is used elsewhere
			iDiv.mmMouseOver = new Function("evt", "mmMouseEnter(" + target.mm_refcnt + "," + i + ")");
			if (ie) iDiv.onmouseover = iDiv.mmMouseOver;
			if (!ie) iDiv.addEventListener('mouseover', iDiv.mmMouseOver, false);
			if (rows[i][1] == "FLEXSHOW") {
				iDiv.mmMouseDown = new Function("evt", "flexSubmit();");
			} else {
				iDiv.mmMouseDown = new Function("evt", "mmRedirectClick(" + target.mm_refcnt + ", '" + i + "')");
			}
			if (ie) iDiv.onmousedown = iDiv.mmMouseDown;
			if (!ie) iDiv.addEventListener('mousedown', iDiv.mmMouseDown, false);
		}		
		rowDiv.appendChild(iDiv);
		
		var rightSpan = target.parameters.document.createElement("span");
		rightSpan.innerHTML = target.parameters.suggBoxRightFrameHTML();		
		rowDiv.appendChild(rightSpan);
		
		target.suggBox.appendChild(rowDiv);
	}

	// nav div? (bottom/both)
	if (target.parameters.pageSize > 0 && (target.parameters.navigationPos == "bottom" || target.parameters.navigationPos == "both")) {
		var nDiv = target.parameters.document.createElement("div");
		var page = Math.round(po / target.parameters.pageSize) + 1;
		var maxPages = Math.ceil(Number(rows.length) / Number(target.parameters.pageSize));
		// custom nav div?
		if ((typeof target.parameters.navigationBar != "undefined") && (target.parameters.navigationBar != null) && (target.parameters.navigationBar != "")) {
			if (target.parameters.navigationBar(target, nDiv, page, maxPages))
				target.suggBox.appendChild(nDiv);
		} else {
			// predefined nav div
			var l_inactive = (page <= 1);
			var r_inactive = (page >= maxPages);
			nDiv.onmousedown = function(event) {
				mmCancelEvent(event);
				return false;
			}
			nDiv.innerHTML = "<div class='navRow'>" +
			"<a href='#' onmousedown='" + mmGetPageNavig(target, MM_UPDIR) + "'>" +
				"<img valign='absmiddle' border=0 id='navleft' alt='<' title='Zur&uuml;ckbl&auml;ttern (PgUp)' src='" + target.parameters.iconPath + "leftarrow" + (l_inactive ? "_inactive" : "") + ".png'></a>" +
			 	"<sup>Seite " + page + " von " + maxPages + "</sup>" +
			"<a href='#' onmousedown='" + mmGetPageNavig(target, MM_DOWNDIR) + "'>" + 
				"<img valign='absmiddle' border=0 name='navright' alt='>' title='Vorbl&auml;ttern (PgDown)' src='" + target.parameters.iconPath + "rightarrow" + (r_inactive ? "_inactive" : "") + ".png'></a>" +
			"</div>";
			// use different event model in IE
			if (ie) {
				var e = nDiv.firstChild.childNodes[0];
				e.onmousedown = new Function("", mmGetPageNavig(target, MM_UPDIR));
				var e = nDiv.firstChild.childNodes[2];
				e.onmousedown = new Function("", mmGetPageNavig(target, MM_DOWNDIR));
			}
			target.suggBox.appendChild(nDiv);
		}		
	}
	
	if (((target.parameters.flags & AS_NOLOGO) == AS_NOLOGO) && (typeof target.parameters.footerFunction != "undefined") && (target.parameters.footerFunction != null) && (target.parameters.footerFunction != "")) {
		var iDiv = target.parameters.document.createElement("div");
		var ih = target.parameters.footerFunction(target, iDiv, rows);
		iDiv.innerHTML = ih;
		iDiv.style.width = "100%";
		target.suggBox.appendChild(iDiv);
	}
	
	// flexform: create the status bar	
 	/*var eDiv = flexGetStatusDiv();
 	if (eDiv != null) {
 		target.suggBox.appendChild(eDiv);
 	}*/
		
	// Following code may only be removed or modified if contract regulations permit
	// Der folgende Code darf nur entfernt oder veraendert werden, falls die Vertragsbedingungen es gestatten
	if ((target.parameters.flags & AS_NOLOGO) == 0) {		
		var eDiv = target.parameters.document.createElement("div");
		eDiv.style.cursor = "pointer";
		eDiv.mmMouseDown = new Function("", "window.location.href = 'http://www.exorbyte.de';");
		eDiv.innerHTML = "<div class='suggSides'><div align=right style='padding: 1px 5px 3px 0; margin: 0; border-top:1px solid gray; vertical-align: middle;'><nobr><font size=1 style='font-weight: bold; color: #2D556E; font-family: Arial, Helvetica, Sans-Serif; vertical-align: bottom;'>Powered by: <img src='" + exorbyteLogo + "' style='padding: 0; margin: 0;' align='texttop' alt='exorbyte'></font></nobr></div></div>";
		if (ie) eDiv.onmousedown = eDiv.mmMouseDown;
		if (!ie) eDiv.addEventListener('mousedown', eDiv.mmMouseDown, false);
		target.suggBox.appendChild(eDiv);	
	}
	// Der vorstehende Code darf nur entfernt oder veraendert werden, falls die Vertragsbedingungen es gestatten
	// Previous code may only be removed or modified if contract regulations permit

	// bottom div
	if ((typeof target.parameters.suggBoxBottom != "undefined") && (target.parameters.suggBoxBottom != null) && (target.parameters.suggBoxBottom != "")) {
		var iDiv = target.parameters.document.createElement("div");
		iDiv.style.width = "100%";
		if (target.parameters.suggBoxBottom(iDiv))
			target.suggBox.appendChild(iDiv);
	}
	// highlight first currently visible item
	target.lastHighlightedId = po - 1;
	
	return 0;
}

function removeAliases(str) {
	str = str.ltrim("~!<>");  // get rid of leading chars on direct fields
	newstr = str.split("=");
	
	if ((typeof newstr[0] == "undefined") || (newstr[0] == null)) {
		return str;
	}
	return newstr[0].trim();
}

function addMarking(str) {
	str = removeAliases(str);
	var fText = str;

	if (str.search(/|/) >= 0) {
		var res = str.split("|");
		if ((typeof res != "undefined") && (res != null) && (res.length == 3)) {
			fText = res[0] + "<b>" + res[1] + "</b>" + res[2];
		}
	}
	return fText;
}

function mmSuggestDeliver(target_id, fieldnames, rows, totalSuggCount, coID) {
	if ((target < 0) || (target >= Number(mm_inputs.length))) return 0;
	var target = mm_inputs[target_id];
	if (!target) return 0;
	// deferred creation for IE
	if (ie) 
		if (!mmCreateBox(target)) 
			return 0;
	target.timeout = 0;
	target.coID = coID;
	target.fieldnames = fieldnames;
	target.mmPageOffset = 0;
	if (target.dynamicNotification)
		window.status = "Received data after " + (new Date().getTime() - mm_qtime) + " ms";

	// this was autoselect-only, but nothing found. so call the flexSubmit() again
	if (flex_autoselect_only == true && rows.length == 0) {
		flex_autoselect_only = false;
		target.value = "";
		flexSubmit();
		return 0;
	}
		
	// flexform-suggest: we want the real hits!!!
	if (Number(flex_num_hits) != -1 && Number(flex_num_hits) <= target.parameters.flexTreshold && flex_redirect == true) {
		if ((typeof target.parameters.flexRedirectFunction != "undefined") && (target.parameters.flexRedirectFunction != null) && (target.parameters.flexRedirectFunction != "")) {
			target.parameters.flexRedirectFunction(fieldnames, rows, totalSuggCount);
		}
		return 0;
	}
	
	// no hits, so show suggest or tell the user to run the search
	if (!flex_nothing_found && rows.length == 0 && target.parameters.flexNoHitsText != null) {
		if (flex_sugg_mode) {

			var mr1 = new Array("F&uuml;r diese Kombination gibt es "+flex_num_hits+" passende Ange-", "FLEXHELP");
			var mr2 = new Array("bote.", "FLEXHELP");
			var mr3 = new Array("Bitte klicken Sie hier f&uuml;r die Ergebnisse!", "FLEXSHOW");
			var rows = new Array(mr1, mr2, mr3);

			target.rows = rows;
			target.suggCount = 1;
			mmFillDiv(target, rows);
			
			mmShowSuggBox(target);
			return 0;
		} else {
			flex_nothing_found = true;
			flex_special_status = target.parameters.flexNoHitsText;
			flexTimeout(true);
			return 0;
		}
	}
	if (flex_nothing_found && rows.length == 0) {
		target.rows = null;
		mmHideSuggBox(target);
		return 0;
	}
	flex_nothing_found = false;

	// translate the result
	for (var z = 0; z < rows.length; z++) {
		var row = rows[z];

		row[0] = flexTranslate(row[0], "internal");
		row[1] = flexTranslate(row[1], "internal");
		row[4] = flexTranslate(row[4], "internal");
	}
	
	// handler-function for the autoselect-values
	rows = flexFastSelect(rows);
	if (flex_autoselect_only == true) {
		flex_autoselect_only = false;
		target.value = "";
		flexSubmit();
		return 0;
	}
 	if (rows == null || rows.length == 0) {
		target.rows = null;
		mmHideSuggBox(target);
		if (target.dynamicNotification) {
			mmFlash(target.targetIndex, "gray", 3);
		}
		return 0;
	}

	// generate a hierachical design for the flat suggest
	if (target.parameters.flexAlwaysHierarchical) {
		rows = flexGenerateHierachical(rows);
	}
	
	// row index alias generation
	for (var r = 0; r < rows.length; r++) {
		for (var f = 0; f < Number(fieldnames.length); f++) {
			var row = rows[r];
			if (f < Number(row.length)) {
				row[fieldnames[f]] = row[f];
			}
		}
	}
	// preprocessing?
	if ((typeof target.parameters.preFunction != "undefined") && (target.parameters.preFunction != null) && (target.parameters.preFunction != "")) {
		rows = target.parameters.preFunction(target, rows);
	}
	
	var insert_input = (target.parameters.flags & AS_DISPLAY_INPUT) == AS_DISPLAY_INPUT;
	if (insert_input) {
		rows.unshift(new Array(target.value, target.parameters.inputTitle));
	}
	target.rows = rows;
	target.suggCount = Number(rows.length);
	mmFillDiv(target, rows);

	mmShowSuggBox(target);
	target.rows = flexDelocalize(target.rows);
	
	// highlight the first entry automaticly
	if (target.parameters.flexHighlightFirst == true) {
		target.lastHighlightedId = 0;
		mmSelectRow(target, 0, MM_DOWNDIR);
	}
	
	return 0;
}

function mmCallSearch(target, delay) {
	// target has focus
	target.lostFocus = false;
	if (target.timeout != 0)
		window.clearTimeout(target.timeout);
	
	if (target.value.trim() != "")
		target.timeout = setTimeout("mmDoSearch(" + target.mm_refcnt + ")", delay);
	
	return 0;
}

function mmQReplace(url, target) {
	var v = target.value;
	// replace url parameters
	var turl = "";
	var inPar = false;
	var par = "";
	for (var i = 0; i < Number(url.length); i++) {
		// in parameter?
		if (inPar) {
			// end of parameter?
			if (url.charAt(i) == '$') {
				// lookahead = 1
				if ((i < url.length - 1) && (url.charAt(i + 1) == '$')) {
					i++;
					turl += '$';
				} else {
					// evaluate parameter
					if (par != "") {
						// use input value?
						if (par == 'v') {
							turl += escape(v)
							inPar = false;
						} else {
							// evaluate parameter
							var pv = escape(eval(par));
							turl += pv;
							inPar = false;
						}
					}
				}
			} else
				// still in parameter
				par += url.charAt(i);
		} else 
		// out of parameter
		if (url.charAt(i) == '$') {
			// lookahead = 1
			if ((i < Number(url.length) - 1) && (url.charAt(i + 1) == '$')) {
				i++;
				turl += '$';
			} else {
				// start parameter
				par = "";
				inPar = true;
			}
		} else
			turl += url.charAt(i);
		}
	return turl;
}

function mmDoSearch(target_id, direct) {
	var target = mm_inputs[target_id];
	target.timeout = 0;
	// lost focus in the mean time?
	if (target.lostFocus) {
		return 0;
	}
	if (Number(target.value.length) < Number(target.parameters.letterLimit)) {
		mmHideSuggBox(target);
		return 0;
	}	
	
	// call the suggest box the normal way
	mmDoSuggest(target_id, direct);
	
	return 0;
}

function callInProgress(xmlhttp) {
	switch ( xmlhttp.readyState ) {
		case 1, 2, 3:
		return true;
		break;
		
		// Case 4 and 0
		default:
		return false;
		break;
	}
}

function mmDoSuggest(target_id, direct) {
	var target = mm_inputs[target_id];
	
	// show progress bar while waiting for response
	if (target.parameters.flexProgressbar == true) {
		var rows = new Array();
		var row = new Array("", "FLEXPROGRESSBAR");
		rows.push(row);
		target.rows = rows;
		mmFillDiv(target, rows);
		mmShowSuggBox(target);
	}
	
	try {		
		if (!target.xmlhttp) return 0;
		if (!target.parameters.beforeRequest(target)) {
			return 0;
		}		
		// search running right now?
		if (callInProgress(target.xmlhttp)) {
			// cancel current search
			target.xmlhttp.onreadystatechange = function () {};		// IE bug workaround
			target.xmlhttp.abort();
			target.xmlhttp = mmGetXMLHTTP();  // Get a new xmlhttp object
		}
		var url = flexBuildUrl() + "&target_id=" + target_id + 
			// bypass caching when clickout tracking is on
			(target.parameters.clickout || target.parameters.nocache ? "&hash=" + Math.random() : "");
		if (direct) {
			target.parameters.document.location.href = url;
		} else {
			target.xmlhttp.open("GET", url, true);
			target.xmlhttp.onreadystatechange = target.async_fn;
			if (target.parameters.debug) {
				alert("Sending request: " + url);
			}
			if (!callInProgress(target.xmlhttp)) {
				target.xmlhttp.send(null);
				mm_qtime = new Date().getTime();
			}
		 }
	} catch (E) {
		if (target.parameters.debug) {
			alert("URL processing interrupted: " + E);
		}
	}
	
	if (target.suggBox == null) {
		mmCreateBox(target);
	}	
	
	return 0;
}

function mmNotifyError(target) {
	if (target.dynamicNotification) {
		mmFlash(target.targetIndex, "red", 3);
	}
	
	return 0;
}

function mmOverlapsObject(target, obj) {
	if (!obj) return false;
	var result = false;
	var l = mmGetParentProps(target.suggBox, "offsetLeft");
	var t = mmGetParentProps(target.suggBox, "offsetTop");
	var r = l + Number(target.suggBox.offsetWidth);
	var b = t + Number(target.suggBox.offsetHeight);
	var ol = mmGetParentProps(obj, "offsetLeft");
	var ot = mmGetParentProps(obj, "offsetTop");
	var or = ol + Number(obj.offsetWidth);
	var ob = ot + Number(obj.offsetHeight);
	if (ol <= l) {
		if (ot <= t)
			result = (ob > t) && (or > l);
		else 
			result = (ot <= b) && (or > l);
	} else if (ol <= r) {
		if (ot <= t)
			result = (ob > t);
		else
			result = (ot <= b);
	}
	return result;
}

function mmShowSuggBox(target) {
	if (!target.suggBox) return 0;
	target.suggBox.style.visibility = "visible";
	mmIgnoreFirstMouseEnter = firefox;
	target.suggVisible = true;
	if ((target.parameters.overlappedObjects != null) & ieZIndexBug) {
		for (var i = 0; i < Number(target.parameters.overlappedObjects.length); i++) {
			if (mmOverlapsObject(target, target.parameters.overlappedObjects[i])) {
				target.parameters.overlappedObjects[i].style.visibility = "hidden";
			} else {
				target.parameters.overlappedObjects[i].style.visibility = "visible";
			}
		}
	}
	
	return 0;
}

function mmHideSuggBox(target) {
	target.suggVisible = false;
	if (!target.suggBox) return 0;
	target.suggBox.style.visibility = "hidden";
	if ((target.parameters.overlappedObjects != null) & ieZIndexBug) {
		for (var i = 0; i < Number(target.parameters.overlappedObjects.length); i++)
			if (target.parameters.overlappedObjects[i])
				target.parameters.overlappedObjects[i].style.visibility = "visible";
	}
	
	return 0;
}

function mmOnResize() {
	for (i = 0; i < Number(mm_inputs.length); i++) {
		mmSetDivSize(mm_inputs[i]);
	}
	
	return 0;
}

function mmCreateBox(input) {
	
	if (input.created) return true;
	
	// return false if target document not yet loaded (only ie)
	if (ie && (input.parameters.document.readyState != "complete")) return false;

	// create visual element
	input.suggBox = input.parameters.document.createElement("div");
	input.suggBox.className = "suggBox";
	input.suggBox.name = "suggBox" + input.mm_refcnt;
	input.suggBox.id = "suggBox" + input.mm_refcnt;
	input.suggBox.style.zIndex = input.parameters.flexZIndex; //raise the box

	input.parameters.document.body.appendChild(input.suggBox);
	mmSetDivSize(input);
	input.created = true;
	return true;
}

/** Returns true if setup is successful */
function SetupMMSuggest(input, args) {
	var old_input = input;
	input = flexInit(old_input);
	
	// Internet Explorer 5.0 and below can't handle MMSuggest code
	if (ie && (parseInt(ieVersion) <= 5.0)) return 0;
	try {
		// create parameter container
		input.parameters = new mmSuggestParams();
		for (var i in args) {
			if (typeof input.parameters[i] != "undefined") {
				input.parameters[i] = args[i];
			} else {
				// parameter specification error
				alert("SetupMMSuggest parameter undefined: " + i + "=" + args[i]);
			}	
		}		
		input.parameters.clickOut = false;
		input.suggBox = null;			// suggestion box (DIV)
		input.suggVisible = false;
		input.xmlhttp = null;
		input.lastHighlightedId = -1;
		input.suggCount = -1;
		input.dynamicNotification = false;
		input.timeout = 0;
		
		// flexform-suggest: overwrite some parameters
		input.parameters.clickout = false;
		input.parameters.onActivate = flexOnActivate;
		input.parameters.flags = AS_TABSELECTS + AS_NOLOGO;
		input.parameters.oneColumn = false;
		input.parameters.headerFunction = null;
		input.parameters.rowFunction = null;
		input.parameters.footerFunction = null;
		input.parameters.preFunction = null;
		input.parameters.navigationPos = "bottom";
		input.parameters.pageSize = 0;
      input.parameters.initialWidth = input.offsetWidth;
		
		input.xmlhttp = mmGetXMLHTTP();
		if (!input.xmlhttp) {
			return false;
		}
	
		// setup input field	
		input.autocomplete = "off";
		input.setAttribute("autocomplete", "off");
		input.oldBlur = input.onblur;
		input.onblur = mmDoBlur;
		input.oldFocus = input.onfocus;
		input.onfocus = mmDoFocus;
		input.onkeydown = mmDoFieldKeyDown;
			
		// remember input field
		mm_inputs.push(input);
		input.targetIndex = mm_inputs.length - 1;
		// setup state change fn
		var fn = function() {
			if (input.xmlhttp.readyState == 4)
				if (input.xmlhttp.responseText.charAt(0) == "<") {
					if (input.parameters.debug) {
						alert("Error: Received XML or HTML reply"); 
					}
				} else {
					try {
						if (input.parameters.debug) {
							alert("Received response: " + input.xmlhttp.responseText); 
						}
						eval(input.xmlhttp.responseText);
						return 0;
					} catch (e) {
						if (input.parameters.debug) {
							alert('Error executing the response: ' + e); 
						}
					}
					// try to fix possible quotation mistakes
					var txt = input.xmlhttp.responseText.replace(/\'/g, "\\\\'");
					try {
						eval(txt);
					} catch (e) {
						if (input.parameters.debug) {
							alert('Error executing the response: ' + e); 
						}
						mmNotifyError(input);
						mmHideSuggBox(input);
					}
				}
		};

		input.async_fn = fn;
		input.xmlhttp.onreadystatechange = input.async_fn;
	
		input.mm_refcnt = mm_refcnt;
		mm_refcnt++;
		
		// deferred creation only for Internet Explorer
		if (ie)
			input.created = false;
		else
			mmCreateBox(input);
	
		window.oldResize = window.onresize;
		window.onresize = mmOnResize;
		
		// suggest function for direct calls
		input.suggest = function() {
			window.setTimeout(function() {
				input.focus();
				input.lostFocus = false;
				mmDoSuggest(input.mm_refcnt, false);
			}, 50)
		};
		
		// preload images for paged view?
		if (input.parameters.pageSize > 0) {
			var i1 = new Image();
			i1.src = input.parameters.iconPath + "leftarrow.png";
			var i2 = new Image();
			i2.src = input.parameters.iconPath + "leftarrow_inactive.png";
			var i3 = new Image();
			i3.src = input.parameters.iconPath + "rightarrow.png";
			var i4 = new Image();
			i4.src = input.parameters.iconPath + "rightarrow_inactive.png";
		}
				
		if (input.parameters.debug) {
			alert("Setup successful for MMSuggest on component " + input.name);
		}
		
		flexSetInputValue();
		
		// set the timeout for the beginners help
		if (input.parameters.flexBeginnerTimeout > 0) {
			flexSetAttribute(input, "onfocus", "setTimeout('flexBeginnerHelp()', " + input.parameters.flexBeginnerTimeout + ")");
			setTimeout('flexBeginnerHelp()', input.parameters.flexBeginnerTimeout);
		}
		
		// init some values
		flex_act_max_tab_width = input.parameters.flexMaxTabWidth;
		
		return true;
		
	} catch (e) {
		alert("MMSuggest Error:\n" + e);
		return false;
	}	
	
	return true;
}

