/**************************************************************************************/
/* Global variables:                                                                  */
/*                                                                                    */
/* marketKey      = market key, e.g. '7-6-147'                                        */
/* entriesPerPage = number of result entries per page                                 */
/* cookieDays     = number of days to store cookies                                   */
/* allowNoPrice   = view vehicles with no price or 0 EUR (true = yes, false = no)     */
/*																					  */
/* vehicleTypes   = array with vehicle types, e.g. [ 1, 2, 3, 4, 5, 6 ]               */
/* preselect      = array with preselected fields, e.g. { manufacturer: 36 }          */
/* showExtras     = array with vehicle extras, e.g. { aircondition: 8 }               */
/*                                                                                    */
/* contents       = array with site contents, e.g.                                    */
/*                  result:  { header: { xml: '...', xsl: '...', box: '...' },        */
/*                             body:   { xml: '...', xsl: '...', box: '...' } },      */
/*                  details: { header: { xml: '...', xsl: '...', box: '...' },        */
/*                             body:   { xml: '...', xsl: '...', box: '...' } }       */
/*                                                                                    */
/* xsltCallBack() = callback function (called after XSL transformation)               */
/* initCallBack() = callback function (called after initialization)                   */
/**************************************************************************************/

/* global variables */
var market = null;

/* car market creation + initialization */
function newCarMarket(frmName, debug, noHistory, noMask) {
	if(typeof(MdxAjax) != 'function') {
		alert('MdxAjax class needed');
	}
	else if(typeof(MdxXslTransform) != 'function') {
		alert('MdxXslTransform class needed');
	}
	else {
		market = new MdxCarMarket(frmName, debug, noHistory, noMask);

		if(market) {
			market.initStart(frmName);
			return true;
		}
		else alert('Could not create MdxCarMarket instance');
	}
	return false;
}

/**************************************************************************************/
/* Car Market Class                                                                   */
/*                                                                                    */
/* frmName   = search form name                                                       */
/* debug     = view debug info (true or false)                                        */
/* noHistory = disable history emulation (true or false)                              */
/* noMask    = don't load search mask automatically (true or false)                   */
/**************************************************************************************/

function MdxCarMarket(frmName, debug, noHistory, noMask) {

/* Member variables *******************************************************************/

	this.cache = {};
	this.userSelect = {};
	this.popup = null;
	this.frmName = '';
	this.browserId = '';
	this.carType = (typeof(vehicleTypes) == 'object') ? vehicleTypes.join('||') : 1;
	this.section = '';
	this.curAction = '';
	this.lastAction = '';
	this.actionBeforeDetails = '';
	this.iFrame = null;
	this.iFrameUrl = '';
	this.iv = 0;
	this.timer = 0;
	this.pages = 0;
	this.debug = debug;
	this.noHistory = noHistory;
	this.noMask = noMask;
	this.pageCnt = 10;

	this.mdxAjax = new MdxAjax();
	this.getFormData = this.mdxAjax.getFormData;
	this.getFieldValue = this.mdxAjax.getFieldValue;

/* Initialization functions ***********************************************************/

	/*
	  frmName = search form name, optional
	*/
	this.initStart = function(frmName) {
		var action = '';
		this.initCache(); /* must be initialized here to avoid error when redirecting */
		var query;

		if(location.search && !this.noHistory) {
			query = this.getQuery();

			if(query['chiffre']) {
				action = '#' + this.b64encode('viewDetails::' + query['chiffre']);
			}
			else if(query['dkey']) {
				action = '#' + this.b64encode('viewResultList::dkey=' + query['dkey'] + '&sort=price');
			}
			else if(query['searchstring']) {
				action = '#' + this.b64encode('viewResultList::searchstring=' + query['searchstring'] + '&sort=price');
			}
			else action = '#' + this.b64encode('viewHome');
			location.replace(location.href.replace(/\?.*/, action));
		}
		else {
			if(!this.noHistory) {
				if(!location.hash) {
					action = '#' + this.b64encode('viewHome');
					location.replace(location.href + action);
				}
				action = this.b64decode(location.hash.substr(1));

				if(window.ActiveXObject || window.opera) {
					var scriptTags = document.getElementsByTagName('script');
					var src = '';

					for(var i = 0; i < scriptTags.length; i++) {
						src = scriptTags[i].getAttribute('src');

						if(src.indexOf('class_car_market.js') != -1) {
							this.iFrameUrl = src.replace(/\/[^\/]+$/, '/iframe.htm');
							break;
						}
					}
					this.createHiddenFrame();
				}
			}
			if(frmName == null) frmName = 0;
			this.frmName = frmName;
			this.initUserSelect();
			this.browserId = this.getCookie('browserId');

			if(!this.browserId) {
				var d = new Date();
				var ms = d.getTime();
				var rn = Math.round(Math.random() * 10000);
				this.browserId = 'mdx' + ms + rn;
				this.setCookie('browserId', this.browserId, cookieDays, '/');
			}
			if(this.debug) this.createDebugBox();
			this.createMessageBox();
			this.createDarkScreen();
			if(!this.noMask) this.loadSection('searchmask');

			if(this.noHistory) {
				if(location.search) {
					query = this.getQuery();

					if(query['chiffre']) {
						this.viewDetails(query['chiffre']);
					}
					else if(query['dkey']) {
						this.viewResultList('dkey=' + query['dkey'] + '&sort=price');
					}
					else if(query['searchstring']) {
						this.viewResultList('searchstring=' + query['searchstring'] + '&sort=price');
					}
					else this.viewHome();
				}
				else this.viewHome();
			}
			else this.setAction(action, true);

			if(action.match(/^viewDetails/)) {
				if(contents.details_result.body.box != contents.result.body.box) {
					this.loadSection('start');
				}
			}
			if(typeof(initCallBack) == 'function') {
				initCallBack();
			}
		}
	}

	this.initUserSelect = function() {
		var f = document.forms[this.frmName];

		if(f) {
			var field = '';

			for(i = 0; i < f.elements.length; i++) {
				field = f.elements[i].name;
				this.userSelect[field] = '';
			}
		}
	}

	this.createDebugBox = function() {
		var e = document.createElement('div');
		e.id = 'debugBox';
		e.style.position = 'absolute';
		e.style.width = '95%';
		e.style.height = '18%';
		e.style.top = '78%';
		e.style.bottom = '2%';
		e.style.left = '2%';
		e.style.padding = '5px';
		e.style.whiteSpace = 'nowrap';
		e.style.zIndex = 69;
		e.style.overflow = 'auto';
		e.style.backgroundColor = '#F0F8FF';
		e.style.border = '1px dashed #C0D0E0';
		document.body.appendChild(e);
		this.setOpacity(document.getElementById('debugBox'), 0.8);
	}

	this.createMessageBox = function() {
		var e = document.createElement('div');
		e.id = 'messageBox';
		e.className = 'messageBoxInfo';
		e.style.position = 'absolute';
		e.style.top = 0;
		e.style.left = 0;
		e.style.visibility = 'hidden';
		e.style.zIndex = 59;
		e.style.textAlign = 'center';
		document.body.appendChild(e);

		e = document.createElement('div');
		e.id = 'messageBoxXml';
		e.className = 'messageBoxXml';
		e.style.position = 'absolute';
		e.style.top = 0;
		e.style.left = 0;
		e.style.visibility = 'hidden';
		e.style.zIndex = 58;
		document.body.appendChild(e);
	}

	this.createDarkScreen = function() {
		document.body.style.height = '100%';
		var e = document.createElement('div');
		e.id = 'darkScreen';
		e.style.position = 'absolute';
		e.style.top = '0px';
		e.style.left = '0px';
		e.style.width = '100%';
		e.style.height = '100%';
		e.style.backgroundColor = '#000000';
		e.style.display = 'none';
		e.style.zIndex = 57;
		document.body.appendChild(e);
	}

	this.createHiddenFrame = function() {
		document.write('<iframe name="__IEFrame__" href="' + this.iFrameUrl +
		               '" style="display:none;"></iframe>');
		this.iFrame = window.frames['__IEFrame__'];
	}

/* XSLT functions *********************************************************************/

	/*
	  xslPath    = path to XSLT stylesheet, optional
	  xmlPath    = path to XML document, optional
	  targetElem = target element (object), optional
	  cache      = cache array, optional
	  callback   = callback function (can also be an array), optional
	  loadInfo   = loading info message, optional
	*/
	this.xslTransform = function(xslPath, xmlPath, targetElem, cache, callBack, loadInfo) {
		var xsltObj = new MdxXslTransform(targetElem, cache, callBack, loadInfo);

		if(xsltObj) {
			var xsltInfo = xsltObj.transform(xslPath, xmlPath);
			this.addDebugInfo(xsltInfo + '<br/>');
		}
	}

	/*
	  xml = path to XML document
	*/
	this.buildXmlLink = function(xml) {
		xml += ((xml.indexOf('?') == -1) ? '?' : '&') + 'mkey=' + escape(marketKey);
		xml += '&browser_id=' + this.browserId;
		xml += '&ctype=' + this.carType;
		if(typeof(allowNoPrice) != 'undefined' && allowNoPrice) xml += '&allow_no_price=1';
		return xml;
	}

	/*
	  section   = content section, e.g. 'result', 'searchmask', 'pinboard_view'
	  container = content container, e.g. 'header', 'body', 'offers'
	*/
	this.loadContent = function(section, container) {
		var content = contents[section][container];
		var box = document.getElementById(content.box);

		if(box || content.box == 'popup') {
			var cache = this.cache[section];
			var page = cache.page;
			var xsl = content.xsl;
			var xml = content.xml;
			var limit = (section == 'result') ? entriesPerPage : 0;
			var loadInfo = contents.loadinfo;
			var loadInfoMask = contents.loadinfomask;
			var callBack = [];

			if(content.reloadData) {
				this.clearCache(section, container, 'xmlPath');
			}
			switch(section) {
				case 'result':
					if(container == 'header') {
						callBack.push(this.createPageNumbers.bind(this));
						callBack.push(this.switchListView.bind(this));
					}
					break;
				case 'pinboard_remove':
				case 'pinboard_remove_all':
					callBack.push(this.loadSection.bind(this, 'pinboard_view'));
					loadInfo = '';
					break;
				case 'favdealers_remove':
				case 'favdealers_remove_all':
					callBack.push(this.loadSection.bind(this, 'favdealers_view'));
					loadInfo = '';
					break;
				case 'pinboard_counter':
				case 'favdealers_counter':
					this.clearCache(section, container, 'xmlPath');
					loadInfo = '';
					break;
				case 'pinboard_add':
				case 'favdealers_add':
					loadInfo = '';
					break;
				case 'searchmask':
				case 'searchmask_small':
					callBack.push(this.maskCallBack.bind(this));
					loadInfo = '';
					if(loadInfoMask) {
						var cont = document.getElementById('loadInfoMask');
						if(cont) cont.innerHTML = loadInfoMask;
					}
					break;
			}
			if(typeof(content.defaultText) == 'string') {
				loadInfo = content.defaultText;
			}
			if(content.callBack) {
				switch(typeof(content.callBack)) {
					case 'object':
						for(var i = 0; i < content.callBack.length; i++) {
							callBack.push(content.callBack[i]);
						}
						break;
					case 'function':
						callBack.push(content.callBack);
						break;
				}
			}
			if(xml) {
				xml = this.buildXmlLink(xml);

				if(section.substr(0, 10) == 'searchmask' && !cache.param) {
					cache.param = this.createParamList(section);
				}
				else {
					if(limit) xml += '&limit=' + limit;
					if(page && container != 'header') xml += '&page=' + page;
				}
				if(cache.param) xml += '&' + cache.param;
			}
			if(content.box == 'popup') {
				this.openPopup(content.width, content.height, xsl, xml);
			}
			else if(content.box == 'messageBoxXml') {
				this.viewMessageBoxXml(xsl, xml);
			}
			else {
				this.xslTransform(xsl, xml, box, cache[container], callBack, loadInfo);
				if(section != 'searchmask') this.viewBox(box);
			}
		}
	}

	this.loadSection = function(section) {
		if(section) {
			for(var container in contents[section]) {
				this.loadContent(section, container);
			}
		}
	}

	this.maskCallBack = function() {
		this.checkFormFields();
		document.body.style.cursor = 'auto';

		if(contents.loadinfomask) {
			var cont = document.getElementById('loadInfoMask');

			if(cont) {
				var icon = contents.iconinfomask ? contents.iconinfomask : '';
				cont.innerHTML = icon;
			}
		}
		var obj = document.getElementById('maskCover');
		if(obj) this.hideBox(obj);
	}

/* History functions ******************************************************************/

	/*
	  func = function name
	  args = arguments list (array), optional
	*/
	this.historyAdd = function(func, args) {
		if(func) {
			var action = func;

			if(args) {
				if(typeof(args) != 'object') args = [args];
				var param = '';

				for(var i = 0; i < args.length; i++) {
					if(typeof(args[i]) != 'undefined') {
						if(func == 'viewResultList' && !i && !args[i]) {
							args[i] = this.cache.result.param;
						}
						if(i) param += ';;';
						param += args[i];
					}
				}
				if(param) action += '::' + param;
			}
			if(func == 'viewDetails') {
				var curAction = this.b64decode(this.curAction);
				if(!curAction.match(/^viewDetails/)) {
					this.actionBeforeDetails = curAction;
				}
			}
			if(this.noHistory) this.render(action);
			else this.setAction(action);
		}
	}

	this.historyLoad = function() {
		var action = '';
		if(this.iFrame) action = this.iFrame.location.search.substr(1);
		else action = location.hash.substr(1);

		if(action && action != this.curAction) {
			this.curAction = action;
			action = this.b64decode(action);
			this.render(action);
		}
	}

	this.historyBack = function() {
		if(this.lastAction && this.lastAction != this.curAction) {
			history.back();
		}
		else {
			var action = this.b64decode(location.hash.substr(1));

			if(action.match(/^viewDetails/)) {
				var arr = action.split(';;');

				if(typeof(arr[3]) != 'undefined') {
					this.render('viewResultList::' + arr[3]);
				}
				else this.render('viewHome');
			}
			else this.render('viewHome');
		}
	}

	/*
	  action = function and arguments, e.g. 'viewResultList::sort=timestamp;;4'
	  noSave = don't save action (true or false)
	*/
	this.setAction = function(action, noSave) {
		if(this.iv) clearInterval(this.iv);
		action = this.b64encode(action);

		if(!noSave) {
			this.lastAction = this.curAction;
		}
		if(this.iFrame) {
			this.iFrame.location.href = this.iFrameUrl + '?' + action;
		}
		location.hash = '#' + action;
		this.iv = setInterval(this.historyLoad.bind(this), 200);
	}

/* Rendering functions ****************************************************************/

	this.render = function(action) {
		this.hideMessageBox();
		this.hideMessageBox('messageBoxXml');
		var a = action.split('::');
		var funct = a[0];
		var args = a[1] ? a[1].split(';;') : [];

		switch(funct) {
			case 'viewFavDealers':
				var section = 'favdealers_view';
				var cache = this.cache[section];
				cache.param = '';
				this.loadSection(section);
				break;

			case 'viewPinboard':
				var section = 'pinboard_view';
				var cache = this.cache[section];
				cache.param = '';
				this.loadSection(section);
				break;

			case 'viewUserSearches':
				this.loadSection('usersearches');
				break;

			case 'viewDetails':
				var vkey = args[0];
				var type = args[1];
				var section = args[2];
				var paramList = args[3];
				var trackVal = args[4];

				if(vkey) {
					if(!section || !this.cache[section]) {
						section = this.section ? this.section : 'details_result';
					}
					if(type == null) type = 0;
					this.section = section;
					var cache = this.cache[section];

					if(section != 'details_pinboard' && !paramList) {
						if(this.cache.result.param) {
							paramList = this.cache.result.param;
							paramList = paramList.replace(/&?limit=[^&]*/, '');
							paramList = paramList.replace(/&?page=[^&]*/, '');
						}
						else paramList = 'sort=price';
					}
					if(type != 0) {
						vkey = parseInt(vkey);
						if(isNaN(vkey)) vkey = 1;
						else if(vkey < 1) vkey = 1;

						if(type == 1) {
							var page = 1;

							if(section == 'details_result' && this.cache.result.page) {
								page = this.cache.result.page;
							}
							vkey = (page - 1) * entriesPerPage + vkey;
							type = 2;
						}
						cache.param = 'limit=1&page=' + vkey;
						if(paramList) cache.param += '&' + paramList;
					}
					else cache.param = 'chiffre=' + escape(vkey);
					this.loadSection(section);
					if(trackVal) this.pageTracker(trackVal, 'Detailansicht');
				}
				else this.viewHome();
				break;

			case 'viewResultList':
				var paramList = args[0];
				var page = args[1];
				var xsl = args[2];
				var section = 'result';
				var cache = this.cache[section];
				if(page) cache.page = parseInt(page);
				if(xsl) contents.result.body.xsl = xsl;

				if(cache.param != paramList) {
					this.clearCache('searchstring', 'body', 'xmlPath');
				}
				switch(paramList) {
					case 'new':
						cache.param = '';
						this.sortResultList('price');
						break;
					default:
						if(paramList) cache.param = paramList;
						if(paramList || !page) {
							this.loadSection(section);
						}
						else {
							this.loadContent(section, 'body');
							this.createPageNumbers();
						}
				}
				break;

			case 'compareVehicles':
				var paramList = args[0];
				var section = 'result_compare';
				var cache = this.cache[section];
				cache.param = paramList;
				this.loadSection(section);
				break;

			case 'viewHome':
			default:
				this.loadSection('start');
		}
	}

/* Debug functions ********************************************************************/

	/*
	  msg = message string
	*/
	this.addDebugInfo = function(msg) {
		var debug = document.getElementById('debugBox');

		if(debug) {
			debug.innerHTML += msg;
			debug.scrollTop += 40;
		}
	}

/* Start page functions ***************************************************************/

	this.viewHome = function() {
		this.historyAdd('viewHome', arguments);
	}

/* Result list functions **************************************************************/

	/*
	  paramList = search arguments (e.g. 'model=28&color=5') or 'new', optional
	  page      = page number or '+' or '-', optional
	  xsl       = path to XSLT stylesheet, optional
	*/
	this.viewResultList = function(paramList, page, xsl) {
		if(!page) page = 1;
		else page = this.getPageNumber('result', page);
		this.historyAdd('viewResultList', [paramList, page, xsl]);
	}

	/*
	  field    = search field name
	  noHeader = don't update header (true or false)
	  noView   = don't view result list (true or false)
	  order    = search order ('ASC' or 'DESC'), optional
	*/
	this.sortResultList = function(field, noHeader, noView, order) {
		var section = 'result';
		var cache = this.cache[section];
		var paramList = cache.param;
		var f = document.forms[this.frmName];
		if(order) order = order.toUpperCase();
		if(order != 'ASC' && order != 'DESC') order = 'ASC';

		if(f) {
			if(f.sort) {
				this.setField(f.sort, field);
				this.saveField(f.sort);
			}
			if(f.order) {
				this.setField(f.order, field);
				this.saveField(f.order);
			}
		}
		if(paramList) {
			if(paramList.indexOf('sort=') != -1) {
				paramList = paramList.replace(/sort=[^&]*/, 'sort=' + field);
			}
			else paramList += '&sort=' + field;

			if(paramList.indexOf('order=') != -1) {
				paramList = paramList.replace(/order=[^&]*/, 'order=' + order);
			}
			else paramList += '&order=' + order;
		}
		else paramList = 'sort=' + field + '&order=' + order;

		if(noView || noHeader) {
			cache.page = 1;
			cache.param = paramList;
		}
		if(!noView) {
			if(noHeader) this.loadContent(section, 'body');
			else this.viewResultList(paramList, 1);
		}
	}

	/*
	  xsl = path to XSLT stylesheet, optional
	  xml = path to XML document, optional
	*/
	this.switchListView = function(xsl, xml) {
		var section = 'result';
		var xslList = contents[section].body.xsl;
		var str = xsl ? xsl : xslList;
		var img1 = xslList.substring(xslList.lastIndexOf('_') + 1, xslList.lastIndexOf('.'));
		var img2 = str.substring(str.lastIndexOf('_') + 1, str.lastIndexOf('.'));

		this.replaceImage(img1, 'view_' + img1 + '.gif');
		this.replaceImage(img2, 'view_' + img2 + '_active.gif');

		if(xsl) contents.result.body.xsl = xsl;
		if(xsl || xml) this.loadContent(section, 'body');
	}

	/*
	  frmName = form name
	*/
	this.compareVehicles = function(frmName) {
		var f = document.forms[frmName];

		if(f) {
			var p = [];

			for(var i = 0; i < f.elements.length; i++) {
				if(f.elements[i].checked) p.push(f.elements[i].value);
			}
			if(p[0]) {
				var param = 'chiffre=' + p.join('||') + '&sort=timestamp';
				this.historyAdd('compareVehicles', param);
			}
			else this.viewMessageBox(cmMsg['selectVehicle'], 'Error', 2);
		}
	}

/* Page number functions **************************************************************/

	/*
	  section = section name
	  page    = page number or '+' or '-'
	*/
	this.getPageNumber = function(section, page) {
		var cache = this.cache[section];
		if(!cache.page) cache.page = 1;

		switch(page) {
			case '+': page = cache.page + 1; break;
			case '-': page = cache.page - 1; break;
			default:
				page = parseInt(page);
				if(isNaN(page)) page = cache.page;
		}
		if(this.pages && page > this.pages) page = this.pages;
		else if(page < 1) page = 1;
		return page;
	}

	this.createPageNumbers = function() {
		var obj = document.getElementById(contents.resultpages);

		if(obj) {
			if(typeof(entriesPerPage) == 'string') {
				entriesPerPage = parseInt(entriesPerPage);
			}
			var section = 'result';
			var cache = this.cache[section].body;
			var page = this.cache[section].page ? this.cache[section].page : 1;
			var found = parseInt(document.fResult.found.value);
			var pages = (found > entriesPerPage) ? Math.ceil(found / entriesPerPage) : 1;
			var mid = Math.floor(this.pageCnt / 2);
			var from = (page > mid) ? page - mid : 1;
			var to = from + this.pageCnt - 1;

			if(to > pages) {
				to = pages;
				from = to - this.pageCnt + 1;
				if(from < 1) from = 1;
			}
			obj.innerHTML = '&nbsp;';

			if(page > 1) {
				obj.innerHTML += '<a class="pageNumbersUnselected" href="javascript:market.viewResultList(\'\', ' + (page - 1) + ')">&lt;&lt;</a>&nbsp;';
			}
			for(var i = from; i <= to; i++) {
				if(i == page) obj.innerHTML += '<strong class="pageNumbersSelected">' + i + '</strong>&nbsp;';
				else obj.innerHTML += '<a class="pageNumbersUnselected" href="javascript:market.viewResultList(\'\', ' + i + ')">' + i + '</a>&nbsp;';
			}
			if(page < pages) {
				obj.innerHTML += '<a class="pageNumbersUnselected" href="javascript:market.viewResultList(\'\', ' + (page + 1) + ')">&gt;&gt;</a>&nbsp;';
			}
			this.pages = pages;
		}
	}

/* Details functions ******************************************************************/

	/*
	  vkey      = vehicle chiffre or vehicle position
	  type      = vkey type (0 = chiffre, 1 = relative pos., 2 = absolute pos.); optional
	  section   = details section name, optional
	  paramList = search arguments, e.g. 'model=5&color=1'; optional
	  trackVal	= Google tracker value, optional
	*/
	this.viewDetails = function(vkey, type, section, paramList, trackVal) {
		this.historyAdd('viewDetails', arguments);
	}

	/*
	  vkey    = vehicle key
	  section = section name, optional
	*/
	this.printDetails = function(vkey, section) {
		if(!section) section = 'details_print';
		var cache = this.cache[section];
		cache.param = 'chiffre=' + escape(vkey);
		this.loadSection(section);
	}

	/*
	  vkey = vehicle key
	*/
	this.xxlPictures = function(vkey) {
		var section = 'xxlpictures';
		var cache = this.cache[section];
		cache.param = 'chiffre=' + escape(vkey);
		this.loadSection(section);
	}

/* Pinboard functions *****************************************************************/

	this.viewPinboard = function() {
		this.historyAdd('viewPinboard', arguments);
	}

	this.printPinboard = function() {
		var section = 'pinboard_print';
		var cache = this.cache[section];
		cache.param = '';
		this.loadSection(section);
	}

	this.clearPinboard = function() {
		var section = 'pinboard_remove_all';
		var cache = this.cache[section];
		cache.param = '';
		if(cache.body) cache.body.xmlPath = '';
		this.clearCache('pinboard_view', 'header', 'xmlPath');
		this.clearCache('pinboard_view', 'body', 'xmlPath');
		this.loadSection(section);
		this.viewMessageBox(cmMsg['pinboardCleared'], 'Info', 2);
	}

	this.viewPinboardCounter = function() {
		var section = 'pinboard_counter';
		var cache = this.cache[section];
		cache.param = '';
		this.loadSection(section);
	}

	/*
	  vkey 	   = vehicle key
	  trackVal = Google tracker value, optional
	*/
	this.addVehicle = function(vkey, trackVal) {
		if(vkey) {
			var section = 'pinboard_add';
			var cache = this.cache[section];
			cache.param = 'chiffre=' + escape(vkey);
			if(cache.body) cache.body.xmlPath = '';
			this.clearCache('pinboard_view', 'header', 'xmlPath');
			this.clearCache('pinboard_view', 'body', 'xmlPath');
			this.loadSection(section);
			this.viewMessageBox(cmMsg['pinboardVehicleSaved'], 'Info', 2);
			if(trackVal) this.pageTracker(trackVal, 'Merkzettel');
		}
	}

	/*
	  vkey = vehicle key
	*/
	this.removeVehicle = function(vkey) {
		if(vkey) {
			var section = 'pinboard_remove';
			var cache = this.cache[section];
			cache.param = 'chiffre=' + escape(vkey);
			if(cache.body) cache.body.xmlPath = '';
			this.clearCache('pinboard_view', 'header', 'xmlPath');
			this.clearCache('pinboard_view', 'body', 'xmlPath');
			this.loadSection(section);
			this.viewMessageBox(cmMsg['pinboardVehicleRemoved'], 'Info', 2);
		}
	}

/* User searches functions ************************************************************/

	this.viewUserSearches = function() {
		this.historyAdd('viewUserSearches', arguments);
	}

/* Favorite dealers functions *********************************************************/

	this.viewFavDealers = function() {
		this.historyAdd('viewFavDealers', arguments);
	}

	this.clearFavDealers = function() {
		var section = 'favdealers_remove_all';
		var cache = this.cache[section];
		cache.param = '';
		if(cache.body) cache.body.xmlPath = '';
		this.clearCache('favdealers_view', 'header', 'xmlPath');
		this.clearCache('favdealers_view', 'body', 'xmlPath');
		this.loadSection(section);
		this.viewMessageBox(cmMsg['favDealersCleared'], 'Info', 2);
	}

	this.viewFavDealersCounter = function() {
		var section = 'favdealers_counter';
		var cache = this.cache[section];
		cache.param = '';
		this.loadSection(section);
	}

	/*
	  dkey = dealer key
	*/
	this.addDealer = function(dkey) {
		if(dkey) {
			var section = 'favdealers_add';
			var cache = this.cache[section];
			cache.param = 'dkey=' + escape(dkey);
			if(cache.body) cache.body.xmlPath = '';
			this.clearCache('favdealers_view', 'header', 'xmlPath');
			this.clearCache('favdealers_view', 'body', 'xmlPath');
			this.loadSection(section);
			this.viewMessageBox(cmMsg['favDealerSaved'], 'Info', 2);
		}
	}

	/*
	  dkey = dealer key
	*/
	this.removeDealer = function(dkey) {
		if(dkey) {
			var section = 'favdealers_remove';
			var cache = this.cache[section];
			cache.param = 'dkey=' + escape(dkey);
			if(cache.body) cache.body.xmlPath = '';
			this.clearCache('favdealers_view', 'header', 'xmlPath');
			this.clearCache('favdealers_view', 'body', 'xmlPath');
			this.loadSection(section);
			this.viewMessageBox(cmMsg['favDealerRemoved'], 'Info', 2);
		}
	}

/* Search form functions **************************************************************/

	this.getExtras = function() {
		var ids = [];
		var param = '';

		for(var key in showExtras) {
			ids.push(showExtras[key]);
		}
		if(ids[0]) param += 'show_extras=' + ids.join(',');
		return param;
	}

	this.getPreselect = function() {
		var p = [];
		var param = '';

		for(var key in preselect) {
			p.push(key + '=' + escape(preselect[key]));
			this.userSelect[key] = preselect[key];
		}
		if(p[0]) param += p.join('&');
		return param;
	}

	/*
	  section = section name
	*/
	this.createParamList = function(section) {
		var param = '';

		if(section.substr(0, 10) == 'searchmask') {
			param = this.getFormData(this.frmName, true, 'PLZ', '', this.userSelect);

			if(showExtras) {
				var extras = this.getExtras();
				if(extras) param += (param ? '&' : '') + extras;
			}
			if(typeof(preselect) == 'object') {
				var preSel = this.getPreselect();
				if(preSel) param += (param ? '&' : '') + preSel;
				preselect = null;
			}
		}
		else {
			param = this.getFormData(this.frmName, true, 'PLZ', '_sv_', this.userSelect);
		}
		return param;
	}

	/*
	  field = search form field (object)
	  val   = field value
	*/
	this.setField = function(field, val) {
		if(field) {
			if(field.type.indexOf('select') != -1) {
				field.selectedIndex = 0;

				for(var i = 0; i < field.options.length; i++) {
					if(field.options[i].value == val) {
						field.selectedIndex = i;
						break;
					}
				}
			}
			else if(field.type == 'checkbox') {
				field.checked = val ? true : false;
			}
			else field.value = val;
		}
	}

	/*
	  field   = search form field (object)
	  section = section name, optional
	*/
	this.saveField = function(field, section) {
		if(field) {
			var val = this.getFieldValue(field);

			if(val) this.userSelect[field.name] = val;
			else {
				var fname = (field.name == 'radius') ? 'radius/zip' : field.name;
				this.resetField(fname, true, section);
			}
		}
	}

	/*
	  fieldName = search field name(s) separated by "/"
	  noReload  = don't reload search mask (true or false)
	  section   = section name, optional
	*/
	this.resetField = function(fieldName, noReload, section) {
		if(!section) section = 'searchmask';
		var cache = this.cache[section];
		var update = false;
		var fields = [];

		if(fieldName.indexOf('/') != -1) {
			fields = fieldName.split('/');
		}
		else fields = [fieldName];

		for(var i = 0; i < fields.length; i++) {
			if(this.userSelect[fields[i]]) {
				this.userSelect[fields[i]] = '';
				this.clearField(fields[i]);
				if(noReload) this.replaceImage(fieldName, 'none.gif');
				update = true;
			}
		}
		if(update) {
			if(noReload) cache.param = this.createParamList(section);
			else this.updateForm(null, '', section);
		}
	}

	/*
	  field = field name
	*/
	this.clearField = function(field) {
		var frm = document.forms[this.frmName];

		if(frm) {
			var elem = frm[field];
			if(elem) this.setField(elem, '');
		}
	}

	/*
	  field = search form field (object)
	  val   = field value
	*/
	this.checkField = function(field, val) {
		if(field.type.indexOf('select') != -1) {
			for(var i = 0; i < field.options.length; i++) {
				if(field.options[i].value == val) {
					field.selectedIndex = i;
					break;
				}
			}
		}
	}

	this.checkFormFields = function() {
		var f = document.forms[this.frmName];

		if(f) {
			var e = f.elements;

			for(var i = 0; i < e.length; i++) {
				if(e[i].type.indexOf('select') != -1 && !this.userSelect[e[i].name]) {
					if(e[i].name != 'order') {
						e[i].disabled = (e[i].options.length > 2) ? false : true;
					}
				}
			}
			if(this.userSelect.radius) {
				this.setField(e.radius, this.userSelect.radius);
			}
			if(this.userSelect.sort) {
				this.setField(e.sort, this.userSelect.sort);
			}
			if(this.userSelect.order) {
				this.setField(e.order, this.userSelect.order);
			}
			for(var key in this.userSelect) {
				if(typeof(f[key]) != 'undefined' && this.userSelect[key] != '') {
					if(this.getFieldValue(f[key]) != this.userSelect[key]) {
						this.checkField(e[key], this.userSelect[key]);
					}
					if(!document.images[key]) {
						key = key.substr(0, key.lastIndexOf('_'));
					}
					if(document.images[key]) {
						this.replaceImage(key, 'icon_reset_arrow.gif');
					}
				}
			}
		}
	}

	this.disableForm = function() {
		var obj = document.getElementById('maskCover');

		if(obj) {
			obj.style.width = obj.parentNode.offsetWidth + 'px';
			obj.style.height = obj.parentNode.offsetHeight + 'px';
			this.setOpacity(obj, 0.5);
			this.viewBox(obj);
		}
		var f = document.forms[this.frmName];

		if(f) {
			var e = f.elements;

			for(var i = 0; i < e.length; i++) {
				e[i].disabled = true;
			}
		}
		document.body.style.cursor = 'wait';
	}

	/*
	  field   = search form field (object), optional
	  reset   = name of field that will be resetted if selected value is empty, optional
	  section = section name, optional
	*/
	this.updateForm = function(field, reset, section) {
		if(!section) section = 'searchmask';
		/* must be called BEFORE createParamList()! */
		if(field) this.saveField(field, section);

		var cache = this.cache[section];
		cache.param = this.createParamList(section);

		if(reset && this.getFieldValue(field) == '') {
			this.resetField(reset, true, section);
		}
		this.disableForm();
		this.loadSection(section);
	}

	/*
	  section = section name, optional
	*/
	this.clearForm = function(section) {
		if(!section) section = 'searchmask';
		var cache = this.cache[section];
		cache.param = '';
		this.initUserSelect();
		this.disableForm();
		this.loadSection(section);
	}

	this.submitForm = function() {
		var section = 'result';
		this.viewResultList(this.createParamList(section));
		var f = document.forms[this.frmName];

		if(f) {
			if(!this.userSelect.sort) {
				if(f.sort) this.saveField(f.sort);
			}
			if(!this.userSelect.order) {
				if(f.order) this.saveField(f.order);
			}
		}
	}

	/*
	  paramList = search arguments, e.g. 'model=1&color=5'; optional
	  section   = section name, optional
	*/
	this.loadForm = function(paramList, section) {
		if(!section) section = 'searchmask';
		var cache = this.cache[section];
		cache.param = this.getExtras();
		this.initUserSelect();

		if(paramList) {
			var f = document.forms[this.frmName];
			var a1 = paramList.split('&');
			var a2 = [];

			for(var i = 0; i < a1.length; i++) {
				a2 = a1[i].split('=');

				if(!f || typeof(this.userSelect[a2[0]]) != 'undefined') {
					this.userSelect[a2[0]] = a2[1];
				}
			}
			var p = [];

			for(var key in this.userSelect) {
				if(this.userSelect[key]) {
					p.push(key + '=' + this.userSelect[key]);
				}
			}
			if(p.length > 0) {
				cache.param += (cache.param ? '&' : '') + p.join('&');
			}
		}
		this.loadSection(section);
	}

	/*
	  paramList = search arguments, e.g. 'model=1&color=5', or form name
	  sort      = sort field, e.g. 'sort=model&order=DESC', optional
	*/
	this.searchByField = function(paramList, sort) {
		if(!sort) {
			var f = document.forms[this.frmName];

			if(f && f.sort) {
				sort = 'sort=' + this.getFieldValue(f.sort);
				if(f.order) sort += '&order=' + this.getFieldValue(f.order);
			}
		}
		if(paramList.indexOf('=') == -1) {
			if(document.forms[paramList]) {
				paramList = this.getFormData(paramList);
			}
		}
		if(paramList) {
			if(sort) paramList += '&' + sort;
		}
		else paramList = sort;
		this.loadForm(paramList);
		this.viewResultList(paramList);
	}

/* Message box functions **************************************************************/

	/*
	  msg   = message string
	  mode  = message box mode ('Info' or 'Error')
	  close = close window after .. seconds, optional
	*/
	this.viewMessageBox = function(msg, mode, close) {
		var obj = document.getElementById('messageBox');

		if(obj) {
			var link = "javascript:market.hideMessageBox()";
			obj.innerHTML = '<div style="height:15px; font-weight:bold; text-align:right;">' +
			                '<a href="' + link + '" title="' + cmMsg['closeWindow'] + '">X</a>' +
			                '</div>' +
			                '<div style="margin-bottom:15px; padding:10px; font-weight:bold;">' +
			                msg + '</div>';

			if(!mode) mode = 'Info';
			obj.className = 'messageBox' + mode;
			this.viewBox(obj, true, 0.2);

			if(close) {
				if(this.timer) clearTimeout(this.timer);
				this.timer = setTimeout(this.hideMessageBox.bind(this), close * 1000);
			}
		}
	}

	/*
	  xsl   = path to XSLT stylesheet, optional
	  xml   = path to XML document, optional
	  close = close window after .. seconds, optional
	*/
	this.viewMessageBoxXml = function(xsl, xml, close) {
		var obj = document.getElementById('messageBoxXml');

		if(obj) {
			this.xslTransform(xsl, xml, obj, null, this.messageBoxXmlCallBack.bind(this));
			if(close) setTimeout("market.hideMessageBox('messageBoxXml')", close * 1000);
		}
	}

	this.messageBoxXmlCallBack = function() {
		var obj = document.getElementById('messageBoxXml');
		var link = "javascript:market.hideMessageBox('messageBoxXml')";

		if(obj) {
			obj.innerHTML = '<div style="height:15px; font-weight:bold; text-align:right;">' +
			                '<a href="' + link + '" title="' + cmMsg['closeWindow'] + '">X</a>' +
			                '</div>' + obj.innerHTML;
			this.viewBox(obj, true, 0.2);
		}
	}

	/*
	  id = message box ID
	*/
	this.hideMessageBox = function(id) {
		if(!id) id = 'messageBox';
		var obj = document.getElementById(id);
		if(obj) this.hideBox(obj, 1.0);
	}

/* Cookie functions *********************************************************************/

	/*
	  name = cookie name
	*/
	this.getCookie = function(name) {
		if(document.cookie) {
			var idx = document.cookie.lastIndexOf(name + '=');
			if(idx == -1) return null;
			var value = document.cookie.substring(idx + name.length + 1);
			var end = value.indexOf(';');
			if(end == -1) end = value.length;
			value = value.substring(0, end);
			value = unescape(value);
			return value;
		}
	}

	/*
	  name   = cookie name
	  value  = cookie value
	  days   = number of days, optional
	  path   = cookie path, optional
	  domain = cookie domain, optional
	  secure = deliver cookie only over HTTPS session (true or false)
	*/
	this.setCookie = function(name, value, days, path, domain, secure) {
		if(this.cookieSupport()) {
			var expires = -1;

			if(typeof(days) == 'number' && days >= 0){
				var d = new Date();
				d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
				expires = d.toGMTString();
			}
			value = escape(value);
			document.cookie = name + '=' + value + ';' +
				              ((expires != -1) ? ' expires=' + expires + ';' : '') +
				              (path ? 'path=' + path : '') +
				              (domain ? '; domain=' + domain : '') +
				              (secure ? '; secure' : '');
		}
	}

	/*
	  name = cookie name
	*/
	this.deleteCookie = function(name) {
		this.setCookie(name, '-', 0, '/');
	}

	this.cookieSupport = function() {
		if(typeof(navigator.cookieEnabled) != 'boolean'){
			this.setCookie('__TestingYourBrowserForCookieSupport__', 'CookiesAllowed');
			var cookieVal = this.getCookie('__TestingYourBrowserForCookieSupport__');
			navigator.cookieEnabled = (cookieVal == 'CookiesAllowed');

			if(navigator.cookieEnabled){
				this.deleteCookie('__TestingYourBrowserForCookieSupport__');
			}
		}
		return navigator.cookieEnabled;
	}

/* AJAX functions ***********************************************************************/

	/*
	  url     = target URL
	  frmName = form name, optional
	*/
	this.httpReq = function(url, frmName) {
		if(this.mdxAjax) {
		    if(frmName) {
		    	url += ((url.indexOf('?') != -1) ? '&' : '?') + 'mkey=' + escape(marketKey);
		    }
		    this.mdxAjax.makeRequest(url, this.requestHandler.bind(this, frmName), frmName);
		}
	}

	this.requestHandler = function(frmName) {
	    if(this.mdxAjax && this.mdxAjax.request) {
        	if(this.mdxAjax.request.responseText == '1') {
				var msg = cmMsg['requestSent'];
				this.viewMessageBox(msg, 'Info', 2);

				if(frmName) {
					var frm = document.forms[frmName];

					if(frm && frm.trackVal) {
						this.pageTracker(frm.trackVal.value, 'Fahrzeuganfrage');
					}
				}
        	}
			else if (this.mdxAjax.request.responseText == 'error_captcha') {
				var msg = cmMsg['errorCaptcha'];
				this.viewMessageBox(msg, 'Error', 2);
			} else {
				var msg = cmMsg['emailOrPhoneRequired'];
				this.viewMessageBox(msg, 'Error', 2);
			}
        }
        else {
        	var msg = cmMsg['requestNotSent'];
			this.viewMessageBox(msg, 'Error');
	    }
		
		if(frmName) {
			var frm = document.forms[frmName];

			if(frm && frm.captchaPic) {
				var src = frm.captchaPic.src;
				var pos = src.indexOf('?');
				if (pos >= 0) {
					src = src.substr(0, pos);
				}
				var date = new Date();
				frm.captchaPic.src = src + '?u=' + date.getTime();
			}
		}
	}

/* Cache functions **********************************************************************/

	/*
	  section   = section name, e.g. 'result', 'searchmask'
	  container = container name, e.g. 'header', 'body'; optional
	  variable  = variable name, e.g. 'xmlPath', 'param'; optional
	*/
	this.clearCache = function(section, container, variable) {
		if(this.cache[section]) {
			if(container) {
				if(this.cache[section][container]) {
					if(variable) {
						this.cache[section][container][variable] = null;
					}
					else this.cache[section][container] = {};
				}
				else this.cache[section][container] = {};
			}
			else this.cache[section] = {};
		}
		else this.cache[section] = {};
	}

	this.initCache = function() {
		var section, container;

		for(section in contents) {
			this.cache[section] = {};
			this.cache[section].param = '';

			for(container in contents[section]) {
				this.cache[section][container] = {};
			}
		}
	}

/* Base64 encoding / decoding functions *************************************************/

	this.b64encode = function(input) {
		var output = '';

		if(input) {
			var k = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
			var chr1, chr2, chr3;
			var enc1, enc2, enc3, enc4;
			var i = 0;

			do {
				chr1 = input.charCodeAt(i++);
				chr2 = input.charCodeAt(i++);
				chr3 = input.charCodeAt(i++);

				enc1 = chr1 >> 2;
				enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
				enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
				enc4 = chr3 & 63;

				if(isNaN(chr2)) enc3 = enc4 = 64;
				else if(isNaN(chr3)) enc4 = 64;

				output = output + k.charAt(enc1) + k.charAt(enc2) + k.charAt(enc3) + k.charAt(enc4);
			}
			while(i < input.length);
		}
		return output;
	}

	this.b64decode = function(input) {
		var output = '';

		/* sanity check */
		if(input.indexOf('&') != -1) {
			input = input.substr(0, input.indexOf('&'));
		}

		if(input) {
			var k = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
			var chr1, chr2, chr3;
			var enc1, enc2, enc3, enc4;
			var i = 0;
			input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');

			do {
				enc1 = k.indexOf(input.charAt(i++));
				enc2 = k.indexOf(input.charAt(i++));
				enc3 = k.indexOf(input.charAt(i++));
				enc4 = k.indexOf(input.charAt(i++));

				chr1 = (enc1 << 2) | (enc2 >> 4);
				chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
				chr3 = ((enc3 & 3) << 6) | enc4;

				output = output + String.fromCharCode(chr1);

				if(enc3 != 64) {
					output = output + String.fromCharCode(chr2);
				}
				if(enc4 != 64) {
					output = output + String.fromCharCode(chr3);
				}
			}
			while(i < input.length);
		}
		return output;
	}

/* General functions ********************************************************************/

	/*
	  imgName = name of image to replace
	  newImg  = new image
	  isPath  = new image includes webpath (true or false)
	*/
	this.replaceImage = function(imgName, newImg, isPath) {
		if(document.images && document.images[imgName]) {
			var img = document.images[imgName];

			if(newImg) {
				if(isPath) img.src = newImg;
				else {
					var s = img.src;
					img.src = s.replace(/\/[^\/]+$/, '/' + newImg);
				}
			}
		}
	}

	this.sendRecMail = function(vkey) {
		var subject = escape(cmMsg['recMailSubject']);
		var param = url = '';

		if(location.hash) {
			param = '#' + this.b64encode('viewDetails::' + vkey + ';;0;;details_result;;sort=price');
			url = location.href.replace(location.hash, param);
		}
		else {
			param = '?chiffre=' + vkey;
			url = location.href.replace(location.search, param);
		}
		var body = escape(cmMsg['recMailBody'] + url);
		location.href = 'mailto:?subject=' + subject + '&body=' + body;
	}

	/*
	  width  = popup width, optional
	  height = popup height, optional
	  xsl    = path to XSLT stylesheet
	  xml    = path to XML document
	*/
	this.openPopup = function(width, height, xsl, xml) {
		if(!width) width = 500;
		if(!height) height = 400;
		var x = Math.round((screen.width - width) / 2);
		var y = Math.round((screen.height - height) / 2);
		this.newWindow('', width, height, x, y, true, true);
		if(this.popup) this.xslTransform(xsl, xml, this.popup);
	}

	/*
	  url    = window URL
	  width  = window width, optional
	  height = window height, optional
	  x      = window x position, optional
	  y 	 = window y position, optional
	  scroll = view scrollbars (true or false)
	  menu   = view menubar (true or false)
	  tool   = view toolbar (true or false)
	  resize = resizable window (true or false)
	*/
	this.newWindow = function(url, width, height, x, y, scroll, menu, tool, resize) {
		if(this.popup && !this.popup.closed) this.popup.close();
		if(!width) width = 500;
		if(!height) height = 400;
		if(!x) x = Math.round((screen.width - width) / 2);
		if(!y) y = Math.round((screen.height - height) / 2);
		scroll = scroll ? 1 : 0;
		menu = menu ? 1 : 0;
		tool = tool ? 1 : 0;
		resize = resize ? 1 : 0;
		this.popup = window.open(url, 'popup', 'width=' + width + ',height=' + height +
					 			',left=' + x + ',top=' + y + ',scrollbars=' + scroll +
					 			',menubar=' + menu + ',toolbar=' + tool + ',resizable=' + resize);
		this.popup.focus();
	}

	/*
	  op = opacity (0.0 - 1.0)
	*/
	this.setBGOpacity = function(op) {
		var obj = document.getElementById('darkScreen');

		if(obj) {
			var ds = this.getDocumentSize();
			if(op >= 0.0) this.setOpacity(obj, op);
			obj.style.width = ds.width + 'px';
			obj.style.height = ds.height + 'px';
			obj.style.display = (op < 1.0) ? 'block' : 'none';
		}
	}

	/*
	  obj    = HTML element (object)
	  center = place object in window center (true or false)
	  op     = background opacity (0.0 - 1.0); optional
	*/
	this.viewBox = function(obj, center, op) {
		if(obj) {
			if(center) this.placeOnScreen(obj);
			if(op) this.setBGOpacity(op);
			obj.style.visibility = 'visible';
			if(obj.style.display == 'none') obj.style.display = 'block';
		}
	}

	/*
	  obj = HTML element (object)
	  op  = background opacity (0.0 - 1.0); optional
	*/
	this.hideBox = function(obj, op) {
		if(obj) {
			obj.style.visibility = 'hidden';
			if(obj.style.display == 'block') obj.style.display = 'none';
		}
		if(op) this.setBGOpacity(op);
	}

	/*
	  btnID   = button ID
	  status  = disabled status (true or false)
	  seconds = number of seconds to toggle back; 0 = don't toggle back
	*/
	this.setButtonStatus = function(btnID, status, seconds) {
		var obj = document.getElementById(btnID);

		if(obj) {
			obj.disabled = status;

			if(seconds) {
				status = !status;
				setTimeout("market.setButtonStatus('" + btnID + "', " + status + ", 0)", seconds * 1000);
			}
		}
	}

	this.getQuery = function() {
		var query = {};
		var qs = unescape(location.search.substr(1));

		if(qs) {
			var a1 = qs.split('&');
			var a2 = [];

			for(var i = 0; i < a1.length; i++) {
				a2 = a1[i].split('=');
				query[a2[0]] = a2[1];
			}
		}
		return query;
	}

	/*
	  obj = HTML element (object)
	  op  = object opacity (0.0 - 1.0)
	*/
	this.setOpacity = function(obj, op){
		if(obj) {
			if(op > 1.0) op = 1.0;
			else if(op < 0.0) op = 0.0;
		    obj.style.opacity = op;
		    obj.style.MozOpacity = op;
		    obj.style.KhtmlOpacity = op;
		    obj.style.filter = 'alpha(opacity=' + parseInt(op * 100) + ')';
		}
	}

	this.getViewport = function() {
		var x = y = 0;
		var body = document.documentElement ? document.documentElement : document.body;

		if(window.innerWidth) {
			x = window.innerWidth;
			y = window.innerHeight;
		}
		else if(body.clientWidth) {
			x = body.clientWidth;
			y = body.clientHeight;
		}
		else if(body.offsetWidth) {
			x = body.offsetWidth;
			y = body.offsetHeight;
		}
		else {
			x = screen.width;
			y = screen.height;
		}
		return { width: x, height: y };
	}

	this.getScroll = function() {
		var x = y = 0;

		if(window.pageXOffset || window.pageYOffset) {
			x = window.pageXOffset;
			y = window.pageYOffset;
		}
		else if(document.documentElement &&
		       (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
			x = document.documentElement.scrollLeft;
			y = document.documentElement.scrollTop;
		}
		else if(document.body &&
		       (document.body.scrollLeft || document.body.scrollTop)) {
			x = document.body.scrollLeft;
			y = document.body.scrollTop;
		}
		return { left: x, top: y };
	}

	this.getDocumentSize = function() {
		var body = document.documentElement ? document.documentElement : document.body;
		var innerWidth = (self.innerWidth && !isNaN(self.innerWidth)) ? self.innerWidth : 0;
		var innerHeight = (self.innerHeight && !isNaN(self.innerHeight)) ? self.innerHeight : 0;
		return {
			width: Math.max(body.scrollWidth, body.clientWidth, innerWidth),
			height: Math.max(body.scrollHeight, body.clientHeight, innerHeight)
		};
	}

	/*
	  obj = HTML element (object)
	  x   = horizontal position (pixels), optional
	  y   = vertical position (pixels), optional
	*/
	this.placeOnScreen = function(obj, x, y) {
		if(obj) {
			if(!x || !y) {
				var vp = this.getViewport();
				if(!x) x = Math.round((vp.width - obj.offsetWidth) / 2);
				if(!y) y = Math.round((vp.height - obj.offsetHeight) / 2);
			}
			var scr = this.getScroll();
			obj.style.left = (x + scr.left) + 'px';
			obj.style.top = (y + scr.top) + 'px';
		}
	}

	/*
	  msg    = message text
	  action = URL or function object
	*/
	this.confirmAction = function(msg, action) {
		var ok = confirm(msg);

		if(ok) {
			if(typeof(action) == 'function') action();
			else location.href = action;
		}
	}

	/*
	  val  = string to be tracked
	  page = page title, optional
	*/
	this.pageTracker = function(val, page) {
		if(val && typeof(pageTracker) != 'undefined') {
			var d = new Date();
			val += ' / Timestamp: ' + d.toGMTString();
			if(page) val = page + ': ' + val;
			pageTracker._setVar(val);
		}
	}
}