var _enableDebug = false;

var _hostname = "app.kigo.net";


/* vkDom.js */

function	vkDomClass()
{
	this.loadCallbacks = new Array();
	this.unloadCallbacks = new Array();
	this.loadFirst = true;
	this.unloadFirst = true;
	this.loaded = false;
	this.loadIE = null;
}



/**************************************************************/
/* vkDom.onLoad() handling */

vkDomClass.prototype.onLoad = function(callback)
{
	// Simply queues the callback	
	this.loadCallbacks[this.loadCallbacks.length] = callback;

	// If this is the very first time we're called, fire up the events, according to the browser
	if(this.loadFirst)
	{
		// Intercept and remember IE which requires a very special handler
		var	ua = navigator.userAgent.toLowerCase();

		this.loadIE = /msie/.test(ua) && !/opera/.test(ua);

		// Deal with mozilla & friend.
		// But avoid browsers implementing document.readyState
		if(!document['readyState'] && document.addEventListener)
			document.addEventListener('DOMContentLoaded', function() { vkDom._setLoaded() }, false);
		else if(this.loadIE)	
		{
			// Thanks to:
			// http://blog.outofhanwell.com/2006/06/08/the-windowonload-problem-revisited/

			// document.write('<scr'+'ipt id="__vkDom_onLoad" defer="true" src="//[]"></scr'+'ipt>');
			// 14/05/2008
			// "[]" caused the browser to do a DNS lookup, try a fake IP
			//document.write('<scr'+'ipt id="__vkDom_onLoad" defer="true" src="//0.0.0.0"></scr'+'ipt>');
			// 20/02/2009 -> use src=/: as in swfobject
			// 20/02/2009 - Also, embed this in try/catch block
			try
			{
				document.write('<scr'+'ipt id="__vkDom_onLoad" defer="true" src="//:"></scr'+'ipt>');

				var	trickScript = document.getElementById('__vkDom_onLoad');

				if(trickScript)
				{
					trickScript.onreadystatechange = function()
					{
						if(this.readyState == 'complete')
							vkDom._setLoaded();
					}

					// Test it right now
					trickScript.onreadystatechange();

					trickScript = null;	// ie leaks?
				}
			}
			catch(e)
			{
			}
		}
		// Other browser will use readyState or getElementsByTagName('body') which worked fine till now...

		this.loadFirst = false;
	}

	if(this.loadCallbacks.length == 1)
		setTimeout('vkDom._wait4dom()', 75);	
}


// FRIEND
vkDomClass.prototype._setLoaded = function()
{
	this.loaded = true;
}

// PRIVATE
vkDomClass.prototype._wait4dom = function()
{
	if(!this.loaded)
	{
		if(document['readyState'])
		{
			if(	document.readyState == 'loaded' ||
				document.readyState == 'complete' )
				this.loaded = true;
		}
		else if(!document.addEventListener && !this.loadIE)	// Use old technique for non-mozilla & non-ie browsers
		{
			if(	
				//document.body != null ||
				(document.getElementsByTagName('body') != null &&
				document.getElementsByTagName('body')[0] != null) 
			)
				this.loaded = true;
		}
	}

	if(this.loaded)
	{
		var	i;

		for(i = 0; i < this.loadCallbacks.length; i++)
		{
			// 15/03/2010 - This is a kind of race condition, occurs when the document is unloaded (ie location changes) so fast that 
			// not all the onLoad() processing could achieve...
			if(!document.body)
				return;
			this.loadCallbacks[i]();
		}

		// Resets the list
		this.loadCallbacks = new Array();
	}
	else
		setTimeout('vkDom._wait4dom()', 75);
}


/*---------------------*/

/* This previous version failed miserably with IE

vkDomClass.prototype.loaded = function()
{
	if(	
		document.body != null ||
		(document.getElementsByTagName('body') != null &&
		document.getElementsByTagName('body')[0] != null) 
	)
		return true;

	return false;
}


vkDomClass.prototype.onLoad = function(callback)
{
	this.loadCallbacks[this.loadCallbacks.length] = callback;
	if(this.loadCallbacks.length == 1)
		setTimeout('vkDom.wait4dom()', 75);	
}

vkDomClass.prototype.wait4dom = function()
{
	if(this.loaded())
	{
		var	i;

		for(i = 0; i < this.loadCallbacks.length; i++)
			this.loadCallbacks[i]();

		// Resets the list
		this.loadCallbacks = new Array();
	}
	else
		setTimeout('vkDom.wait4dom()', 75);	
}


*/


/**************************************************************/
/* vkDom.onUnload() handling */

vkDomClass.prototype.onUnload = function(callback)
{
	this.unloadCallbacks[this.unloadCallbacks.length] = callback;

	if(this.unloadFirst)
	{
		/*
		if(document.addEventListener)
			document.addEventListener('unload', function() { vkDom._onUnload(); }, false);
		}
		else */
		
		if(window.attachEvent)
			window.attachEvent('onunload', function() { vkDom._onUnload(); });
		else
			window.onunload = function() { vkDom._onUnload(); };

		this.unloadFirst = false;
	}
}

// FRIEND
vkDomClass.prototype._onUnload = function()
{
	var	i;

	for(i = 0; i < this.unloadCallbacks.length; i++)
		this.unloadCallbacks[i]();

	// Resets the list
	this.unloadCallbacks = new Array();
}








// Usefull tool

vkDomClass.prototype.html = function(str)
{
	str = ''+str;
	str = str.replace(/&/g, '&amp;');
	str = str.replace(/\"/g, '&quot;');
	str = str.replace(/</g, '&lt;');
	return str.replace(/>/g, '&gt;');
}

vkDomClass.prototype.nl2br = function(str)
{
	str = ''+str;
	return str.replace(/\n/g, '<br />');
}

vkDomClass.prototype.htmlbr = function(str)
{
	return this.nl2br(this.html(str));
}


vkDomClass.prototype.js = function(str)
{
	str = ''+str;
	str = str.replace(/\'/g, '\\\'');
	// TODO: linebreaks and so on...
	return str;
}

vkDomClass.prototype.uri = function(str)
{
	return encodeURIComponent(str);
}


vkDomClass.prototype.el = function(id)
{
	// 15/10/2008 - takes either a string or the object itself, allowing other methods to transparently allow
	// both as primary params...

	if(typeof(id) == 'string')
		return document.getElementById(id);
	else if(typeof(id) == 'object')
		return id;
	else
		return null;
}	

vkDomClass.prototype.visibility = function(id)
{
	var	el = this.el(id);

	if(!el)
		return;

	if(arguments.length == 2 && !arguments[1])
		el.style.visibility = 'hidden';
	else
		el.style.visibility = 'visible';
}

vkDomClass.prototype.display = function(id)
{
	var	el = this.el(id);

	if(!el)
		return;

	if(arguments.length > 1)
	{
		if(typeof(arguments[1]) == 'string')
			el.style.display = arguments[1];
		else if(arguments[1])
			el.style.display = '';		// true
		else
			el.style.display = 'none';	// false
	}
	else
		el.style.display = '';		// true
}

vkDomClass.prototype.value = function(id)
{
	var	el = this.el(id);
	if(el)
		return el.value;
	return null;
}

vkDomClass.prototype.enable = function(id)
{
	var	el = this.el(id);
	if(el)
		el.disabled = !(arguments.length < 2 || arguments[1]);
}

vkDomClass.prototype.disable = function(id)
{
	var	el = this.el(id);
	if(el)
		el.disabled = true;
}

vkDomClass.prototype.focus = function(id)
{
	var	el = this.el(id);
	if(el && !el.disabled)
		el.focus();
}

vkDomClass.prototype.select = function(id)
{
	var	el = this.el(id);
	if(el && !el.disabled)
	{
		el.focus();
		el.select();
	}
}

vkDomClass.prototype.clean = function(id)
{
	var	el = this.el(id);
	if(el)
	{
		while(el.firstChild)
			el.removeChild(el.firstChild);
	}
}

vkDomClass.prototype.setHtml = function(id, html)
{
	var	el = this.el(id);
	if(el)
		el.innerHTML = html;
}

vkDomClass.prototype.setText = function(id, text)
{
	var	el = this.el(id);
	if(el)
		el.innerHTML = this.html(text);
}

vkDomClass.prototype.setTextBr = function(id, text)
{
	var	el = this.el(id);
	if(el)
		el.innerHTML = this.htmlbr(text);
}


vkDomClass.prototype.setFormValue = function(id, value)
{
	var	i, el = this.el(id);

	if(!el)
		return;

	switch(el.tagName)
	{
		case 'INPUT':
			switch(el.getAttribute('type').toUpperCase())
			{
				case 'TEXT':
				case 'PASSWORD':
				case 'HIDDEN':
					
					if(
						typeof(value) != 'string' &&
						typeof(value) != 'number'
					)
						value = '';

					el.value = value;
					break;

				case 'CHECKBOX':
				case 'RADIO':
					el.checked = value ? true : false;
					break;
			}
			break;

		case 'TEXTAREA':

			if(
				typeof(value) != 'string' &&
				typeof(value) != 'number'
			)
				value = '';
		
			el.value = value;
	
			break;

		case 'SELECT':

			if(el.selectedIndex >= 0)
			{
				el.selectedIndex = 0;

				if(
					typeof(value) == 'string' ||
					typeof(value) == 'number'
				)
				{
					for(i = 0; i < el.options.length; i++)
					{
						if(el.options[i].value == value)
						{
							el.selectedIndex = i;
							return;
						}
					}
				}
			}
	}
}


vkDomClass.prototype.getFormValue = function(id)
{
	var	i, el = this.el(id);

	if(!el)
		return null;

	switch(el.tagName)
	{
		case 'INPUT':
			switch(el.getAttribute('type').toUpperCase())
			{
				case 'TEXT':
				case 'PASSWORD':
				case 'HIDDEN':
					return el.value;

				case 'CHECKBOX':
				case 'RADIO':
					return el.checked;

				default:
					return null;
			}
			

		case 'TEXTAREA':
			return el.value;

		case 'SELECT':
			if(el.selectedIndex < 0)
				return null;
			return el.options[el.selectedIndex].value;


		default:
			return null;
	}
}





// Class-handling related

vkDomClass.prototype.listClass = function(id)
{
	var	el = this.el(id);

	if(!el)
		return null;

	return el.className.split(/\s+/);
}

vkDomClass.prototype.hasClass = function(id, name)
{
	var	el = this.el(id);
	var	cls;

	if(!el)
		return false;

	cls = this.listClass(id);

	for(var i = 0; i < cls.length; i++)
	{
		if(cls[i] == name)
			return true;
	}

	return false;
}


vkDomClass.prototype.addClass = function(id, name)
{
	var	el = this.el(id);
	var	cls;

	if(!el || this.hasClass(id, name))
		return;

	el.className = el.className.length ? ( el.className + ' ' + name ) : name;
}

vkDomClass.prototype.removeClass = function(id, name)
{
	var	el = this.el(id);
	var	oldCls, newCls;

	if(!el)
		return;
	
	oldCls = this.listClass(id);
	newCls = [];

	for(var i = 0; i < oldCls.length; i++)
	{
		if(oldCls[i] != name)
			newCls[newCls.length] = oldCls[i];
	}

	if(newCls.length)
		el.className = newCls.join(' ');
	else
		el.className = '';
}




vkDomClass.prototype.getAbsolutePosition = function(el)
{
/*
	if(	typeof(element) == 'string' && 
		!(element = vkDom.el(element))
	)
		return { x: 0, y : 0 };
*/
	
	el = this.el(el);

	if(!el)
		return { x: 0, y : 0 };

	var r = { x: el.offsetLeft - el.scrollLeft, y: el.offsetTop - el.scrollTop };

	if(el.offsetParent) 
	{
		var tmp = this.getAbsolutePosition(el.offsetParent);
		r.x += tmp.x;
		r.y += tmp.y;
	}

	return r;
}


// Cookies related

vkDomClass.prototype.setCookie = function(name, value, seconds, path, domain)
{
	var	str;

	if(seconds)
	{
		var	expiryDate = new Date();

		expiryDate.setTime(
			expiryDate.getTime() + seconds * 1000
		);

		//str = name + '=' + escape(value) + '; expires='+expiryDate.toGMTString();
		str = name + '=' + encodeURIComponent(value) + '; expires='+expiryDate.toGMTString();	// 04/08/2010 - escape() was not escaping the '+' character
	}
	else
		//str = name + '=' + escape(value);
		str = name + '=' + encodeURIComponent(value);	// 04/08/2010 - escape() was not escaping the '+' character
	
	if(path)
		str += '; path='+path;

	if(domain)
		str += '; domain='+domain;

	document.cookie = str;
}


vkDomClass.prototype.setSessionCookie = function(name, value, path, domain)
{
	this.setCookie(name, value, 0, path, domain);
}

vkDomClass.prototype.getCookie = function(name)	// [, default=null]
{
	var	i, search, parts;
	
	search = name+'=';
	parts = document.cookie.split('; ');

	for(i = 0; i < parts.length; i++)
	{
		if(parts[i].indexOf(search) == 0)
			return unescape(parts[i].substring(search.length));
	}

	if(arguments.length > 1)
		return arguments[1];

	return null;
}


vkDomClass.prototype.removeCookie = function(name, path, domain)
{
	this.setCookie(name, '', -1, path, domain);
}




/*
vkDomClass.prototype.getAbsoluteScrollPosition = function(element)
{
	if(	typeof(element) == 'string' && 
		!(element = vkDom.el(element))
	)
		return { x: 0, y : 0 };

	var r = { x: element.offsetLeft, y: element.offsetTop };

	if(element.offsetParent) 
	{
		var tmp = this.getAbsoluteScrollPosition(element.offsetParent);
		r.x += tmp.x;
		r.y += tmp.y;
	}

	return r;
}
*/


var	vkDom = new vkDomClass();

// vkDebug.js


function	vkDebug()
{
	this.container = null;
	this.log = null;
	this.buttons = null;
	this.enabled = false;
}

vkDebug.prototype.enable = function()	// (enable, jserrors)
{
	if(	arguments.length > 0 &&
		!arguments[0])
		this.enabled = false;
	else
		this.enabled = true;
}

vkDebug.prototype.text = function(string)
{
	if(this.enabled)
	{
		this._init();

		if(typeof(string) != 'string')
		{
			if(typeof(string) == 'number')
				string = ''+string;
			else if(string == null)
				string = 'null';
			else if(typeof(string) == 'object')
				string = string.toString();
			else
				return;
		}

		string = string.split('\n');

		for(var i = 0; i < string.length; i++)
		{
			this.log.appendChild(document.createTextNode(string[i].replace(new RegExp('^\t', 'g'), '    ').replace(new RegExp('^[\\s]', 'g'), '\u00a0')));
			this.log.appendChild(document.createElement('br'));
		}

		this.log.scrollTop = this.log.scrollHeight;
		//this._write(vkDom.nl2br(vkDom.html(string))+'<br />');
	}
}


vkDebug.prototype.html = function(string)
{
	if(this.enabled)
		this._write(string+'<br />');
}


vkDebug.prototype.domtree = function(element, skiptext)
{
	if(this.enabled)
	{
		this._init();
		this._domtree(element, skiptext, 0);
		this.log.scrollTop = this.log.scrollHeight;
	}
}

// PRIVATE



vkDebug.prototype._domtree = function(node, skiptext, depth)
{
	var	i, str;

	str = '+ ';

	switch(node.nodeType)
	{
		case 1:	// ELEMENT_NODE
			str += 'ELEMENT_NODE [1] ['+node.tagName+']';
			if(node.id)
				str += ' #'+node.id;
			break;

		case 2:	// ATTRIBUTE_NODE
			str = null;
			break;

		case 3:	// TEXT_NODE
			if(!skiptext)
				str += 'TEXT_NODE [3]';
			else
				str = null;
			break;

		case 4:	// CDATA_SECTION_NODE
			if(!skiptext)
				str += 'CDATA_SECTION_NODE [4]';
			else
				str = null;
			break;

		case 5:	// ENTITY_REFERENCE_NODE
			str += 'ENTITY_REFERENCE_NODE [5]';
			break;

		case 6:	// ENTITY_NODE
			str += 'ENTITY_NODE [6]';
			break;

		case 7:	// PROCESSING_INSTRUCTION_NODE
			str += 'PROCESSING_INSTRUCTION_NODE [7]';
			break;

		case 8:	// COMMENT_NODE
			if(!skiptext)
				str += 'COMMENT_NODE [8]';
			else
				str = null;
			break;

		case 9:	// DOCUMENT_NODE
			str += 'DOCUMENT_NODE [9]';
			break;

		case 10:// DOCUMENT_TYPE_NODE
			str += 'DOCUMENT_TYPE_NODE [10]';
			break;

		case 11:// DOCUMENT_FRAGMENT_NODE
			str += 'DOCUMENT_FRAGMENT_NODE [11]';
			break;

		case 12:// NOTATION_NODE
			str += 'NOTATION_NODE [12]';
			break;

		default:
			str += 'UNKNOWN_TYPE ['+node.nodeType+']';
	}

/*
	NodeType 	Named Constant
	1			ELEMENT_NODE
	2 			ATTRIBUTE_NODE
	3 			TEXT_NODE
	4 			CDATA_SECTION_NODE
	5 			ENTITY_REFERENCE_NODE
	6 			ENTITY_NODE
	7 			PROCESSING_INSTRUCTION_NODE
	8 			COMMENT_NODE
	9 			DOCUMENT_NODE
	10 			DOCUMENT_TYPE_NODE
	11 			DOCUMENT_FRAGMENT_NODE
	12 			NOTATION_NODE
*/


	if(str != null)
	{
		var	spacer = '';

		for(i = 0; i < depth*2; i++)
			spacer += '\u00a0';		// unfortunately, IE ignores "white-space: pre"

		//this._write(spacer+vkDom.html(str)+'<br />');
		this.log.appendChild(document.createTextNode(spacer+str));
		this.log.appendChild(document.createElement('br'));
	}

	// Try detecting any instance of vkDebug
	if(node.nodeType == 1 && node.tagName == 'DIV' && node.className == 'vkDebug')
		return;

	if(node.hasChildNodes())
	{
		depth++;
		for(i = 0; i < node.childNodes.length; i++)
			this._domtree(node.childNodes[i], skiptext, depth)
	}
}



vkDebug.prototype._write = function(string)
{
	this._init();
	this.log.innerHTML += string;
	this.log.scrollTop = this.log.scrollHeight;
}

/*
vkDebug.prototype._append = function(o)
{
	this._init();
	this.log.appendChild(o);
	this.log.scrollTop = this.log.scrollHeight;
}
*/

vkDebug.prototype._init = function()
{
	if(!this.log)
	{
		// Get last position and dimensions from cookie
		this._getParams();

		this.container = document.createElement('div');
		this.container.className = 'vkDebug';

		this.log = document.createElement('div');
		this.log.className = 'log';
		this.log.style.width = this.width+'px';
		this.log.style.height = this.height+'px';

		this.container.appendChild(this.log);

		document.body.appendChild(this.container);

		this._updatePosition();

		var	self = this;

		if(window.addEventListener)
		{
			window.addEventListener('resize', function() { self._updatePosition() }, false);
			window.addEventListener('scroll', function() { self._updatePosition() }, false);
		}
		else if(window.attachEvent)
		{
			window.attachEvent('onresize', function() { self._updatePosition() });
			window.attachEvent('onscroll', function() { self._updatePosition() });
		}
	}
	this.container.style.display = 'block';
}



vkDebug.prototype._getParams = function()
{
	var	cookie, parts;

	this.width = null;
	this.height = null;
	this.position = null;

	cookie = vkDom.getCookie('vkDebug');

	if(cookie)
	{
		parts = cookie.split('|');
		if(parts.length == 3)
		{
			this.width = parseInt(parts[0]);
			this.height = parseInt(parts[1]);
			this.position = parseInt(parts[2]);
		}
	}

	if(this.width == null)
	{
		this.width = 400;
		this.height = 50;
		this.position = 1;
	}

	this._normalizeParams();
}

vkDebug.prototype._updateParams = function()
{
	vkDom.setCookie('vkDebug', this.width+'|'+this.height+'|'+this.position, 7*24*60*60);
}

vkDebug.prototype._normalizeParams = function()
{
	if(this.width < 300)
		this.width = 300;
	else if(this.width > 2000)
		this.width = 2000;

	if(this.height < 100)
		this.height = 100;
	else if(this.height > 2000)
		this.height = 2000;

	if(this.position < 1 || this.position > 4)
		this.position = 1;
}

vkDebug.prototype._updatePosition = function()
{
	var	x, y;


	function createButton(text, action)
	{
		var	button;

		button = document.createElement('button');
		button.innerHTML = vkDom.html(text);
		button.onclick = function() { return action(); };

		return button;
	}


	if(this.buttons)
		this.container.removeChild(this.buttons);
	else
	{
		var	self = this;
		this.buttons = document.createElement('div');
		this.buttons.className = 'buttons';
		this.buttons.style.width = this.width+'px';


		function setpos()
		{
			self.position++;
			self._normalizeParams();
			self._updateParams();
			self._updatePosition();
			return false;
		}

		function	resize(width, more)
		{
			if(width)
			{
				if(more)
					self.width += 50;
				else
					self.width -= 50;
			}
			else
			{
				if(more)
					self.height += 50;
				else
					self.height -= 50;
			}

			self._normalizeParams();
			self._updateParams();

			self.log.style.width = self.width+'px';
			self.log.style.height = self.height+'px';

			self.buttons.style.width = self.width+'px';

			self._updatePosition();
			return false;
		}


		// Create buttons

		this.buttons.appendChild(createButton('Hide', function() { self.container.style.display = 'none'; }));
		this.buttons.appendChild(createButton('Pos', function() { setpos(); }));
		this.buttons.appendChild(createButton('W -', function() { resize(true, false); }));
		this.buttons.appendChild(createButton('W +', function() { resize(true, true); }));
		this.buttons.appendChild(createButton('H -', function() { resize(false, false); }));
		this.buttons.appendChild(createButton('H +', function() { resize(false, true); }));
		this.buttons.appendChild(createButton('Clear', function() { vkDom.clean(self.log); }));
	}

	switch(this.position)
	{
		case 1:	// LEFT/TOP
			this.buttons.style.textAlign = 'left';
			this.container.insertBefore(this.buttons, this.container.firstChild);
			x = 0;
			y = 0;
			break;

		case 2:	// RIGHT/TOP
			this.buttons.style.textAlign = 'right';
			this.container.insertBefore(this.buttons, this.container.firstChild);
			x = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft) + document.documentElement.clientWidth-this.container.clientWidth;
			y = 0;
			break;

		case 3:	// RIGHT/BOTTOM
			this.buttons.style.textAlign = 'right';
			this.container.appendChild(this.buttons);
			x = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft) + document.documentElement.clientWidth-this.container.clientWidth;
			y = Math.max(document.documentElement.scrollTop, document.body.scrollTop) + document.documentElement.clientHeight-this.container.clientHeight;
			break;

		case 4:	// LEFT/BOTTOM
			this.buttons.style.textAlign = 'left';
			this.container.appendChild(this.buttons);
			x = 0;
			y = Math.max(document.documentElement.scrollTop, document.body.scrollTop) + document.documentElement.clientHeight-this.container.clientHeight;
			break;

	}

	this.container.style.left = x+'px';
	this.container.style.top = y+'px';
}


/* vkFormMonitor.js */

/********************************/
/* PUBLIC METHODS */

function	vkFormMonitor()
{
	this.els = new Array();
	this.values = new Array();
	this.callback = null;
	this.period = 0;

	this.debug = null;
}

// bool
vkFormMonitor.prototype.enableDebug = function(debug)
{
	if(
		debug == null ||
		(
			typeof(debug) == 'object' &&
			debug.constructor == vkDebug
		)
	)
	{
		this.debug = debug;
		return true;
	}
			
	this.debug = null;
	return false;
}

// bool
vkFormMonitor.prototype.disableDebug = function()
{
	return this.enableDebug(null);
}

vkFormMonitor.prototype.addElement = function(el)
{
	this.els[this.els.length] = el;
	this.values[this.values.length] = null;
}

// 10/11/2008
vkFormMonitor.prototype.add = function()
{
	for(var i = 0; i < arguments.length; i++)
		this.addElement(arguments[i]);
}



vkFormMonitor.prototype.reset = function()
{
	this.callback = null;
	this.els.length = 0;
	this.values.length = 0;
}

vkFormMonitor.prototype.sync = function()
{
	this.checkAndSync();
}

vkFormMonitor.prototype.monitor = function(callback, period)
{
	// re-sync
	this.checkAndSync();

	if(this.callback == null)
	{
		var	self = this;
		setTimeout(
			function()
			{
				self.checkForChanges()
			},
			period
		);
	}

	this.callback = callback;
	this.period = period;
}

vkFormMonitor.prototype.stop = function()
{
	this.callback = null;
}


/********************************/
/* PRIVATE METHODS */

// void
vkFormMonitor.prototype.writeDebug = function(text)
{
	if(this.debug)
		this.debug.text('vkFormMonitor: '+text);
}


vkFormMonitor.prototype.getValueByIndex = function(i)
{
	// 10/11/2008

	return vkDom.getFormValue(this.els[i]);

	/*
	var	type, el = this.els[i];

	switch(type = vkDom.el(el).tagName.toUpperCase())
	{
		case 'SELECT':
			if(vkDom.el(el).selectedIndex < 0)
				return null;
			return vkDom.el(el).options[vkDom.el(el).selectedIndex].value;

		case 'INPUT':
			
			switch(type = vkDom.el(el).type.toUpperCase())
			{
				case 'TEXT':
				case 'PASSWORD':
				case 'HIDDEN':
					return vkDom.el(el).value;

				case 'CHECKBOX':
					return vkDom.el(el).checked ? '1' : '0';

				default:
					this.writeDebug('unhandled input form field type ['+type+']');
					return null;
			}
			break;

		case 'TEXTAREA':
			return vkDom.el(el).value;

		default:
			this.writeDebug('unhandled form field type ['+type+']');
			return null;
	}

	// Unreachable
	return null;
	*/
}


vkFormMonitor.prototype.findElement = function(el)
{
	var	i;

	for(i = 0; i < this.els.length; i++)
	{
		if(this.els[i] == el)
			return i;
	}
	
	return -1;
}

// Checks for changes while also resync'ing with current form values
vkFormMonitor.prototype.checkAndSync = function()
{
	var	i, val, changed = false;

	for(i = 0; i < this.els.length; i++)
	{
//		if(!this.els[i].disabled)
//		{
			val = this.getValueByIndex(i);

			if(this.values[i] != val)
			{
				changed = true;
				this.values[i] = val;
			}
//		}
	}

	return changed;
}



vkFormMonitor.prototype.checkForChanges = function()
{
	var	changed;

	if(this.callback == null)
		return;	// was stopped

	if(this.checkAndSync() && this.callback != null)
	{
		this.writeDebug('detected changes');
		this.callback();
	}

	if(this.callback != null)	// Re-schedule
	{
		var	self = this;
		setTimeout(
			function()
			{
				self.checkForChanges();
			}, 
			this.period
		);
	}
}

/* vkImagePreload.js */

function	vkImagePreload(maxThreads)
{
	this.maxThreads = maxThreads;

	if(
		arguments.length > 1 &&
		typeof(arguments[1] == 'string')
	)
		this.instanceName = arguments[1];
	else
		this.instanceName = null;

	this.currentThreads = 0;
	this.queue = [];
	this.preloads = [];

	this.debug = null;
}


// bool
vkImagePreload.prototype.enableDebug = function(debug)
{
	if(
		debug == null ||
		(
			typeof(debug) == 'object' &&
			debug.constructor == vkDebug
		)
	)
	{
		this.debug = debug;
		return true;
	}
			
	this.debug = null;
	return false;
}

// bool
vkImagePreload.prototype.disableDebug = function()
{
	return this.enableDebug(null);
}


vkImagePreload.prototype.setThreads = function(threads)
{
	this.maxThreads = threads;
}

vkImagePreload.prototype.add = function(image)
{
	if(this.currentThreads < this.maxThreads)
		this._preload(image);
	else
	{
		this.writeDebug('Queueing '+image);
		this.queue[this.queue.length] = image;
	}
}



/********************************/
/* PRIVATE METHODS */

// void
vkImagePreload.prototype.writeDebug = function(text)
{
	if(this.debug)
		this.debug.text('vkImagePreload'+(this.instanceName != null ? ' (' + this.instanceName + ')': '')+': '+text);
}

vkImagePreload.prototype._preload = function(image)
{
	var	idx, self;

	this.writeDebug('Preloading '+image);

	++this.currentThreads;
	idx = this.preloads.length;

	this.preloads[idx] = new Image;

	self = this;

	if(window.addEventListener)
	{
		this.preloads[idx].addEventListener('load', function() { self._onLoad(idx); }, false);
		this.preloads[idx].addEventListener('error', function() { self._onLoad(idx); }, false);
	}
	else
	{
		this.preloads[idx].attachEvent('onload', function() { self._onLoad(idx); });
		this.preloads[idx].attachEvent('onerror', function() { self._onLoad(idx); });
	}

	this.preloads[idx].src = image;
}

vkImagePreload.prototype._onLoad = function(idx)
{
	var	tmp;

	this.writeDebug('Image #'+idx+' loaded');

	--this.currentThreads;

	for(var i = 0; this.currentThreads < this.maxThreads && i < this.queue.length; i++)
	{
		if(this.queue[i] != null)
		{
			tmp = this.queue[i];
			this.queue[i] = null;
			this._preload(tmp);
		}
	}

	if(!this.currentThreads)
	{
		this.writeDebug('Thread queue empty, cleaning-up references');
		// Cleanup references
		this.queue = [];
		this.preloads = [];
	}
}



/* vkJSONRemote.js */

/*

Interface to the vkJSONRemote.php counterpart.

*/



/*********************************************/
/* PUBLIC */


function	vkJSONRemote()
{
	this.xmlRequest = null;
	this.aborted = false;
	this.currentTimeout = null;
	this.debug = null;
}

// bool
vkJSONRemote.prototype.enableDebug = function(debug)
{
	if(
		debug == null ||
		(
			typeof(debug) == 'object' &&
			debug['enable'] != 'undefined' &&
			typeof(debug.enable) == 'function'
		)
	)
	{
		this.debug = debug;
		return true;
	}
			
	this.debug = null;
	return false;
}

// bool
vkJSONRemote.prototype.disableDebug = function()
{
	return this.enableDebug(null);
}

// bool
vkJSONRemote.prototype.get = function(url, timeout)
{
	var	self = this;

	if(!this._prepare())
		return false;

	if(timeout !== undefined)
		this.currentTimeout = window.setTimeout(
			function() { self._onTimeout(); },
			timeout);

	this.xmlRequest.open('GET', url);
	this.xmlRequest.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8')

	this.xmlRequest.onreadystatechange = function()
	{
		if(self.aborted)
			return;

		switch(self.xmlRequest.readyState)
		{
			case 3:		// receiving
				self.onLoading();
				break;

			case 4:		// loaded
				self._onLoad();
				break;

			case 0:		// uninitialized
			case 1:		// open
			case 2:		// sent
			default:
				// we don't care
		}
	}

	this.xmlRequest.send(null);

	return true;
}





// bool
vkJSONRemote.prototype.post = function(url, data, timeout)
{
	var	self = this;

	if(!this._prepare())
		return false;

	if(timeout !== undefined)
		this.currentTimeout = window.setTimeout(
			function() { self._onTimeout() },
			timeout);

	this.xmlRequest.open('POST', url);
	this.xmlRequest.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8')

	this.xmlRequest.onreadystatechange = function()
	{
		if(self.aborted)
			return;

		switch(self.xmlRequest.readyState)
		{
			case 3:		// receiving
				self.onLoading();
				break;

			case 4:		// loaded
				self._onLoad();
				break;

			case 0:		// uninitialized
			case 1:		// open
			case 2:		// sent
			default:
				// we don't care
		}
	}

	var	json;

	if(data == null)
		json = null;
	else
	{
		try
		{
			//json = JSON.stringify(data);
			// 23/06/2010 - Yet another stupid IE bug
			// http://blogs.msdn.com/b/jscript/archive/2009/06/23/serializing-the-value-of-empty-dom-elements-using-native-json-in-ie8.aspx
			// http://tech.groups.yahoo.com/group/json/message/1271
			json = JSON.stringify(data, function(k,v) { return v==='' ? '' : v});
		}
		catch (e)
		{
			if(this.debug)
			{
				this._writeDebug('========== vkJSONRemote ==========');
				this._writeDebug('EVENT: Failed building JSON string');
				this._writeDebug('ERROR NAME: '+e.name);
				this._writeDebug('ERROR MESSAGE:');
				this._writeDebug('----------------------------------');
				this._writeDebug(e.message);
				this._writeDebug('==================================');
			}

			//json = 'null';
			return false;
		}
	}

	this.xmlRequest.send(json);

	return true;
}


vkJSONRemote.prototype.abort = function()
{
	this.aborted = true;

	if(this.currentTimeout)
	{
		clearTimeout(this.currentTimeout);
		this.currentTimeout = null;
	}
}

vkJSONRemote.prototype.free = function()
{
	this.abort();

	// 20/01/2009
	if(this.xmlRequest && typeof(this.xmlRequest.abort) == 'function')
		this.xmlRequest.abort();
	
	this.xmlRequest = null;
}


/*********************************************/
/* PROTECTED */


// May be overriden
vkJSONRemote.prototype.onLoading = function()
{
	/* Default behaviour: don't do anything */
}

// Should be overriden
vkJSONRemote.prototype.onLoad = function(httpStatus, result)
{
	/* Default behaviour: don't do anything */
}

// May be overriden
vkJSONRemote.prototype.onTimeout = function()
{
	/* Default behaviour: don't do anything */
}



/*********************************************/
/* PRIVATE */

// bool
vkJSONRemote.prototype._prepare = function()
{
	//this.abort();
	this.free();	// 20/01/2009

	try
	{
		// Get the "right" XML request object
		if(!window.XMLHttpRequest)
		{
			if(document.all && window.ActiveXObject)
			{
				try
				{
					this.xmlRequest = new ActiveXObject('Msxml2.XMLHTTP');
				}
				catch (e1)
				{
					try
					{
						this.xmlRequest = new ActiveXObject('Microsoft.XMLHTTP');
					}
					catch (e2)
					{
						throw 'vkJSONRemote: Browser lacks XMLHttpRequest support';
					}
				}
			}
			else
				throw 'vkJSONRemote: Browser lacks XMLHttpRequest support';
		}
		else
			this.xmlRequest = new XMLHttpRequest();
	}
	catch(e0)
	{
		return false;
	}

	this.aborted = false;
	this.currentTimeout = null;

	return true;
}




// void
vkJSONRemote.prototype._writeDebug = function(text)
{
	if(this.debug)
		this.debug.text(text);
}

// void
vkJSONRemote.prototype._onLoad = function()
{
	var	status, result;

	this.abort();	// Clears the timeout
	
	status = this.xmlRequest.status;

	if(this.debug)
	{
		this._writeDebug('========== vkJSONRemote ==========');
		this._writeDebug('EVENT: Received reply');
		this._writeDebug('HTTP STATUS: '+status);
		this._writeDebug('DATA:');
		this._writeDebug('----------------------------------');
		this._writeDebug(this.xmlRequest.responseText);
		this._writeDebug('==================================');
	}

	if(status == 200)
	{
		var	xmlObject, nodeList;

		if(	(xmlObject = this.xmlRequest.responseXML) &&
			(nodeList = xmlObject.getElementsByTagName('JSON')) &&
			nodeList.length == 1 &&
			nodeList[0].firstChild )
		{
			// Some browsers (ie. Firefox) have limitation on maximum node length, and therefore split
			// the received data in multiple nodes...

			var	i, value = '';

			for(i = 0; i < nodeList[0].childNodes.length; i++)
				value += nodeList[0].childNodes[i].nodeValue;

			try
			{
				eval('result='+value);
			}
			catch (e)
			{
				if(this.debug)
				{
					this._writeDebug('========== vkJSONRemote ==========');
					this._writeDebug('EVENT: Failed parsing JSON reply');
					this._writeDebug('ERROR NAME: '+e.name);
					this._writeDebug('ERROR MESSAGE:');
					this._writeDebug('----------------------------------');
					this._writeDebug(e.message);
					this._writeDebug('==================================');
				}
				result = null;
			}
		}
		else
			result = null;
	}
	else
		result = null;

	this.onLoad(status, result);
}

vkJSONRemote.prototype._onTimeout = function()
{
	//this.abort();	// Clears the timeout and aborts the request
	this.free();	// 20/01/2009 - Makes sure the request is canceled

	if(this.debug)
	{
		this._writeDebug('========== vkJSONRemote ==========');
		this._writeDebug('EVENT: Request timed out');
		this._writeDebug('==================================');
	}
	
	
	this.onTimeout();
}




/***********************************************************************/
/* STOLEN FROM http://www.JSON.org/json2.js (Public Domain) */
/* then minified @ http://fmarcia.info/jsmin/test1.html */

if(!this.JSON){this.JSON={};}
(function(){function f(n){return n<10?'0'+n:n;}
if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+
f(this.getUTCMonth()+1)+'-'+
f(this.getUTCDate())+'T'+
f(this.getUTCHours())+':'+
f(this.getUTCMinutes())+':'+
f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}
var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}
function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
if(typeof rep==='function'){value=rep.call(holder,key,value);}
switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}
v=partial.length===0?'[]':gap?'[\n'+gap+
partial.join(',\n'+gap)+'\n'+
mind+']':'['+partial.join(',')+']';gap=mind;return v;}
if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}
v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+
mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}
if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}
rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}
return str('',{'':value});};}
if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}
return reviver.call(holder,key,value);}
text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+
('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}
if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}
throw new SyntaxError('JSON.parse');};}}());


/* vkXMLRemote.js */


/*********************************************/
/* PUBLIC */


function	vkXMLRemote()
{
	this.xmlRequest = null;
	this.aborted = false;
	this.currentTimeout = null;
	this.debug = null;
}

// bool
vkXMLRemote.prototype.enableDebug = function(debug)
{
	if(
		debug == null ||
		(
			typeof(debug) == 'object' &&
			debug.constructor == vkDebug
		)
	)
	{
		this.debug = debug;
		return true;
	}
			
	this.debug = null;
	return false;
}

// bool
vkXMLRemote.prototype.disableDebug = function()
{
	return this.enableDebug(null);
}

// bool
vkXMLRemote.prototype.get = function(url, timeout)
{
	var	self = this;

	if(!this._prepare())
		return false;

	if(timeout !== undefined)
		this.currentTimeout = window.setTimeout(
			function() { self._onTimeout(); },
			timeout);

	this.xmlRequest.open('GET', url);
	this.xmlRequest.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8')

	this.xmlRequest.onreadystatechange = function()
	{
		if(self.aborted)
			return;

		switch(self.xmlRequest.readyState)
		{
			case 3:		// receiving
				self.onLoading();
				break;

			case 4:		// loaded
				self._onLoad();
				break;

			case 0:		// uninitialized
			case 1:		// open
			case 2:		// sent
			default:
				// we don't care
		}
	}

	this.xmlRequest.send(null);

	return true;
}

vkXMLRemote.prototype.abort = function()
{
	this.aborted = true;

	if(this.currentTimeout)
	{
		clearTimeout(this.currentTimeout);
		this.currentTimeout = null;
	}
}

vkXMLRemote.prototype.free = function()
{
	this.abort();

	// 20/01/2009
	if(this.xmlRequest && typeof(this.xmlRequest.abort) == 'function')
		this.xmlRequest.abort();
	
	this.xmlRequest = null;
}


/*********************************************/
/* PROTECTED */


// May be overriden
vkXMLRemote.prototype.onLoading = function()
{
	/* Default behaviour: don't do anything */
}

// Should be overriden
vkXMLRemote.prototype.onLoad = function(httpStatus, result)
{
	/* Default behaviour: don't do anything */
}

// May be overriden
vkXMLRemote.prototype.onTimeout = function()
{
	/* Default behaviour: don't do anything */
}



/*********************************************/
/* PRIVATE */

// bool
vkXMLRemote.prototype._prepare = function()
{
	//this.abort();
	this.free();	// 20/01/2009

	try
	{
		// Get the "right" XML request object
		if(!window.XMLHttpRequest)
		{
			if(document.all && window.ActiveXObject)
			{
				try
				{
					this.xmlRequest = new ActiveXObject('Msxml2.XMLHTTP');
				}
				catch (e1)
				{
					try
					{
						this.xmlRequest = new ActiveXObject('Microsoft.XMLHTTP');
					}
					catch (e2)
					{
						throw 'vkXMLRemote: Browser lacks XMLHttpRequest support';
					}
				}
			}
			else
				throw 'vkXMLRemote: Browser lacks XMLHttpRequest support';
		}
		else
			this.xmlRequest = new XMLHttpRequest();
	}
	catch(e0)
	{
		return false;
	}

	this.aborted = false;
	this.currentTimeout = null;

	return true;
}




// void
vkXMLRemote.prototype._writeDebug = function(text)
{
	if(this.debug)
		this.debug.text(text);
}

// void
vkXMLRemote.prototype._onLoad = function()
{
	var	status, result;

	this.abort();	// Clears the timeout
	
	status = this.xmlRequest.status;

	if(this.debug)
	{
		this._writeDebug('=========== vkXMLRemote ==========');
		this._writeDebug('EVENT: Received reply');
		this._writeDebug('HTTP STATUS: '+status);
		this._writeDebug('DATA:');
		this._writeDebug('----------------------------------');
		this._writeDebug(this.xmlRequest.responseText);
		this._writeDebug('==================================');
	}

	if(status == 200 && this.xmlRequest.responseXML)
		result = this.xmlRequest.responseXML;
	else
		result = null;

	this.onLoad(status, result);
}

vkXMLRemote.prototype._onTimeout = function()
{
	//this.abort();	// Clears the timeout and aborts the request
	this.free();	// 20/01/2009 - Makes sure the request is canceled

	if(this.debug)
	{
		this._writeDebug('=========== vkXMLRemote ==========');
		this._writeDebug('EVENT: Request timed out');
		this._writeDebug('==================================');
	}
	
	
	this.onTimeout();
}



// vkPopup.js
/*
(c) Vedad KAJTAZ, 2008
Use prohibited without prior written consent from the author.

PUBLIC API:

# GENERIC

	bool	setIconPath(path)											Default: /img/popup
	bool	setLang(lang)												Currently supported: EN (default), FR
	bool	setZIndexBase(base)											Default: 10000
	bool	addEventListener(event, callback)							Events:	popup_open, popup_close, modal_open, modal_close
		

# POPUP RELATED

	void	message(title, text, icon, callback)
	void	status(title, text, icon, cancelCallback)
	void	yesno(title, text, icon, callback, defaultYes)
	void	closePopup()
	bool	isPopupOpen()


# MODAL RELATED

	bool	modal(title, url, width, height)							Returns true on success, false on failure (ie. a modal already open)
	element	localModal(title, width, height)							Returns reference to the container <div>, null on failure

	void	closeModal()
	bool	isModalOpen()

*/

function	vkPopupClass()
{
	this.bg = null;
	this.currentPopup = null;
	this.currentModal = null;
	this.currentModalIsLocal = null;
	this.currentModalIframe = null;
	this.currentModalBody = null;

	this.currentPopupCloseCallback = null;

	this.disabledControls = { 'body' : null, 'modal' : null };
	this.hiddenControls = { 'body' : null, 'modal' : null };
	this.deactivatedControls = { 'body' : null, 'modal' : null };

	this.prevElId = 0;

	this.eventListeners = { 'popup_open' : [], 'popup_close' : [], 'modal_open' : [], 'modal_close' : [] };

	// Config
	this.iconPath = '/img/popup/';
	this.langs = 
	{
		'FR'	:	{
						'OK'		:	'OK',
						'CANCEL'	:	'Annuler',
						'YES'		:	'Oui',
						'NO'		:	'Non'
					},
		'EN'	:	{
						'OK'		:	'OK',
						'CANCEL'	:	'Cancel',
						'YES'		:	'Yes',
						'NO'		:	'No'
					}
	}

	this.currentLang = this.langs.EN;

	this.zIndexBase = 10000;


	if(window.addEventListener)
	{
		window.addEventListener('resize', function() { vkPopup.__reposition() }, false);
		window.addEventListener('scroll', function() { vkPopup.__reposition() }, false);
	}
	else if(window.attachEvent)
	{
		window.attachEvent('onresize', function() { vkPopup.__reposition() });
		window.attachEvent('onscroll', function() { vkPopup.__reposition() });
	}

	// Preload icons
	var	icons = [ 'info', 'warn', 'err', 'wait', 'question', 'ok' ];

	this.preloadedImages = new Array;

	for(var i = 0; i < icons.length; i++)
	{
		this.preloadedImages[i] = new Image();
		this.preloadedImages[i].src = this._iconUrl(icons[i]);
	}
}

/*********************************************************************************************************/
/* GENERIC */

vkPopupClass.prototype.setIconPath = function(path)
{
	if(typeof(path) != 'string')
		return false;

	this.iconPath = path;

	if(path.substring(-1, 1) != '/')
		this.iconPath += '/';

	return true;
}

vkPopupClass.prototype.setLang = function(lang)
{
	if(	typeof(lang) != 'string' ||
		!this.langs[(lang = lang.toUpperCase())] )
		return false;

	this.currentLang = this.langs[lang];

	return true;
}

vkPopupClass.prototype.setZIndexBase = function(base)
{
	if(typeof(base) != 'number')
		return false;

	this.zIndexBase = parseInt(base);
	return true;
}

vkPopupClass.prototype.addEventListener = function(event, callback)
{
	if(	typeof(event) != 'string' || 
		!this.eventListeners[event] ||
		typeof(callback) != 'function'
	)
		return false;

	this.eventListeners[event][this.eventListeners[event].length] = callback;
	return true;
}





/*********************************************************************************************************/
/* POPUP-RELATED */

vkPopupClass.prototype.message = function(title, text, icon, callback)
{
	var	focusBtnId = this._genId();

	this._createPopup(
		title,
		text,
		icon,
		'<button type="button" id="'+focusBtnId+'" onclick="vkPopup._closePopup(true, null);">'+vkDom.html(this.currentLang.OK)+'</button>',
		focusBtnId,
		callback
	);
}

vkPopupClass.prototype.status = function(title, text, icon, cancelCallback)
{
	var	buttons, focusBtnId;
	
	if(cancelCallback)
	{
		focusBtnId = this._genId();
		buttons = '<button type="button" id="'+focusBtnId+'" onclick="vkPopup._closePopup(true, null);">'+vkDom.html(this.currentLang.CANCEL)+'</button>';
	}
	else
	{
		focusBtnId = null;
		buttons = null;
	}

	this._createPopup(
		title,
		text,
		icon,
		buttons,
		focusBtnId,
		cancelCallback
	);
}

vkPopupClass.prototype.yesno = function(title, text, icon, callback, defaultYes)
{
	var	yesBtnId = this._genId();
	var	noBtnId = this._genId();

	this._createPopup(
		title,
		text,
		icon,
		'<button type="button" id="'+yesBtnId+'" onclick="vkPopup._closePopup(true, true);">'+vkDom.html(this.currentLang.YES)+'</button>'+
		'<button type="button" id="'+noBtnId+'" onclick="vkPopup._closePopup(true, false);">'+vkDom.html(this.currentLang.NO)+'</button>',
		(defaultYes ? yesBtnId : noBtnId),
		callback
	);
}

vkPopupClass.prototype.closePopup = function()
{
	this._closePopup(false);
}

vkPopupClass.prototype.isPopupOpen = function()
{
	return this.currentPopup ? true : false;
}





/*********************************************************************************************************/
/* MODAL-RELATED */

vkPopupClass.prototype.modal = function(title, url, width, height)
{
	// Fail if there is already a modal or a popup 
	if(this.currentPopup || this.currentModal)
		return false;

	// Disable / Hide elements within the body!
	this.disabledControls.body = [];
	this.hiddenControls.body = [];
	this.deactivatedControls.body = [];

	this._disableControls(document.body, this.disabledControls.body, this.hiddenControls.body, this.deactivatedControls.body);
	
	// Get the background, position it and display it		
	this._getBg();
	this._setBgPosition();

	this.bg.style.zIndex = this.zIndexBase + 1;
	this.bg.style.display = 'block';

	// Okay, build the popup body...
	// TODO: REDESIGN THIS WITHOUT TABLES !
	var	modalBody, iconUrl;

	var	iframeId = this._genId();

/*
	modalBody =		'<table>';

	if(typeof(title) == 'string' && title.length)
		modalBody +=	'<thead>'+
							'<tr>'+
								'<td>'+vkDom.html(title)+'</td>'+
							'</tr>'+
						'</thead>';

	modalBody +=		'<tbody>'+
							'<tr>'+
								'<td><iframe id="'+iframeId+'" src="'+vkDom.html(url)+'" width="'+parseInt(width)+'" height="'+parseInt(height)+'" style="visibility: hidden;"></iframe></td>'+
							'</tr>'+
						'</tbody>'+
					'</table>';
*/						
	// 22/07/2010

	modalBody =		'<table class="vkPopupModal">';

	if(typeof(title) == 'string' && title.length)
		modalBody +=	'<thead class="vkPopupModal">'+
							'<tr class="vkPopupModal">'+
								'<td class="vkPopupModal">'+vkDom.html(title)+'</td>'+
							'</tr>'+
						'</thead>';

	modalBody +=		'<tbody class="vkPopupModal">'+
							'<tr class="vkPopupModal">'+
								'<td class="vkPopupModal"><iframe id="'+iframeId+'" src="'+vkDom.html(url)+'" width="'+parseInt(width)+'" height="'+parseInt(height)+'" style="visibility: hidden;"></iframe></td>'+
							'</tr>'+
						'</tbody>'+
					'</table>';


	this.currentModal = document.createElement('div');
	this.currentModal.className = 'vkPopupModal';
	this.currentModal.style.zIndex = this.zIndexBase + 2;
	this.currentModal.style.visibility = 'hidden';
	this.currentModal.innerHTML = modalBody;

	document.body.appendChild(this.currentModal);

	this.currentModalIsLocal = false;
	this.currentModalIframe = vkDom.el(iframeId);
	this.currentModalBody = null;

	// Position
	this._setModalPosition();

	// Show
	this.currentModal.style.visibility = 'visible';

	this._dispatchEvent('modal_open');

	return true;	
}


vkPopupClass.prototype.localModal = function(title, width, height)
{
	// Fail if there is already a modal or a popup 
	if(this.currentPopup || this.currentModal)
		return null;

	// Disable / Hide elements within the body!
	this.disabledControls.body = [];
	this.hiddenControls.body = [];
	this.deactivatedControls.body = [];

	this._disableControls(document.body, this.disabledControls.body, this.hiddenControls.body, this.deactivatedControls.body);
	
	// Get the background, position it and display it		
	this._getBg();
	this._setBgPosition();

	this.bg.style.zIndex = this.zIndexBase + 1;
	this.bg.style.display = 'block';

	// Okay, build the popup body...
	// TODO: REDESIGN THIS WITHOUT TABLES !
	var	modalBody, iconUrl;

	var	containerId = this._genId();

/*
	modalBody =		'<table>';

	if(typeof(title) == 'string' && title.length)
		modalBody +=	'<thead>'+
							'<tr>'+
								'<td>'+vkDom.html(title)+'</td>'+
							'</tr>'+
						'</thead>';

	modalBody +=		'<tbody>'+
							'<tr>'+
								'<td><div id="'+containerId+'" style="width: '+parseInt(width)+'px; height: '+parseInt(height)+'px;"></div></td>'+
							'</tr>'+
						'</tbody>'+
					'</table>';
*/
	// 22/07/2010

	modalBody =		'<table class="vkPopupModal">';

	if(typeof(title) == 'string' && title.length)
		modalBody +=	'<thead class="vkPopupModal">'+
							'<tr class="vkPopupModal">'+
								'<td class="vkPopupModal">'+vkDom.html(title)+'</td>'+
							'</tr>'+
						'</thead>';

	modalBody +=		'<tbody class="vkPopupModal">'+
							'<tr class="vkPopupModal">'+
								'<td class="vkPopupModal"><div id="'+containerId+'" style="width: '+parseInt(width)+'px; height: '+parseInt(height)+'px;"></div></td>'+
							'</tr>'+
						'</tbody>'+
					'</table>';


	this.currentModal = document.createElement('div');
	this.currentModal.className = 'vkPopupModal';
	this.currentModal.style.zIndex = this.zIndexBase + 2;
	this.currentModal.style.visibility = 'hidden';
	this.currentModal.innerHTML = modalBody;

	document.body.appendChild(this.currentModal);

	this.currentModalIsLocal = true;
	this.currentModalIframe = null;
	this.currentModalBody = vkDom.el(containerId);

	// Position
	this._setModalPosition();

	// Show
	this.currentModal.style.visibility = 'visible';

	this._dispatchEvent('modal_open');

	return this.currentModalBody;
}







vkPopupClass.prototype.closeModal = function()
{
	this._closeModal();
}

vkPopupClass.prototype.isModalOpen = function()
{
	return this.currentModal ? true : false;
}







/*********************************************************************************************************/
/* PRIVATE */


vkPopupClass.prototype.__reposition = function()
{
	if(this.currentPopup || this.currentModal)
	{
		this.bg.style.width = '0px';
		this.bg.style.height = '0px';

		this._setPopupPosition();
		this._setModalPosition();
		this._setBgPosition();
	}
}



vkPopupClass.prototype.__modalReady = function(body)
{
//	if(!this.currentModal || this.currentModalIsLocal || this.currentModalBody)
//		return;

	// This failed when reloading modal - this.currentModalBody was set but was pointing nowhere

	if(!this.currentModal || this.currentModalIsLocal || this.currentModalBody == body)
		return;

	this.currentModalBody = body;
	this.currentModalIframe.style.visibility = 'visible';

	if(this.currentPopup)
	{
		this.disabledControls.modal = [];
		this.hiddenControls.modal = [];
		this.deactivatedControls.modal = [];

		this._disableControls(this.currentModalBody, this.disabledControls.modal, this.hiddenControls.modal, this.deactivatedControls.modal);
	}
}




vkPopupClass.prototype._createPopup = function(title, text, icon, buttons, focusItem, closeCallback)
{
	// Close current popup if any
	this._closePopup(false);

	// Is a modal box present?
	if(this.currentModal)
	{
		// Disable / Hide elements within the modal!

		this.disabledControls.modal = [];
		this.hiddenControls.modal = [];
		this.deactivatedControls.modal = [];


		if(this.currentModalIsLocal)
			this._disableControls(this.currentModal, this.disabledControls.modal, this.hiddenControls.modal, this.deactivatedControls.modal);
		else
		{
			// Remote - the action depends on modal load state...
			if(this.currentModalBody)
				this._disableControls(this.currentModalBody, this.disabledControls.modal, this.hiddenControls.modal, this.deactivatedControls.modal);
		}

		// Okay, there already is a background, move it in front of the modal
		this.bg.style.zIndex = this.zIndexBase + 3;
	}
	else
	{
		// Disable / Hide elements within the body!
		this.disabledControls.body = [];
		this.hiddenControls.body = [];
		this.deactivatedControls.body = [];

		this._disableControls(document.body, this.disabledControls.body, this.hiddenControls.body, this.deactivatedControls.body);
		
		// There is no background, get it, position it and display it		
		this._getBg();
		this._setBgPosition();

		this.bg.style.zIndex = this.zIndexBase + 3;
		this.bg.style.display = 'block';
	}

	this.currentPopupCloseCallback = closeCallback;

	// Okay, build the popup body...
	// TODO: REDESIGN THIS WITHOUT TABLES !
	var	popupBody, iconUrl;

	popupBody =		'<table>';

	if(typeof(title) == 'string' && title.length)
		popupBody +=	'<thead>'+
							'<tr>'+
								'<td colspan="2">'+vkDom.html(title)+'</td>'+
							'</tr>'+
						'</thead>';

	popupBody +=		'<tbody>'+
							'<tr>';


	if((iconUrl = this._iconUrl(icon)) == null)
		popupBody +=			'<td colspan="2">'+vkDom.nl2br(vkDom.html(text))+'</td>';
	else
		popupBody +=			'<td class="icon"><img src="'+iconUrl+'" /></td>'+
								'<td>'+vkDom.nl2br(vkDom.html(text))+'</td>';


	popupBody += 			'</tr>';

	if(buttons != null)
		popupBody +=		'<tr>'+
								'<td class="buttons" colspan="2">'+buttons+'</td>'+
							'</tr>';

	popupBody += 		'</tbody>'+
					'</table>';


	this.currentPopup = document.createElement('div');
	this.currentPopup.className = 'vkPopup';
	this.currentPopup.style.zIndex = this.zIndexBase + 4;
	this.currentPopup.style.visibility = 'hidden';
	this.currentPopup.innerHTML = popupBody;

	document.body.appendChild(this.currentPopup);

	// Position
	this._setPopupPosition();

	// Show
	this.currentPopup.style.visibility = 'visible';

	// Focus on button
	if(focusItem)
		vkDom.focus(focusItem);

	this._dispatchEvent('popup_open');
}

vkPopupClass.prototype._closePopup = function(fireCallback, callbackParam)
{
	if(!this.currentPopup)
		return;

	this.currentPopup.parentNode.removeChild(this.currentPopup);
	this.currentPopup = null;

	// Is there still a modal?
	if(this.currentModal)
	{
		// Okay, only re-enable modal controls, and lower the bg zIndex
		this._enableControls(this.disabledControls.modal, this.hiddenControls.modal, this.deactivatedControls.modal);

		this.bg.style.zIndex = this.zIndexBase + 1;
	}
	else
	{
		// No modal, re-enable everything, and hide the background div

		this.bg.style.display = 'none';

		this._enableControls(this.disabledControls.body, this.hiddenControls.body, this.deactivatedControls.body);
		this.disabledControls.modal = null;
		this.hiddenControls.modal = null;
		this.deactivatedControls.modal = null;
	}

	this._dispatchEvent('popup_close');

	if(fireCallback && typeof(this.currentPopupCloseCallback) == 'function')
		this.currentPopupCloseCallback(callbackParam);
}


vkPopupClass.prototype._closeModal = function()
{
	if(!this.currentModal)
		return;

	this.currentModal.parentNode.removeChild(this.currentModal);
	this.currentModal = null;
	this.currentModalIframe = null;
	this.currentModalBody = null;

	this.disabledControls.modal = null;
	this.hiddenControls.modal = null;
	this.deactivatedControls.modal = null;

	// Is there a popup left?
	if(this.currentPopup)
		this.bg.style.zIndex = this.zIndexBase + 3;
	else
	{
		this.bg.style.display = 'none';

		this._enableControls(this.disabledControls.body, this.hiddenControls.body, this.deactivatedControls.body);
		this.disabledControls.modal = null;
		this.hiddenControls.modal = null;
		this.deactivatedControls.modal = null;
	}

	this._dispatchEvent('modal_close');
}










vkPopupClass.prototype._getBg = function()
{
	if(!this.bg)
	{
		this.bg = document.createElement('div');
		this.bg.className = 'vkPopupBg';
		this.bg.style.display = 'none';
		document.body.appendChild(this.bg);
	}
}

vkPopupClass.prototype._setBgPosition = function()
{
	this.bg.style.width = Math.max(document.documentElement.clientWidth, document.documentElement.scrollWidth)+'px';
	this.bg.style.height = Math.max(document.documentElement.clientHeight, document.documentElement.scrollHeight)+'px';
}

vkPopupClass.prototype._setPopupPosition = function()
{
	if(this.currentPopup)
	{
		this.currentPopup.style.left = (Math.max(document.documentElement.scrollLeft, document.body.scrollLeft) + ((document.documentElement.clientWidth-this.currentPopup.clientWidth)>>1))+'px';
		this.currentPopup.style.top = (Math.max(document.documentElement.scrollTop, document.body.scrollTop) + ((document.documentElement.clientHeight-this.currentPopup.clientHeight)>>1))+'px';
		/*
		this.currentPopup.style.left = (Math.max(document.documentElement.scrollLeft, document.body.scrollLeft) + ((Math.max(document.documentElement.clientWidth, document.documentElement.scrollWidth)-this.currentPopup.clientWidth)>>1))+'px';
		this.currentPopup.style.top = (Math.max(document.documentElement.scrollTop, document.body.scrollTop) + ((Math.max(document.documentElement.clientHeight, document.documentElement.scrollHeight)-this.currentPopup.clientHeight)>>1))+'px';
		*/
	}
}

vkPopupClass.prototype._setModalPosition = function()
{
	if(this.currentModal)
	{
		this.currentModal.style.left = (Math.max(document.documentElement.scrollLeft, document.body.scrollLeft) + ((document.documentElement.clientWidth-this.currentModal.clientWidth)>>1))+'px';
		this.currentModal.style.top = (Math.max(document.documentElement.scrollTop, document.body.scrollTop) + ((document.documentElement.clientHeight-this.currentModal.clientHeight)>>1))+'px';
	}
}

vkPopupClass.prototype._enableControls = function(disabled, hidden, deactivated)
{
	var	i;

	if(hidden != null)
	{
		for(i = 0; i < hidden.length; i++)
		{
			if(hidden[i])
				hidden[i].style.visibility = 'visible';
		}
	}

	if(disabled != null)
	{
		for(i = 0; i < disabled.length; i++)
		{
			if(disabled[i])
				disabled[i].disabled = false;
		}
	}

	if(deactivated != null)
	{
		for(i = 0; i < deactivated.length; i++)
		{
			if(deactivated[i].object)
				deactivated[i].object.onclick = deactivated[i].onclick;
		}
	}
}

vkPopupClass.prototype._disableControls = function(node, disabled, hidden, deactivated)
{
	var	i, list;

	// Lookup inputs
	list = node.getElementsByTagName('input');
	for(i = 0; i < list.length; i++)
	{
		if(
			list[i].type != 'HIDDEN' &&
			!list[i].disabled
		)
		{
			list[i].disabled = true;
			disabled[disabled.length] = list[i];
		}
	}

	// Lookup textareas
	list = node.getElementsByTagName('textarea');
	for(i = 0; i < list.length; i++)
	{
		if(!list[i].disabled)
		{
			list[i].disabled = true;
			disabled[disabled.length] = list[i];
		}
	}


	// Lookup selects
	list = node.getElementsByTagName('select');
	for(i = 0; i < list.length; i++)
	{
		if(!list[i].disabled)
		{
			list[i].disabled = true;
			disabled[disabled.length] = list[i];
		}

		if(
			/msie|MSIE 6/.test(navigator.userAgent) &&
			list[i].style.visibility != 'hidden'
		)
		{
			list[i].style.visibility = 'hidden';
			hidden[hidden.length] = list[i];
		}
	}

	// Lookup buttons
	list = node.getElementsByTagName('button');
	for(i = 0; i < list.length; i++)
	{
		if(!list[i].disabled)
		{
			list[i].disabled = true;
			disabled[disabled.length] = list[i];
		}
	}


	function	returnFalse()
	{
		kigoDebug.text('Intercepting link');
		return false;
	}

	// Lookup links
	list = node.getElementsByTagName('a');
	for(i = 0; i < list.length; i++)
	{
		if(!list[i].disabled)
		{
			list[i].disabled = true;
			disabled[disabled.length] = list[i];
		}

		/*
		if(list[i].style.visibility != 'hidden')
		{
			list[i].style.visibility = 'hidden';
			hidden[hidden.length] = list[i];
		}
		*/

		deactivated[deactivated.length] = {
			'object'	:	list[i],
			'onclick'	:	list[i].onclick ? list[i].onclick : null
		}

		list[i].onclick = returnFalse;
	}
}

/*
vkPopupClass.prototype._disableControls = function(node, disabled, hidden)
{
	switch(node.tagName)
	{
		case 'INPUT':
			// Patch 05/11/2007
			// Do not disable "HIDDEN" fields, as this prevents us from submitting forms in background

			if(	node.type.toUpperCase() != 'HIDDEN' && 
				!node.disabled )
			{
				node.disabled = true;
				disabled[disabled.length] = node;
			}
			return;
		
		case 'TEXTAREA':
		case 'BUTTON':
		
			if(!node.disabled)
			{
				node.disabled = true;
				disabled[disabled.length] = node;
			}
			return;

		case 'SELECT':
		case 'A':	// Disabling doesn't work in firefox :(

			// both hide & disable
			if(!node.disabled)
			{
				node.disabled = true;
				disabled[disabled.length] = node;
			}

			if(node.style.visibility != 'hidden')
			{
				node.style.visibility = 'hidden';
				hidden[hidden.length] = node;
			}
			return;

		case 'DIV':
			//if(node.id == 'vkDebug')
			if(node.className == 'vkDebug')
				return;
	}		

	if(node.hasChildNodes())
	{
		for(var i = 0; i < node.childNodes.length; i++)
		{
			if(node.childNodes[i].nodeType == 1)	// 9, 10, ...
				this._disableControls(node.childNodes[i], disabled, hidden);
		}
	}
}
*/

vkPopupClass.prototype._dispatchEvent = function(event)
{
	for(var i = 0; i < this.eventListeners[event].length; i++)
		this.eventListeners[event][i]();
}

vkPopupClass.prototype._genId = function()
{
	return 'vkPopupEl'+(++this.prevElId);
}

vkPopupClass.prototype._iconUrl = function(icon)
{
	if(typeof(icon) != 'string')
		return null;

	switch(icon)
	{
		case 'info':
			return this.iconPath+'Info.png';

		case 'warn':
		case 'warning':
			return this.iconPath+'Warning.png';

		case 'err':
		case 'error':
			return this.iconPath+'Error.png';

		case 'wait':
			return this.iconPath+'Gear.png';

		case 'question':
		case 'ask':
			return this.iconPath+'Question.png';

		case 'ok':
		case 'success':
			return this.iconPath+'Good.png';
	}
	return null;
}


var	vkPopup = new vkPopupClass();

// vkModal.js
/*
(c) Vedad KAJTAZ, 2007
Use prohibited without prior written consent from the author.

PUBLIC API:

# POPUP RELATED

	window	opener()	
	void	message(title, text, icon, callback)
	void	status(title, text, icon, cancelCallback)
	void	yesno(title, text, icon, callback, defaultYes)
	void	closePopup()
	bool	isPopupOpen()

*/


function	vkModalClass()
{
	// Let the parent know we finished loading!
	vkDom.onLoad(
		function()
		{
			try {
				if(	window.parent &&
					window.parent.vkPopup &&
					window.parent.vkPopup.__modalReady
				)
					window.parent.vkPopup.__modalReady(document.body);
			} catch (err) {
				/*
				 * Using Firefox and Opera while loading a page from a server inside an iframe on a different domain 
				 * triggers a halt in javascript execution.
				 * While this error is also raised when all files are on the same domain, the script does not halt.
				 * 
				 * So we catch this exception to be ignored.
				 *   
				 * Error: Permission denied for <http://ourwebsite.com> to get property Window.vkPopup from <http://otherwebsite.com>.
				 * 
				 */
			}
		}
	);

	// 10/12/2007
	// IE throws an error when the modal is reloaded, and a previously open popup is being closed,
	// because elements stored in "disabled controls" no longer exist.
	// Therefore, we need to intercept the modal unload, in order to reinit. the disabled/hidden controls list

	vkDom.onUnload(
		function()
		{
			try {
				if(	window.parent &&
					window.parent.vkPopup
				)
				{
					window.parent.vkPopup.disabledControls.modal = null;
					window.parent.vkPopup.hiddenControls.modal = null;
				}
			} catch (err) {
				/*
				 * Using Firefox and Opera while loading a page from a server inside an iframe on a different domain
				 * triggers a halt in javascript execution.
				 * While this error is also raised when all files are on the same domain, the script does not halt.
				 * 
				 * So we catch this exception to be ignored.
				 *   
				 * Error: Permission denied for <http://ourwebsite.com> to get property Window.vkPopup from <http://otherwebsite.com>.
				 * 
				 */
			}
		}
	);
}

vkModalClass.prototype.opener = function()
{
	return window.parent;
}

vkModalClass.prototype.message = function(title, text, icon, callback)
{
	window.parent.vkPopup.message(title, text, icon, callback);
}

vkModalClass.prototype.status = function(title, text, icon, cancelCallback)
{
	window.parent.vkPopup.status(title, text, icon, cancelCallback);
}

vkModalClass.prototype.yesno = function(title, text, icon, callback, defaultYes)
{
	window.parent.vkPopup.yesno(title, text, icon, callback, defaultYes);
}

vkModalClass.prototype.closePopup = function()
{
	window.parent.vkPopup.closePopup();
}

vkModalClass.prototype.isPopupOpen = function()
{
	window.parent.vkPopup.isPopupOpen();
}



vkModalClass.prototype.close = function()
{
	window.parent.vkPopup.closeModal();
}




var	vkModal = new vkModalClass();

/* vkUserIdleMonitor.js */

// 23/05/2009

/********************************/
/* PUBLIC METHODS */

function	vkUserIdleMonitor()
{
	var	self = this;

	this.defaultCallback = (arguments.length && typeof(arguments[0]) == 'function') ? arguments[0] : null;
	this.defaultPeriod = (arguments.length > 1 && typeof(arguments[1]) == 'number') ? parseInt(arguments[1]) : null;

	this.currentCallback = null;
	this.currentPeriod = null;
	
	this.timer = null;
	this.activityDetected = false;

	this.activityCallback = null;

	function	eventDetected()
	{
		self.activityDetected = true;
	}

	var	events = [ 'mousemove', 'mousedown', 'mouseup', 'keydown', 'keyup', 'mousewheel', 'mousemultiwheel', 'scroll', 'resize' ];

	for(var i = 0; i < events.length; i++)
	{
		if(window.addEventListener)
			window.addEventListener(events[i], eventDetected, false);
		else if(window.attachEvent)
			window.attachEvent('on'+events[i], eventDetected);
	}

	setInterval(
		function()
		{
			if(self.activityDetected && self.timer != null)
			{
				self.resume();
				if(typeof(self.activityCallback) == 'function')
					self.activityCallback(self)
			}
		},
		999
	);
}

vkUserIdleMonitor.prototype.monitor = function()
{
	var	self = this;

	this.activityDetected = false;
	this.stop();

	if(arguments.length == 2 && typeof(arguments[0]) == 'function' && typeof(arguments[1]) == 'number')
	{
		this.currentCallback = arguments[0];
		this.currentPeriod = parseInt(arguments[1]);
	}
	else if(arguments.length == 1 && typeof(arguments[0]) == 'function')
	{
		this.currentCallback = arguments[0];
		this.currentPeriod = this.defaultPeriod;
	}
	else if(arguments.length == 1 && typeof(arguments[0]) == 'number')
	{
		this.currentCallback = this.defaultCallback;
		this.currentPeriod = parseInt(arguments[0]);
	}
	else if(!arguments.length)
	{
		this.currentCallback = this.defaultCallback;
		this.currentPeriod = this.defaultPeriod;
	}


	if(
		this.currentCallback != null && 
		this.currentPeriod != null &&
		this.currentPeriod > 0
	)
	{
		this.timer = setTimeout(
			function()
			{
				if(self.stop())	// was running
					self.currentCallback(self);
			},
			this.currentPeriod * 1000
		);


		return true;
	}

	return false;
}

vkUserIdleMonitor.prototype.stop = function()
{
	if(this.timer != null)
	{
		clearTimeout(this.timer);
		this.timer = null;

		return true;
	}

	return false;
}

vkUserIdleMonitor.prototype.resume = function()
{
	return this.monitor(this.currentCallback, this.currentPeriod);
}


/* /js/kigo.js */ /* UTF8 COOKIE: éà */

/* 
	Compatibility layer 
	1) PHP kigo class
	2) PHP libc
	
*/

var	kigo = {

	/*****************************************************************************************/
	/* KIGO */


	'CFG'			:	{},


	/*****************************************************************************************/
	/* TECH */

	'toString'		:	function(string)
	{
		if(string == null)
			return '';

		switch(typeof(string))
		{
			case 'string':
				return string;

			case 'number':
				return ''+string;

			case 'boolean':
				return string ? 'true' : 'false';

			case 'function':
				return '';

			case 'object':

				if(
					typeof(string['toString']) == 'function' &&
					typeof(string = string.toString()) == 'string'
				)
					return string;

				return '';

			case 'undefined':
			default:
				return '';
		}
	},

	'isset'			:	function(v)
	{
		return (typeof(v) == 'undefined' || v == null) ? false : true;
	},

	// 23/02/2010
	'is_function'	:	function(f)
	{
		return typeof(f) == 'function' ? true : false;
	},

	// 24/02/2010
	'is_null'		:	function(n)
	{
		return (typeof(n) == 'object' && n == null) ? true : false;
	},

	'is_array'		:	function(a)
	{
		return (
			a != null &&	
			typeof(a) == 'object' &&
			typeof(a['length']) == 'number' &&
			typeof(a['concat']) == 'function' &&
			typeof(a['join']) == 'function' &&
			typeof(a['pop']) == 'function' &&
			typeof(a['push']) == 'function' &&
			typeof(a['reverse']) == 'function' &&
			typeof(a['shift']) == 'function' &&
			typeof(a['slice']) == 'function' &&
			typeof(a['splice']) == 'function' &&
			typeof(a['sort']) == 'function' &&
			typeof(a['toString']) == 'function' &&
			typeof(a['unshift']) == 'function' &&
			typeof(a['valueOf']) == 'function'
		);
	},

	'in_array'		:	function(value, arr)	// [,strict=false]
	{
		if(!this.is_array(arr) || !arr.length)
			return false;

		if(arguments.length > 2 && arguments[2])
		{
			// strict
			for(var i = 0; i < arr.length; i++)
			{
				if(arr[i] === value)
					return true;
			}
		}
		else
		{
			for(var i = 0; i < arr.length; i++)
			{
				if(arr[i] == value)
					return true;
			}
		}

		return false;
	},

	'is_string'		:	function(s)
	{
		return typeof(s) == 'string';
	},

	'is_object'		:	function(o)
	{
		return typeof(o) == 'object' && o != null && !kigo.is_array(o);
	},

	'is_number'		:	function(n)
	{
		return typeof(n) == 'number';
	},

	'is_int'		:	function(i)
	{
		return typeof(i) == 'number' && ''+i == ''+this.intval(i);
	},

	'is_bool'		:	function(b)
	{
		return typeof(b) == 'boolean';
	},

	'intval'		:	function(value)
	{
		switch(typeof(value))
		{
			case 'number':
				if(isNaN(value = parseInt(Math.round(value), 10)))
					return 0;
				return value;

			case 'string':
				if(isNaN(value = parseInt(this.trim(value), 10)))
					return 0;
				return value;
	
			case 'boolean':
				return value ? 1 : 0;

			case 'object':	// Including null
			case 'function':
			default:
				return 0;
		}
	},

	'trim'			:	function(string)
	{
		if(!(string = this.toString(string)).length)
			return '';
		string = string.replace(new RegExp('^[\\s]+', 'g'), '');
		return string.replace(new RegExp('[\\s]+$', 'g'), '');
	},

	// 24/11/2009 - A couple of new ones
	'substr'		:	function(string, start)	// [, length]
	{
		var	length, total;

		if( (length = arguments.length > 2 ? this.intval(arguments[2]) : null) == 0 || !(total = (string = this.toString(string)).length))
			return '';

		if( (start = this.intval(start)) >= 0)
		{
			if(start >= total)
				return '';
		}
		else if((start += total) < 0)
			start = 0;

		if(length == null)
			return string.substr(start);

		if(length > 0)
			return string.substr(start, length);

		return string.substr(start, total - start + length);
	},


	'str_repeat'	:	function(text, multiplier)
	{
		var	ret = '';

		multiplier = Math.max(0, kigo.intval(multiplier));

		for(var i = 0; i < multiplier; i++)
			ret += text;

		return ret;
	},


	// 08/06/2010
	'strpos'		:	function(str, findme)	// [, offset]
	{
		// This is, currently, totally unoptimized!
		for(var i = (arguments.length > 2 ? arguments[2] : 0); i <= str.length - findme.length; i++)
		{
			if(this.substr(str, i, findme.length) == findme)
				return i;
		}

		return null;
	},

	'strlen'		:	function(str)
	{
		return kigo.toString(str).length;
	},

	// 02/08/2010
	'strtoupper'	:	function(str)
	{
		return this.toString(str).toUpperCase();
	},

	'strtolower'	:	function(str)
	{
		return this.toString(str).toLowerCase();
	},

/* TODO: explode and implode !
	// 02/08/2010
	'explode'		:	function(sep, str)	// [, limit]
	{

	},
*/

	'rawurlencode'	:	function(str)
	{
		return encodeURIComponent(str);
	},

	// 01/03/2010
	'array_merge'	:	function()	// [a1 [, a2 [, a3 [, ...]]]]
	{
		var	res = [];

		for(var i = 0; i < arguments.length; i++)
		{
			for(var j = 0; j < arguments[i].length; j++)
				res.push(arguments[i][j]);
		}

		return res;
	},

	// 26/04/2010
	'object_merge'	:	function()	// [o1 [, o2 [, o3 [, ...]]]]
	{
		var	res = {};

		for(var i = 0; i < arguments.length; i++)
		{
			(new kigoAssoc(arguments[i])).forEach(
				function(key, value)
				{
					res[key] = value;
				}
			);
		}

		return res;
	},

	// 24/02/2010
	'getScroll'		:	function()
	{
		return Math.max(document.body.scrollTop, document.documentElement.scrollTop);
	},

	'setScroll'		:	function(scroll)
	{
		document.body.scrollTop = scroll;
		document.documentElement.scrollTop = scroll;
	}
};



/* /js/kigoFront.js */ /* UTF8 COOKIE: éà */

var	kigoFront = {

	'user'		:	{

		'isOwner'	:	function()	{ return _owner != null ? true : false; },
		'isRa'		:	function()	{ return _ra != null ? true : false; },
		'isRA'		:	function()	{ return this.isRa(); },
		'id'		:	function()	{ return (_owner != null ? _owner : (_ra != null ? _ra : null)); }
	},

	'formatPrice'			:	function(price)	// [, decimals=0 ]
	{
		// Formats the price as a float (or integer) rounded to $decimals decimals...
		var	decimals = arguments.length > 1 ? kigo.intval(arguments[1]) : 0;

		if(decimals)
			return (Math.round(price * (decimals = Math.pow(10, decimals)))) / decimals;
		
		return Math.round(price);
	},


	'formatDisplayPrice'	:	function(price)	// [, decimals=0 ]
	{
		var	decimals = arguments.length > 1 ? kigo.intval(arguments[1]) : 0;
		var	tmp, positive, newIntPart = '';

		// Round...	
		price = this.formatPrice(price, decimals);


		if(decimals && (tmp = (''+price).split('.')).length > 1)
		{
			intPart = ''+tmp[0];
			decimalPart = '.'+tmp[1];
		}
		else
		{
			intPart = ''+price;
			decimalPart = '.';
		}


		if(!(positive = (intPart.substr(0, 1) == '-') ? false : true))
			intPart = intPart.substr(1);
		
		// Work on intPart
		for(var i = intPart.length - 1; i >= 0; i--)
		{
			if(i && !((intPart.length-i)%3))
				newIntPart = '\'' + intPart.substr(i, 1) + newIntPart;
			else
				newIntPart = intPart.substr(i, 1) + newIntPart;
		}

		while(decimalPart.length <= decimals)
			decimalPart += '0';

		return (positive ? '' : '-') + newIntPart + decimalPart;
	},


	'userGuideURL'		:	function(book, page)	// [ book, page [, paragraph] ]
	{
		if(arguments.length >= 2)
			return 'userguide.php?'+encodeURIComponent(book)+'+'+encodeURIComponent(page)+(arguments.length > 2 && kigo.is_string(arguments[2]) ? ('#'+encodeURIComponent(arguments[2])) : '');

		return 'userguide.php';
	}

};

/* /js/kigoMenu.js */ /* UTF8 COOKIE: éà */

/*

Javascript list-based kigo menu

*/

function	kigoMenu()
{
	this.selected = -1;
	this.divElements = [];
	//this.aElements = [];
	this.liElements = [];

	this.ulElement = document.createElement('ul');
	//this.ulElement.className = className;
}

kigoMenu.prototype.add = function(label, div)
{
	var	idx, li, d;
	var	self = this;

  // DIV
	this.divElements[idx = this.divElements.length] = vkDom.el(div);

/*
  // A
	a = document.createElement('a');
	a.innerHTML = vkDom.html(label);
	a.href = '#';

	if(window.addEventListener)
		a.addEventListener('click', function() { return self._onClick(idx); }, false);
	else if(window.attachEvent)
		a.attachEvent('onclick', function() { return self._onClick(idx); });

	this.aElements[idx] = a;

	li = document.createElement('li');
	li.appendChild(a);
*/

	d = document.createElement('div');
	d.innerHTML = vkDom.html(label);

	if(window.addEventListener)
		d.addEventListener('click', function() { return self._onClick(idx); }, false);
	else if(window.attachEvent)
		d.attachEvent('onclick', function() { return self._onClick(idx); });


	li = document.createElement('li');
	li.appendChild(d);
	//li.innerHTML = vkDom.html(label);


	this.liElements[idx] = li;

	this.ulElement.appendChild(li);

	return idx;
}

kigoMenu.prototype.dump = function(hostDiv)
{
	if(!(hostDiv = vkDom.el(hostDiv)))
		throw 'Host element is NULL';

	if(!this.ulElement)
		throw 'Already dumped';
	
	vkDom.setText(hostDiv, '');
	hostDiv.appendChild(this.ulElement);
	this.ulElement = null;
}


kigoMenu.prototype._onClick = function(i)
{
	if(this.onClick(i))
		this.select(i);
	
	return false;
}


kigoMenu.prototype.select = function(i)
{
	this.unselect();

	if(i >= 0 && i < this.divElements.length)
	{
		//this.aElements[i].className = 'sel';
		this.liElements[i].className = 'sel';
		this.divElements[i].style.display = 'block';
		this.selected = i;
		this.onSelect(i);
	}
}

kigoMenu.prototype.unselect = function()
{
	/*
	if(this.selected != -1)
	{
		//this.aElements[this.selected].className = '';
		this.liElements[this.selected].className = '';
		this.divElements[this.selected].style.display = 'none';
		this.selected = -1;
	}
	*/

	// 05/01/2009 - Always unselect all entries - it doesnt cost much, and it fixes a problem with submenus being rewritten...

	for(var i = 0; i < this.divElements.length; i++)
	{
		this.liElements[i].className = '';
		this.divElements[i].style.display = 'none';
	}

	this.selected = -1;
}


kigoMenu.prototype.onClick = function(i)
{
	// Default: accept
	return true;
}


kigoMenu.prototype.onSelect = function(i)
{
	// Default: don't do anything
}



/* /js/kigoMenu2.js */ /* UTF8 COOKIE: éà */

/*

Javascript list-based kigo menu.
Improved version of kigoMenu (uses same GUI layout)

*/

function	kigoMenu2(type)
{
	this.container = kigoDom.create('div', { 'class' : type } ).append(this.list = kigoDom.create('ul'));
	this.tabs = [];
	this.currentTab = null;
}

// object
kigoMenu2.prototype.domNode = function()
{
	return this.container.domNode();
}

// idx
kigoMenu2.prototype.add = function(label)	// [, obj [, bool onEnter() [, bool onLeave() ] ] ]
{
	var	self = this;
	var	li, idx = this.tabs.length;

	this.list.append(
		li = kigoDom.create('li').append(
			kigoDom.create('div', null, null, { 'click' : function() { self.select(idx); return false; } } ).append(label)
		)
	);

	this.tabs.push({
		'obj'		:	arguments.length > 1 ? new kigoDom(arguments[1]) : null,
		'li'		:	li,
		'onEnter'	:	arguments.length > 2 && kigo.is_function(arguments[2]) ? arguments[2] : null,
		'onLeave'	:	arguments.length > 3 && kigo.is_function(arguments[3]) ? arguments[3] : null
	});

	if(this.tabs[idx].obj)
		this.tabs[idx].obj.domNode().style.display = 'none';

	return idx;
}

// bool
kigoMenu2.prototype.select = function(idx)
{
	if(idx < 0 || idx >= this.tabs.length)
		return false;

	if(this.currentTab != null)
	{
		if(this.currentTab == idx)
			return true;

		// If onLeave is defined, invoke it first
		if(
			this.tabs[this.currentTab].onLeave != null &&
			!this.tabs[this.currentTab].onLeave(idx)
		)
			return false;


		this.tabs[this.currentTab].li.domNode().className = '';

		// Okay, if there was an object, "undisplay" it
		if(this.tabs[this.currentTab].obj != null)
			this.tabs[this.currentTab].obj.domNode().style.display = 'none';
	}


	if(
		this.tabs[idx].onEnter != null &&
		!this.tabs[idx].onEnter(idx)
	)
		return false;

	this.tabs[idx].li.domNode().className = 'sel';

	if(this.tabs[idx].obj != null)
		this.tabs[idx].obj.domNode().style.display = 'block';
	
	this.currentTab = idx;

	return true;
}

/* /js/kigoTable.js */ /* UTF8 COOKIE: éà */

/*
Heavily based on NovelaTravel's ntTable & ntListTable
*/


function	kigoTable(containerDiv)
{
	this.containerDiv = containerDiv;
	this.reset();
}

kigoTable.prototype.reset = function()
{
	this.tattribs = '';
	this.caption = '';
	this.thead = '';
	this.tbody = '';
	this.tfoot = '';
	
	return true;
}

kigoTable.prototype.setTableAttributes = function(attribs)
{
	if(typeof(attribs) != 'object')
		throw 'Object expected';

	this.tattribs = this._buildAttr(attribs);
}




kigoTable.prototype.setCaption = function()
{
	this.setCaptionArray(arguments);
}
kigoTable.prototype.setCaptionArray = function(args)
{
	if(args.length == 1)
		attribs = '';
	else if(args.length == 2)
	{
		if(typeof(args[1]) != 'object')
			throw 'Second argument expected to be an object';

		attribs = this._buildAttr(args[1]);
	}
	else
		throw 'Bad arguments';

	this.caption = '<caption'+attribs+'>'+args[0]+'</caption>';
}



kigoTable.prototype.headerLine = function()
{
	this.headerLineArray(arguments);
}
kigoTable.prototype.headerLineArray = function(args)
{
	this.thead += this._line(args);
}


kigoTable.prototype.bodyLine = function()
{
	this.bodyLineArray(arguments);
}
kigoTable.prototype.bodyLineArray = function(args)
{
	this.tbody += this._line(args);
}


kigoTable.prototype.footerLine = function()
{
	this.footerLineArray(arguments);
}
kigoTable.prototype.footerLineArray = function(args)
{
	this.tfoot += this._line(args);
}


kigoTable.prototype._line = function(args)
{
	var i, attribs, cell, str;

	if(!args.length)
		throw 'Bad arguments';

	// Check for TR attributes

	if(typeof(args[0]) == 'object')
	{
		if(args.length < 2)
			throw 'Bad arguments';

		str = '<tr'+this._buildAttr(args[0])+'>';
		i = 1;
	}
	else
	{
		str = '<tr>';
		i = 0;
	}

	while(i < args.length)
	{
		if(typeof(args[i]) != 'string')
			throw 'Bad arguments';

		cell = args[i];

		// Check if options provided
		if(++i < args.length && typeof(args[i]) == 'object')
			str += '<td'+this._buildAttr(args[i++])+'>'+cell+'</td>';
		else
			str += '<td>'+cell+'</td>';
	}

	return str+'</tr>';
}



kigoTable.prototype.dump = function()
{
	var	container;

	if(!(container = vkDom.el(this.containerDiv)))
		throw 'Container not found';

	container.innerHTML = 
		'<table'+this.tattribs+'>'+
			this.caption + 
			(this.thead.length ? '<thead>'+this.thead+'</thead>' : '')+
			(this.tfoot.length ? '<tfoot>'+this.tfoot+'</tfoot>' : '')+
			(this.tbody.length ? '<tbody>'+this.tbody+'</tbody>' : '')+
		'</table>'
	;

	return this.reset();
}


kigoTable.prototype._encode = function(str)
{
	str = ''+str;
	str = str.replace(/&/g, '&amp;');
	str = str.replace(/\"/g, '&quot;');
	str = str.replace(/</g, '&lt;');
	return str.replace(/>/g, '&gt;');
}

kigoTable.prototype._buildAttr = function(obj)
{
	var	res = '';

	if(typeof(obj) != 'object')
		return '';

	for(var i in obj)
	{
		if(typeof(obj[i]) == 'string' || typeof(obj[i]) == 'number')
			res += (res.length ? ' ' : '')+i+'="'+this._encode(obj[i])+'"';
	}

	return res.length ? ' '+res : '';
}


/* /js/kigoToolTips.js */ /* UTF8 COOKIE: éà */

/*

OBSOLETED BY kigoToolip - DO NOT USE ANYMORE

*/

function	kigoToolTipsClass()
{
	this.current = -1;
	this.elements = [];
	this.tooltips = []; // text arrays;
	// used if no tool tip div exists with the respective id
	// so, consider id as the actual tooltip text
	this.timer = null;
	this.closeTime = 150;
	this.mouseDistance = 12;
	
	/*
	 * Stores current browser environement, page size, current scroll etc
	 * when triggering mass events.
	 * 
	 * See add function.
	 */ 	
	this.environement = null;
	
	this.defaultToolTipDiv = document.createElement('div');
	this.defaultToolTipDiv.id = 'defaultToolTipDiv'; 
	this.defaultToolTipDiv.className = 'tooltip';
}

kigoToolTipsClass.prototype.setCloseTime = function(time)
{
	this.closeTime = time;
}
/*
 * Mouse distance in this case reffers to the offset in relation to the current cursor position
 */
kigoToolTipsClass.prototype.setMouseDistance = function(dist)
{
	this.mouseDistance = dist;
}

/*
 * Add a tooltip text to the storage without enabling the target element to display the tooltip.
 * Usefull when generating html from javascript in memory.
 */
kigoToolTipsClass.prototype.silentAdd = function(id) {
	var	idx = this.elements.length;
	
	this.elements[idx] = {			
			'id'			:	id
		};
}

// void
kigoToolTipsClass.prototype.add = function(overElement, id)
{
	var	idx = this.elements.length;
	
	if(!(overElement = vkDom.el(overElement))) {		
		return;
	}

	this.elements[idx] = {
		'element'		:	overElement,
		'id'			:	id
	};

	if (!vkDom.el(id)) {
		this.tooltips[id] = id;
		if (!vkDom.el('defaultToolTipDiv'))
			document.getElementById('global').appendChild(this.defaultToolTipDiv);
		var tip = vkDom.el('defaultToolTipDiv');
	} else {
		var tip = vkDom.el(id);	
	}
	
	// Intercept mouse over/out on the element...
	var setup = function() {

		var mouseMoveHandler = function(ev) { kigoToolTips.__mouseMove(ev, idx); }
		var mouseOutHandler = function() { kigoToolTips.__mouseOut(idx); }
		// Adding event enablers/disablers directly to the tolltip in order to deactivate the current overelement event handlers
		// Due to the fact that we continuously reposition on the move event too many events are triggered in paralel
		// Thus we can not have an accurate calculation of window size, scroll etc.
		tip.enableEvents = function() {		
			if(window.addEventListener)
			{
				overElement.addEventListener('mousemove', mouseMoveHandler, false);
				overElement.addEventListener('mouseout', mouseOutHandler, false);
			}
			else if(window.attachEvent)
			{
				overElement.attachEvent('onmousemove', mouseMoveHandler);
				overElement.attachEvent('onmouseout', mouseOutHandler);
			}
		}
	
		tip.disableEvents = function() {			
			if(window.addEventListener)
			{
				overElement.removeEventListener('mousemove', mouseMoveHandler, false);
				overElement.removeEventListener('mouseout', mouseOutHandler, false);
			}
			else if(window.attachEvent)
			{
				overElement.detachEvent('onmousemove', mouseMoveHandler);
				overElement.detachEvent('onmouseout', mouseOutHandler);
			}
		}
	
	}
	setup();
	tip.enableEvents();
	
	return overElement;
}
// Automatic tooltip collecting in the dom

// void
kigoToolTipsClass.prototype.autoSetup = function(node)
{
	var	i, toolTip;

	if(!node.hasChildNodes())
		return;

	// Since the tree is modified during the process, we need to maintain and work on a copy

	for(i = 0; i < node.childNodes.length; i++)
	{	
		if(node.childNodes[i].nodeType == 1)
		{
			if((toolTip = node.childNodes[i].getAttribute('tooltip')) != null)
				this.add(node.childNodes[i], toolTip);
			else
				this.autoSetup(node.childNodes[i]);
		}
	}
}

kigoToolTipsClass.prototype._cancelTimer = function()
{
	if(this.timer != null)
	{
		clearTimeout(this.timer);
		this.timer = null;
	}
}

kigoToolTipsClass.prototype._onTimer = function(idx)
{
	if(this.current == idx)
		this._close();
}

kigoToolTipsClass.prototype._close = function()
{
	if(this.current != -1)
	{
		var	tip = vkDom.el(this.elements[this.current].id);

		if (!tip) {
			tip = this.defaultToolTipDiv;			
		}
		
		if(tip)
			tip.style.display = 'none';
		
		this.current = -1;
		
		this.environement = null;
	}
}

kigoToolTipsClass.prototype._open = function(ev, idx)
{

	ev.cancelBubble = true;
	ev.returnValue = false;	
	if (navigator.appVersion.indexOf("MSIE")!=-1){	
		ev.keyCode = 0;
	}
	
/*
ev.screenX
ev.screenY
ev.clientX
ev.clientY
*/

	var	tip = vkDom.el(this.elements[idx].id), mx = ev.clientX, my = ev.clientY, tw, th, tx, ty;

	if (!tip) {
		tip = this.defaultToolTipDiv;
		vkDom.setText(tip, this.tooltips[this.elements[idx].id]);
	}
	
	if(tip)
	{
		if (this.tipDimensions==null) {		
			// Gather the current tip element and browser information and store them for the next
			// mouse move event.			
			tip.disableEvents();
			// Disable events so there's only the first event that does the computation, and it's 
			// current information is not affected by subsequent events.
			tip.style.display = 'block';	
			tw = tip.clientWidth;
			th = tip.clientHeight;
			tip.style.display = 'none';
			
			this.environement = new Object();
			
			var tipDimensions = {x: tw, y: th};			
			this.environement.tipDimensions = tipDimensions;			
			
			var event = ev;
			var win = window;
			var doc = win.document.body;						
						
			// The actual viewport size					
			var viewWidth = 0, viewHeight = 0;
			  if( typeof( window.innerWidth ) == 'number' ) {
			    //Non-IE
			    viewWidth = window.innerWidth;
			    viewHeight = window.innerHeight;
			  } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
			    //IE 6+ in 'standards compliant mode'
			    viewWidth = document.documentElement.clientWidth;
			    viewHeight = document.documentElement.clientHeight;
			  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
			    //IE 4 compatible
			    viewWidth = document.body.clientWidth;
			    viewHeight = document.body.clientHeight;
			  }
			  
			var viewSize =  {x: viewWidth, y: viewHeight};					
			this.environement.viewSize = viewSize;
			
			// The scroll offset
			var scrOfX = 0, scrOfY = 0;
			  if( typeof( window.pageYOffset ) == 'number' ) {
				    //Netscape compliant
				    scrOfY = window.pageYOffset;
				    scrOfX = window.pageXOffset;
			  } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
			    //DOM compliant
			    scrOfY = document.body.scrollTop;
			    scrOfX = document.body.scrollLeft;
			  } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
			    //IE6 standards compliant mode
			    scrOfY = document.documentElement.scrollTop;
			    scrOfX = document.documentElement.scrollLeft;
			  }
			
			var scroll = {x: scrOfX, y: scrOfY};	
							
			this.environement.scroll = scroll;
			
			var page = {
					x: event.pageX || event.clientX + scroll.x,
					y: event.pageY || event.clientY + scroll.y
				};
			
			this.environement.page = page;
			
			// enable events for correct movement positioning
			tip.enableEvents();
		} else {			
			// next event has it's settings set
			var tipDimensions = this.environement.tipDimensions;
			tw = this.tipDimensions.x;
			th = this.tipDimensions.y;
			var page = this.environement.page;			
			var presto = this.environement.presto;
			var webkit = this.environement.webkit;
			var viewSize = this.environement.viewSize;
			var scroll = this.environement.scroll; 
		}
		tip.style.display = 'none';

		switch(tip.getAttribute('pos'))
		{
			case 'w':
				tx = - tw - this.mouseDistance;
				ty = - (th >> 1);
				break;

			case 'e':
				tx = this.mouseDistance;
				ty = - (th >> 1);
				break;

			case 'n':
				tx = - (tw >> 1);
				ty = - th - this.mouseDistance;
				break;

			case 's':
				tx = - (tw >> 1);
				ty = this.mouseDistance;
				break;

			case 'nw':
				tx = - tw - this.mouseDistance;
				ty = - th - this.mouseDistance;
				break;

			case 'se':
				tx = this.mouseDistance;
				ty = this.mouseDistance;
				break;
				
			case 'sw':
				tx = - tw - this.mouseDistance;
				ty = this.mouseDistance;
				break;

			case 'ne':	// Default
			default:
				tx = this.mouseDistance;
				ty = - th - this.mouseDistance;

		}
											
		var props = {x: 'left', y: 'top'};
		var propsID = ['x','y'];
		var obj = {};
		// the position to place the tip in relation with the mouse according to display direction nw, se ... etc
		var offset = {x: tx, y: ty};
		// mouse as provided by the event
		var mouse = {x: mx, y: my};
		// page contains: event.pageX, event.pageY
		// adjust for left and top overflow
		switch(tip.getAttribute('pos'))
		{	
		case 'w':
		case 'n':	
		case 'nw':			
			for (var pos in propsID){
				// X | Y
				var coordinateIdent = propsID[pos];
				
				if(typeof props[coordinateIdent] != 'function') {
					
					obj[props[coordinateIdent]] = page[coordinateIdent] + offset[coordinateIdent];
					// compute the left most position and check that it does not go out of the current viewport
					if ((obj[props[coordinateIdent]] - tipDimensions[coordinateIdent] - scroll[coordinateIdent]) < - mouse[coordinateIdent]) 
						{
						//if it does fix it by repositioning on the oposite side nw -> se 
						// (1.3*this.mouseDistance) to prevent accidently moving the mouse over the tip since the mouse distance is added earlier to the sum 
						obj[props[coordinateIdent]] = page[coordinateIdent] + offset[coordinateIdent] + tipDimensions[coordinateIdent] + (1.3*this.mouseDistance);
						}
				}
			}
		break;
		default:
			break;
		}

		// adjust for bottom and right overflow
		if (tip.getAttribute('pos')!='w' && tip.getAttribute('pos')!='n' && tip.getAttribute('pos')!='nw') {
			switch(tip.getAttribute('pos'))
			{
				case 'e':
				case 's':
				case 'se':				
				case 'sw':
				case 'ne':	
				default:
					for (var pos in propsID){
						// X | Y
						var coordinateIdent = propsID[pos];
						if(typeof props[coordinateIdent] != 'function') {
							obj[props[coordinateIdent]] = page[coordinateIdent] + offset[coordinateIdent];
							// compute the bottom right position and check that it does not go out of the current viewport
							if ((obj[props[coordinateIdent]] + tipDimensions[coordinateIdent] - scroll[coordinateIdent]) > viewSize[coordinateIdent]) {
								//if it does fix it by repositioning on the oposite side se -> nw
								obj[props[coordinateIdent]] = page[coordinateIdent] - offset[coordinateIdent] - tipDimensions[coordinateIdent];
							}
					}
				}
				break;
			}		
		}
		
		
		// if the object is still out of left.top bounds then set it to 0 ...
	
		if (obj.left < scroll.x) obj.left = scroll.x;
		if (obj.top < scroll.y) obj.top = scroll.y;
		
		tip.style.left = obj.left+'px';
		tip.style.top = obj.top+'px';
		
		tip.style.display = 'block';
		
		

	}

	this.current = idx;

}

kigoToolTipsClass.prototype.__mouseMove = function(ev, idx)
{
	// Close if some other is currently open
	if(this.current != idx)
		this._close();

	// Cancel timer if any
	this._cancelTimer();

	// Open the new tip
	this._open(ev, idx);
}

kigoToolTipsClass.prototype.__mouseOut = function(idx)
{
	// Cancel timer if any
	this._cancelTimer();

	// Set new timeout for closing the tip
	this.timer = setTimeout(function() { kigoToolTips._onTimer(idx) }, this.closeTime);
}

var	kigoToolTips = new kigoToolTipsClass();
/* /js/kigoTooltip.js */ /* UTF8-Côôkie */

/*

OBSOLETES kigoToolTips

*/


/*

04/05/2010		Release #0



Type	Returns			Method											Note


static	kigoDom			icon(element [, orientation='ne'])

static	kigoDom			register(over, content [, orientation='ne'])	'content' may be either a kigoDom node, an Array of kigoDom nodes, or a method that returns a kigoDom node or an Array of kigoDom nodes.
																		Note that a Strings are handled the same way as kigoDom nodes.
																		If 'content' is a method, it will receive the kigoDom node of the registered 'over' element as first argument.


static	kigoDom			unregister(over)


static	void			hide()											Forces hiding the current tooltip. Used as a workaround to browser bugs.


static	void			autoSetup(node)									To be used once kigoToolTips is completely removed from the application!

*/


var	kigoTooltip = {

	'mouseDistance'	:	12,

	'handlers'		:	{},
	'nextHandler'	:	0,

	'tooltip'		:	null,

	'icon'			:	function(content)
	{
		return this.register(kigoDom.create('img', { 'src' : '/img/tooltip.png' }), content, arguments.length > 1 ? arguments[1] : 'ne');
	},


	'register'		:	function(over, content)
	{
		var	orientation = arguments.length > 2 ? arguments[2] : 'ne';
		var	self = this;

		// Don't catch!
		over = new kigoDom(over);

		/*
		if(!kigo.is_function(handler))
		{
			// Should be either a dom node or a kigoDom instance
			try
			{
				var	tip = (new kigoDom(handler)).clone();

				// Backward compatibility > it it had an id, remove it, so that we don't end up with duplicate id's in the document tree
				tip.domNode().removeAttribute('id');
					
				handler = function()
				{
					return tip;
				}
			}
			catch(e)
			{
				return null;
			}
		}
		*/

		//over.setAttribute('kigo_tooltip', this.handlers.push(handler) - 1);

		var	onOver, onMove, onOut;
		var	width = null, height = null;

		onOver = function(ev)
		{
			if(!self.tooltip)
				kigoDom.getBody().append(self.tooltip = kigoDom.create('div', { 'class' : 'tooltip' }, { 'display' : 'none', 'visibility' : 'hidden' }));
				//(new kigoDom('container')).append(self.tooltip = kigoDom.create('div', { 'class' : 'tooltip' }, { 'display' : 'none', 'visibility' : 'hidden' }));

			// Fill in the tooltip, set it invisible, and compute its dimensions
			self.tooltip.domNode().style.visibility = 'hidden';
			self.tooltip.domNode().style.left = '-10000px';
			self.tooltip.domNode().style.top = '-10000px';

			// New content
			self.tooltip.empty().append(kigo.is_function(content) ? content(over) : content);
			self.tooltip.domNode().style.display = 'block';

			width = self.tooltip.domNode().offsetWidth;
			height = self.tooltip.domNode().offsetHeight;

			// Trigger an onMove() so that the positionning is done in a single place of code
			onMove(ev);
			self.tooltip.domNode().style.visibility = 'visible';
		}


		onMove = function(ev)
		{
			var	mx = ev.clientX + Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);
			var	my = ev.clientY + Math.max(document.documentElement.scrollTop, document.body.scrollTop);
			var	tx, ty;

			if(width == null || height == null)	// Means the previous onOver() event processing hasn't finished yet
				return;

			// ViewPort
			var	vpTop, vpBottom, vpLeft, vpRight;

			vpTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
			vpBottom = vpTop + document.documentElement.clientHeight;

			vpLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);
			vpRight = vpLeft + document.documentElement.clientWidth;

			// Determine the default position, according to the requested orientation
			computeDefault();

			// (Try to) prevent the tooltip from leaving the visible area
			// Always prefer wrapping clipping right/bottom rather than left/top

			if(tx < vpLeft)
				tx = vpLeft;
			else if(tx + width > vpRight)
				tx = Math.max(vpLeft, vpRight - width);

			if(ty < vpTop)
				ty = vpTop;
			else if(ty + height > vpBottom)
				ty = Math.max(vpTop, vpBottom - height);


			// Yet another step : make sure the tooltip won't be positioned under the mouse cursor, because that would make the element loose the mouse focus

			if(
				mx >= tx && mx <= (tx + width) &&
				my >= ty && my <= (ty + height)
			)
			{

				/* Solution A 

				var	onTooltipOut;
				
				onTooltipOut = function()
				{
					kigoDebug.text('onTooltipOut('+temp+')');
					onOut();


					if(window.addEventListener)
					{
						self.tooltip.domNode().removeEventListener('mouseout', onTooltipOut, false);
						over.domNode().addEventListener('mouseout', onOut, false);
					}
					else if(window.attachEvent)
					{
						self.tooltip.domNode().detachEvent('onmouseout', onTooltipOut);
						over.domNode().attachEvent('onmouseout', onOut);
					}

				}

				if(window.addEventListener)
				{
					over.domNode().removeEventListener('mouseout', onOut, false);
					self.tooltip.domNode().addEventListener('mouseout', onTooltipOut, false);
				}
				else if(window.attachEvent)
				{
					over.domNode().detachEvent('onmouseout', onOut);
					self.tooltip.domNode().attachEvent('onmouseout', onTooltipOut);
				}

				//*/


				//* Solution B - Try moving around in order to avoid this situation, even if we end by rendering the tooltip partly out of screen

				// Start everything again, try using the opposite position

				computeDefault();

				if(tx + width > vpRight)
				{
					// tx = vpRight - width;
					
					switch(orientation)
					{
						case 'e':
						case 'ne':
						case 'se':
							tx = mx - width - self.mouseDistance;
							break;

						default:
							tx = vpRight - width;
					}
				}

				if(tx < vpLeft)
				{
					switch(orientation)
					{
						case 'w':
						case 'nw':
						case 'sw':
							tx = mx + self.mouseDistance;
							break;

						default:
							tx = vpLeft;
					}
				}

				if(ty + height > vpBottom)
				{
					switch(orientation)
					{
						case 's':
						case 'sw':
						case 'se':
							ty = my - height - self.mouseDistance;
							break;

						default:
							ty = vpBottom - height;
					}
				}

				if(ty < vpTop)
				{
					switch(orientation)
					{
						case 'n':
						case 'nw':
						case 'ne':
							ty = my + self.mouseDistance;
							break;

						default:
							ty = vpTop;
					}
				}


				// And, finally, if this fails too, fall back to the original position without trying to correct anything!

				if(
					mx >= tx && mx <= (tx + width) &&
					my >= ty && my <= (ty + height)
				)
					computeDefault();

				//*/

			}

			self.tooltip.domNode().style.left = tx+'px';
			self.tooltip.domNode().style.top = ty+'px';

		
			// Closure
			function	computeDefault()
			{
				switch(orientation)
				{
					case 'w':	tx = mx - width - self.mouseDistance;		ty = my - (height >> 1);						break;
					case 'e':	tx = mx + self.mouseDistance;				ty = my - (height >> 1);						break;
					case 'n':	tx = mx - (width >> 1);						ty = my - height - self.mouseDistance;			break;
					case 's':	tx = mx - (width >> 1);						ty = my + self.mouseDistance;					break;
					case 'nw':	tx = mx - width - self.mouseDistance;		ty = my - height - self.mouseDistance;			break;
					case 'se':	tx = mx + self.mouseDistance;				ty = my + self.mouseDistance;					break;
					case 'sw':	tx = mx - width - self.mouseDistance;		ty = my + self.mouseDistance;					break;
					case 'ne':
					default:	tx = mx + self.mouseDistance;				ty = my - height - self.mouseDistance;
				}
			}
		}

		onOut = function(ev)
		{
			//kigoDebug.text('onOut('+temp+')');
			self.tooltip.domNode().style.display = 'none';
			self.tooltip.empty();
			width = null;
			height = null;
		}

		// Remember the handlers, so that me may cleanly unregister
		// (though we probably won't unregister() very often)

		over.domNode().setAttribute('kigo_tooltip', this.nextHandler);
		this.handlers[this.nextHandler++] = {
			'onOver'	:	onOver,
			'onMove'	:	onMove,
			'onOut'		:	onOut
		};

		if(window.addEventListener)
		{
			over.domNode().addEventListener('mouseover', onOver, false);
			over.domNode().addEventListener('mousemove', onMove, false);
			over.domNode().addEventListener('mouseout', onOut, false);
		}
		else if(window.attachEvent)
		{
			over.domNode().attachEvent('onmouseover', onOver);
			over.domNode().attachEvent('onmousemove', onMove);
			over.domNode().attachEvent('onmouseout', onOut);
		}

		return over;
	},


	'unregister'	:	function(over)
	{
		var	idx = (over = new kigoDom(over)).domNode().getAttribute('kigo_tooltip');

		if(kigo.is_object(this.handlers[idx]))
		{
			if(window.addEventListener)
			{
				over.domNode().removeEventListener('mouseover', this.handlers[idx].onOver, false);
				over.domNode().removeEventListener('mousemove', this.handlers[idx].onMove, false);
				over.domNode().removeEventListener('mouseout', this.handlers[idx].onOut, false);
			}
			else if(window.attachEvent)
			{
				over.domNode().detachEvent('onmouseover', this.handlers[idx].onOver);
				over.domNode().detachEvent('onmousemove', this.handlers[idx].onMove);
				over.domNode().detachEvent('onmouseout', this.handlers[idx].onOut);
			}
		
			delete this.handlers[idx];
		}

		return over;
	},

	'hide'		:	function()
	{
		if(this.tooltip)
		{
			this.tooltip.domNode().style.display = 'none';
			this.tooltip.empty();
		}
	},


	'autoSetup'		:	function(node)
	{
		
	}
};

/* /js/kigoInputCharFilter.js */ /* UTF8-Côôkie */

/*

This is a best-effort input character filter.
"Best-effort" means that in some browsers and on some OSes, it does not filter anything.



12/07/2010		Release #0



Type	Returns			Method											Note


static	kigoDom			register(over, filter)							"filter" may be either a user-defined string holding the allowed characters, 
																		or a builtin string.

static	kigoDom			unregister(over)


--- Builtin filters ---

		Filter									Contains

		kigoInputCharFilter.builtin.nothing		Absolutely nothing
		kigoInputCharFilter.builtin.digits		Digits-only



TODO:

static	void			autoSetup(node)									TODO


SOURCES & HELP:

http://www.quirksmode.org/js/keys.html
http://unixpapa.com/js/key.html

*/


var	kigoInputCharFilter = {

	'handlers'		:	{},
	'nextHandler'	:	0,

	'register'		:	function(over, filter)
	{
		var	self = this;

		// Don't catch!
		over = new kigoDom(over);

		// Until this have been tested on other OSes, simply ignore!
		if(!vkDom.hasClass(document.body, 'ua_os-win'))
			return over;

		if(!kigo.is_string(filter))
			throw 'kigoInputCharFilter::kigoInputCharFilter(): Bad filter';

		var onKeyPress = function(ev)
		{
			var	code;

			if(typeof(ev['charCode']) != 'undefined')
				code = ev.charCode;
			else if(typeof(ev['keyCode']) != 'undefined')
				code = ev.keyCode;
			else	// Neither charcode nor keycode, cannot filter
				return true;

			if(
				!code || 
				code == 8 ||	// some browsers consider backspace as a character...
				(typeof(ev.which) != 'undefined' && !ev.which) ||	// http://unixpapa.com/js/key.html
				kigo.strpos(filter, String.fromCharCode(code)) != null
			)
				return true;
							
			// Stop bubble/propagation

			if(ev.stopPropagation)
				ev.stopPropagation();

			if(typeof(ev.cancelBubble) != 'undefined')
				ev.cancelBubble = true;

			// Prevent the default action

			if(ev.preventDefault)
				ev.preventDefault();

			if(typeof(ev.returnValue) != 'undefined')
				ev.returnValue = false;

			return false;
		}

		over.domNode().setAttribute('kigo_inputcharfilter', this.nextHandler);
		this.handlers[this.nextHandler++] = onKeyPress;

		if(window.addEventListener)
			over.domNode().addEventListener('keypress', onKeyPress, false);
		else if(window.attachEvent)
			over.domNode().attachEvent('onkeypress', onKeyPress);

		return over;
	},


	'unregister'	:	function(over)
	{
		var	idx = (over = new kigoDom(over)).domNode().getAttribute('kigo_inputcharfilter');

		if(kigo.is_object(this.handlers[idx]))
		{
			if(window.addEventListener)
				over.domNode().removeEventListener('keypress', this.handlers[idx], false);
			else if(window.attachEvent)
				over.domNode().detachEvent('onkeypress', this.handlers[idx]);

			delete this.handlers[idx];
		}

		return over;
	},

	'builtin'	:	{
		'none'		:	'',
		'digits'	:	'0123456789'
	}
};

/* /js/kigoAjaxRequest.js */ /* UTF8 COOKIE: éà */

/*
	OBSOLETED BY kigoAjaxRequest2
	All the new projects must use kigoAjaxRequest2

*/

/*

07/11/2008	This class allows for shorter and more uniform way of coding for kigo ajax requests
10/11/2009	Increased default timeout from 15 to 20 seconds

*/


//alert('kigoAjaxRequest.js a été modifié, à retester');


function	kigoAjaxRequest()	// debug=false, timeout=20000
{
	this.debug = (arguments.length && arguments[0]) ? arguments[0] : null;

	if(arguments.length > 1)
		this.timeout = parseInt(arguments[1]);
	else
		this.timeout = 20000;	// Default

	// Use a single ajax instance for the whole lifetime
	this.jsonAjax = null;
	this.xmlAjax = null;
}


kigoAjaxRequest.prototype.get = function(url)
{
	this._prepare(true);

	this.jsonAjax.get(
		url,
		this.timeout
	);
}

kigoAjaxRequest.prototype.getXml = function(url)
{
	this._prepare(false);
	this.xmlAjax.get(
		url,
		this.timeout
	);
}


kigoAjaxRequest.prototype.post = function(url, object)
{
	this._prepare(true);

	this.jsonAjax.post(
		url,
		object,
		this.timeout
	);
}







kigoAjaxRequest.prototype.error = function()
{
	var	self = this;
	errorRetry(function(yesno) { self.onErrorRetry(yesno); });
}


kigoAjaxRequest.prototype._timeout = function()
{
	var	self = this;
	timeoutRetry(function(yesno) { self.onTimeoutRetry(yesno); });
}

kigoAjaxRequest.prototype.onLoad = function()
{
	/* NOOP */
}

kigoAjaxRequest.prototype.onErrorRetry = function(yesno)
{
	// Default
	goLogin();
}

kigoAjaxRequest.prototype.onTimeoutRetry = function(yesno)
{
	// Default
	goLogin();
}


/**************************************/
/* PRIVATE */



kigoAjaxRequest.prototype._prepare = function(json)
{
	var	ajax = null;

	if(json && this.jsonAjax == null)
		ajax = this.jsonAjax = new vkJSONRemote();
	else if(!json && this.xmlAjax == null)
		ajax = this.xmlAjax = new vkXMLRemote();

	// One time setup
	if(ajax)
	{
		var	self = this;
		
		if(this.debug)
			ajax.enableDebug(this.debug);

		ajax.onLoad = function(status, data)
		{
			vkPopup.closePopup();

			if(status == 403)
				authError();
			else if(status == 504)
				self.timeout();
			else if(status != 200 || data == null)
				self.error();
			else
			{
				ajax.free();
				self.onLoad(data);
			}
		}

		ajax.onTimeout = function()
		{
			self._timeout();
		}
	}

	loading();

	if(json)
		this.jsonAjax.free();
	else
		this.xmlAjax.free();
}

/* /js/kigoAjaxRequest2.js */ /* UTF8 COOKIE: éà */

/*

04/05/2009	An enchanced version of kigoAjaxRequest
10/11/2009	Increased default timeout from 15 to 20 seconds
26/07/2010	abort() method

*/


function	kigoAjaxRequest2
(
	loadCallback
	// [, url ]
	// [, objectCallback ]
	// [, timeout ]
)
{
	this.loadCallback = (typeof(loadCallback) == 'function' ? loadCallback : null);
	this.defaultUrl = (arguments.length > 1 && typeof(arguments[1]) == 'string') ? arguments[1] : null;
	this.objectCallback = (arguments.length > 2 && typeof(arguments[2]) == 'function') ? arguments[2] : null;
	this.timeout = (arguments.length > 3 && typeof(arguments[3]) == 'number') ? parseInt(arguments[3]) : 20000;

	this.url = null;
	this.debug = null;
	this.postObject = null;
	this.jsonAjax = null;
	this.isSilent = false;
}

kigoAjaxRequest2.prototype.run = function()	// [url] [postObject]
{
	// If specified, url overrides constructor url, and postObject overrides the result from objectCallback()

	if(arguments.length == 2 && typeof(arguments[0]) == 'string' && typeof(arguments[1]) == 'object' /* including null */)
	{
		this.url = arguments[0];
		this.postObject = arguments[1];
	}
	else if(arguments.length == 1 && typeof(arguments[0]) == 'string')
	{
		this.url = arguments[0];
		this.postObject = (this.objectCallback ? this.objectCallback() : null);
	}
	else if(arguments.length == 1 && typeof(arguments[0]) == 'object' /* including null */)
	{
		this.url = this.defaultUrl;
		this.postObject = arguments[0];
	}
	else if(!arguments.length)
	{
		this.url = this.defaultUrl;
		this.postObject = (this.objectCallback ? this.objectCallback() : null);
	}
	else
		throw 'Bad usage';

	this.rerun();
}

kigoAjaxRequest2.prototype.rerun = function()
{
	if(!this.isSilent)
		loading();

	this.getAjax().post(
		this.url,
		this.postObject,
		this.timeout
	);
}

kigoAjaxRequest2.prototype.silent = function(tf)
{
	this.isSilent = tf ? true : false;
}


kigoAjaxRequest2.prototype.enableDebug = function(debug)
{
	this.getAjax().enableDebug(debug);
	return this;	// 22/07/2010 - though this is undocumented for now
}

kigoAjaxRequest2.prototype.abort = function()
{
	if(this.jsonAjax)
		this.jsonAjax.free();

	/* 28/07/2010 - Why deleting? moreover, due to this problem, enableDebug setting is forgotten 
	delete this.jsonAjax;
	this.jsonAjax = null;
	*/
}



/**************************************/
/* DEFAULTS */


kigoAjaxRequest2.prototype.onUnauthenticated = function()
{
	authError();
}

kigoAjaxRequest2.prototype.onTimeout = function()
{
	var	self = this;

	if(!this.isSilent)
	{
		timeoutRetry(
			function(yesno)
			{
				if(yesno)
					self.rerun();
				else
					errorFatal();
			}
		);
	}
}

kigoAjaxRequest2.prototype.onError = function()
{
	var	self = this;

	if(!this.isSilent)
	{
		errorRetry(
			function(yesno)
			{
				if(yesno)
					self.rerun();
				else
					errorFatal();
			}
		);
	}
}

kigoAjaxRequest2.prototype.onLoad = function(data)
{
	if(this.loadCallback != null)
	{
		if(!this.loadCallback(data))
			this.onError();
	}
	else
		this.onError();
}



/**************************************/
/* PRIVATE */



kigoAjaxRequest2.prototype.getAjax = function()
{
	if(this.jsonAjax == null)
	{
		var	self = this;
	
		this.jsonAjax = new vkJSONRemote();
		
		this.jsonAjax.onLoad = function(status, data)
		{
			if(!self.isSilent)
				vkPopup.closePopup();

			if(status == 403)
				self.onUnauthenticated();
			else if(status == 504)
				self.onTimeout();
			else if(status != 200 || data == null)
				self.onError();
			else
			{
				self.jsonAjax.free();
				self.onLoad(data);
			}
		}

		this.jsonAjax.onTimeout = function()
		{
			self.onTimeout();
		}
	}

	this.jsonAjax.free();

	return this.jsonAjax;
}

/* /js/kigoForm.js */ /* UTF8 COOKIE: éà */

/*
	OBSOLETED BY kigoForm2
	All the new projects must use kigoForm2
*/

/************************************************************************************************************/
/* kigoForm */

function	kigoForm()
{
	this.elements = [];
	this.hash = [];
	this.formMonitor = new vkFormMonitor();

	this.monitorCallback = null;
	this.monitorPeriod = 500;
}


kigoForm.prototype.MANDATORY = 1;
kigoForm.prototype.MONITOR = 2;
kigoForm.prototype.READ = 4;
kigoForm.prototype.WRITE = 8;
kigoForm.prototype.READWRITE = kigoForm.prototype.READ | kigoForm.prototype.WRITE;
kigoForm.prototype.PLEASESELECT = 16;

/*
kigoForm.prototype.define = function(element)
{
	var	idx, object, flags;

	if(typeof(element) != 'string' || arguments.length > 2 || this._idx(element) != null)
		throw 'Bad usage';

	if(!(object = vkDom.el(element)))
		return;

	this.elements[idx = this.elements.length] = {
		'id'			:	element,
		'object'		:	object,
		'flags'			:	flags = (arguments.length == 2) ? arguments[1] : 0,
		'defaultFlags'	:	flags
	};

	this.hash[element] = idx;
}
*/


/*

define(element, flags)
define(element, formName, flags)

*/

kigoForm.prototype.define = function(element)
{
	var	idx, object, formName, flags;

	if(typeof(element) != 'string' || arguments.length < 2 || arguments.length > 3 || this._idx(element) != null)
		throw 'Bad usage';

	if(arguments.length == 2)
	{
		formName = element;
		flags = arguments[1];
	}
	else
	{
		formName = arguments[1];
		flags = arguments[2];
	}

	if(!(object = vkDom.el(formName)))
		return;

	this.elements[idx = this.elements.length] = {
		'id'			:	element,
		'object'		:	object,
		'flags'			:	flags,
		'defaultFlags'	:	flags
	};

	this.hash[element] = idx;
}


kigoForm.prototype.resetFlags = function()
{
	// Reset flags for all element to their original values, specified in 'add'
	for(var i = 0; i < this.elements.length; i++)
		this.elements[i].flags = this.elements[i].defaultFlags;
}

kigoForm.prototype.setFlags = function(element, flags)
{
	if(this._idx(element) != null)
		this.elements[this._idx(element)].flags = flags;
}

kigoForm.prototype.addFlags = function(element, flags)
{
	if(this._idx(element) != null)
		this.elements[this._idx(element)].flags |= flags;
}

kigoForm.prototype.removeFlags = function(element, flags)
{
	if(this._idx(element) != null)
		this.elements[this._idx(element)].flags ^= flags;
}

kigoForm.prototype.hasFlags = function(element, flags)
{
	if(this._idx(element) != null)
		return (this.elements[this._idx(element)].flags & flags) == flags;
	else
		return false;
}



/*******************************************/
/* Reading */

kigoForm.prototype.read = function(data)
{
	var	i, obj, tmp;

	for(i = 0; i < this.elements.length; i++)
	{
		if(this.elements[i].flags & kigoForm.prototype.READ)
		{
			obj = this.elements[i].object;

			switch(obj.tagName)
			{
				case 'INPUT':

					switch(obj.getAttribute('type').toUpperCase())
					{
						case 'TEXT':
						case 'PASSWORD':
							obj.value = typeof(data[this.elements[i].id]) == 'string' ? data[this.elements[i].id] : '';
							break;

						case 'CHECKBOX':
							obj.checked = (data[this.elements[i].id] == 1 ? true : false);
							break;
					}
					break;

				case 'TEXTAREA':
					obj.value = typeof(data[this.elements[i].id]) == 'string' ? data[this.elements[i].id] : '';
					break;

				case 'SELECT':
					/*
					tmp = new kigoSelect(obj);
					tmp.setValue(data[this.elements[i].id]);
					*/

					tmp = new kigoSelect(obj);
					tmp.reset();
					tmp.addOptionsFromArray(data['LIST:'+this.elements[i].id]);
					if(!tmp.setValue(data[this.elements[i].id]) && (this.elements[i].flags & kigoForm.prototype.PLEASESELECT))
						tmp.setIndex(tmp.insertOption(0, '-- Please select --'));

					break;

			}
		}
	}
}


/*******************************************/
/* Writing */

kigoForm.prototype.write = function()
{
	var	i, o, id, obj, tmp;

	if(
		arguments.length == 1 && 
		arguments[0] != null && 
		typeof(arguments[0]) == 'object'
	)
		o = arguments[0];
	else if(arguments.length)
		throw 'Bad usage';
	else
		o = {};

	for(i = 0; i < this.elements.length; i++)
	{
		if(this.elements[i].flags & kigoForm.prototype.WRITE)
		{
			id = this.elements[i].id;
			obj = this.elements[i].object;

			switch(obj.tagName)
			{
				case 'INPUT':

					switch(obj.getAttribute('type').toUpperCase())
					{
						case 'TEXT':
						case 'PASSWORD':
							o[id] = obj.value;
							break;

						case 'CHECKBOX':
							o[id] = obj.checked;
							break;
					}
					break;

				case 'TEXTAREA':
					o[id] = obj.value;
					break;

				case 'SELECT':
					tmp = new kigoSelect(obj);
					o[id] = tmp.getValue();
					break;

			}
		}
	}

	return o;
}




/*******************************************/
/* Monitoring */


kigoForm.prototype.resetMonitor = function()
{
	this.formMonitor.reset();
}

kigoForm.prototype.monitor = function()
{
	var	i, added = false;

	if(this.monitorCallback == null)
		return;

	for(i = 0; i < this.elements.length; i++)
	{
		if(this.elements[i].flags & kigoForm.prototype.MONITOR)
			this.formMonitor.addElement(this.elements[i].id);
	}

	this.formMonitor.monitor(this.monitorCallback, this.monitorPeriod);
}

/*******************************************/
/* Validating */


kigoForm.prototype.resetMissing = function()
{
	// All fields are reset, even those that are (currently) not monitored
	for(var i = 0; i < this.elements.length; i++)
		resetMissingFields( [this.elements[i].id] );
}



kigoForm.prototype.getMissing = function()
{
	var	i, obj, missingField = null;

	for(i = 0; i < this.elements.length; i++)
	{
		if(
			this.elements[i].flags & kigoForm.prototype.MANDATORY &&
			this._isMissing(this.elements[i].object)
		)
			missingField = setMissingField(this.elements[i].id, missingField);
	}

	return missingField;
}

kigoForm.prototype.checkMissing = function()
{
	var	missing;

	if(missing = this.getMissing())
	{
		vkPopup.message(
			'Mandatory fields', 
			'Please fill-in the mandatory fields (in blue) and try again.', 
			'warn', 
			function() { vkDom.focus(missing) }
		);
		return true;
	}
	return false;
}


kigoForm.prototype.setMissing = function(errors)
{
	var	i, idx, missingField = null;

	for(i = 0; i < errors.length; i++)
	{
		if( (idx = this._idx(errors[i])) != null)
			missingField = setMissingField(this.elements[idx].id, missingField);
	}

	vkPopup.message(
		'Missing or invalid data', 
		'Please fill-in the fields'+(missingField ? ' (in blue) ' : ' ')+'correctly and try again.', 
		'warn', 
		function() { if(missingField) { vkDom.focus(missingField); } }
	);
}

kigoForm.prototype.markMissing = function(errors)
{
	var	i, idx, missingField = null;

	for(i = 0; i < errors.length; i++)
	{
		if( (idx = this._idx(errors[i])) != null)
			missingField = setMissingField(this.elements[idx].id, missingField);
	}

	if(missingField)
		vkDom.focus(missingField);
}


kigoForm.prototype._isMissing = function(obj)
{
	switch(obj.tagName)
	{
		case 'INPUT':

			switch(obj.getAttribute('type').toUpperCase())
			{
				case 'TEXT':
				case 'PASSWORD':
					if(!obj.value.length)
						return true;
					break;
			}
			break;

		case 'TEXTAREA':
			if(!obj.value.length)
				return true;
			break;

		case 'SELECT':
			if(obj.selectedIndex < 1)
				return true;
			break;
	}

	return false;
}



/*******************************************/
/* Misc */

/*
kigoForm.prototype.listFields = function()	// [flag(s) [,flag(s) ...] ]
{
	var	i, j, matches, result = [];

	for(i = 0; i < this.elements.length; i++)
	{
		if(arguments.length)
		{
			matches = false;

			for(j = 0; j < arguments.length; j++)
			{
				if( (arguments[j] & this.elements[i].flash) == arguments[j])
				{
					matches = true;
					break;
				}
			}
		}
		else
			matches = true;
		
		if(matches)
			result[result.length] = this.elements[i];
	}
}
*/

kigoForm.prototype.forEachField = function(callback)	// [flag(s) [,flag(s) ...] ]
{
	var	i, j, matches, result = [];

	if(typeof(callback) != 'function')
		return;

	for(i = 0; i < this.elements.length; i++)
	{
		if(arguments.length > 1)
		{
			matches = false;

			for(j = 1; j < arguments.length; j++)
			{
				if( (arguments[j] & this.elements[i].flash) == arguments[j])
				{
					matches = true;
					break;
				}
			}
		}
		else
			matches = true;
		
		if(matches)
			callback(this.elements[i].object, this.elements[i].id);
	}
}




// Privates
kigoForm.prototype._idx = function(element)
{
	return typeof(element) == 'string' && typeof(this.hash[element]) == 'number' ? this.hash[element] : null;
}



/* /js/kigoForm2.js */ /* UTF8 COOKIE: éà */

/************************************************************************************************************/
/* kigoForm2 */

/*
	Forked from (and obsoletes) kigoForm on 04/01/2010.
	We may now receive either an ID or an element, and even a kigoDom instance.


	NOTE:

	Unlike kigoForm, this class does not automatically fill "select" choices.
	Please use kigoSelect2 for filling the select choices BEFORE calling kigoForm2::read() or kigoForm2::write() methods

	As of 13/07/2010, the define() method may recieve NULL value for the field argument, and will generate a pseudo-random identifier. 
	Moreover, the define() method now returns the provided or generated field name (was returning void).


Type	Returns			Method							Note

		kigoForm2		kigoForm2()									Creates an empty instance

		string			define(field, element, flags [,validator])	Defines a form field.
																	- field is the unique name given to the field. It matches property names in objects manipulated by read() and write()
																	  field may be NULL. In that case, an auto-generated field name is applied to the field.
																	- element is either a kigoDom instance, a dom node, or a unique element ID (string)
																	- flags is logical "or" of one or more kigoForm2.FLAG_* flags
																	- validator is an optional validator for the field (to be used with the kigoForm2.FLAG_VALIDATE flag).
																		It may either be:
																			-	A callback, that recieves the following parameters:
																					callback(value, field, <kigoDom instance>, <kigoForm2 instance>)
																					The callback must return a boolean value.
																			-	An array that must hold a kigoVal method name as first element.
																					Other optional elements are arguments to be passed to the kigoVal method
																	Returns the provided or the generated field name.

		bool			defined(field)								Checks whether the field was defined.

		void			resetFlags([field])							If a field is specified, resets the flags to the original value (the one provided in define() call)
																	If a field is not specified, resets the flags of each field to the original value.

		void			setFlags(field, flags)						Overrides flags for the field.

		void			addFlags(field, flags)						Adds (logical OR) flags to the field.

		void			removeFlags(field, flags)					Removes flags from the field.

		bool			hasFlags(field, flags)						Checks whether the field has all the flags.

		mixed			getValue(field)								Returns current value of a field, regardless of flags settings.

		bool			validate(field)								Applies field validator, regardless of flags settings. Returns true if no validator was set for the given field.

		void			read(obj)									Reads data from an object (ie. PHP associative array) and fills in the dom elements with those values.
																	Handles fields with FLAG_READ flag only.

		object			write([obj))								Creates or completes (if "obj" is specified) an object (that would become associative array in PHP) with data read from dom elements.
																	Handles fields with FLAG_WRITE flag only.
																	If the optional "obj" value is specified, properties are appended to that object.

		void			setupMonitor(callback [, freq])				Setups a monitor callback. Takes optional second argument freq (milliseconds). Previously running monitoring, if any, is stopped.

		void			startMonitor()								Starts monitoring fields with FLAG_MONITOR flag. Previously running monitoring, if any, is stopped.

		void			stopMonitor()								Stops monitoring all fields.

		array			missingMandatory([mark=true])				Returns an array of fields flagged as mandatory (FLAG_MANDATORY) and not having a value.
																	The return fields will optionally be marked as missing (CSS marker).
																	Are considered without a value:
																		- empty INPUT TEXT, INPUT PASSWORD and TEXTAREA
																		- select for which kigoSelect2::getValue() returns NULL

		array			missingInvalid([mark=true])					Returns an array of fields flagged to be validated (FLAG_VALIDATE) and that failed to validate.
																	The return fields will optionally be marked as missing (CSS marker).
																	Fields flagged with FLAG_VALIDATE but without a valid validator are considered as failed to validate.

		void			markMissing(field)							Marks the fiels as missing (CSS marker). "field" may be either a single field name (string) or an array of field names.

		void			unmarkMissing(field)						Unmarks the field as missing. "field" may be either a single field name (string) or an array of field names.

		void			unmarkMissingAll()							Unmarks all the fields as missing.

		void			unmarkMissingAll(flags [, flags ...])		Unmarks all the fields matching any of the flags (or flag collections) as missing.

		void			focus(field)								Focuses a field.

		void			forEach(callback)							Runs the callback for every element.
																	The callback recieves the following parameters:
																	callback(field, <kigoDom instance>, <kigoForm2 instance>)
																	The loop is done on a clone, therefore changes
																	made to the instance are not visible during the loop.

		void			forEach(callback, flags [, flags ...])		Runs the callback for every element that matched any of the flags (or flag collections)


		void			missingMandatoryMessage([field])			Displays the standard 'fields missing' message.
																	The optional field argument may be either a single field name (string) or an array of field names.
																	It is used only to focus the (first) field on user pressing 'OK' button.

		void			missingInvalidMessage([field])				Displays the standard 'invalid fields' message.
																	The optional field argument may be either a single field name (string) or an array of field names.
																	It is used only to focus the (first) field on user pressing 'OK' button.

*/



//////////////////////////////////////////////////////////////////
// CONSTRUCTOR

function	kigoForm2()
{
	this.fields = [];
	this.monitor = new vkFormMonitor();

	this.monitorCallback = null;
	this.monitorPeriod = 500;
}


//////////////////////////////////////////////////////////////////
// CONSTANTS

kigoForm2.FLAG_NONE = 0;												// No flags - because flag is now a mandatory argument for the define() method
kigoForm2.FLAG_MANDATORY = 1;											// Processed in missingMandatory()
kigoForm2.FLAG_MONITOR = 2;												// Field is monitored for changes
kigoForm2.FLAG_READ = 4;												// Processed in read()
kigoForm2.FLAG_WRITE = 8;												// Processed in write()
kigoForm2.FLAG_READWRITE = kigoForm2.FLAG_READ | kigoForm2.FLAG_WRITE;	// Processed in both read() and write()
kigoForm2.FLAG_VALIDATE = 16;											// Processed in missingInvalid()

kigoForm2.FLAG_USER = 32;												// First user-defined flag. User-defined flags should be FLAG_USER*1, FLAG_USER*2, FLAG_USER*4, ...

//////////////////////////////////////////////////////////////////
// PRIVATE PROPERTIES & METHODS

kigoForm2.prototype.getFieldIndex = function(field)	// [, method]
{
	for(var i = 0; i < this.fields.length; i++)
	{
		if(this.fields[i].name == field)
			return i;
	}

	if(arguments.length > 1)
		throw 'kigoForm2::'+arguments[1]+'() : no such field';

	return null;
}

kigoForm2.isInstance = function(i)
{
	return kigo.is_object(i) && i.constructor === kigoForm2;
}

kigoForm2.prototype.forEachArray = function(callback, arr)
{
	var	fields = this.fields.slice(0);

	if(arr.length)
	{
		for(var i = 0; i < fields.length; i++)
		{
			for(var j = 0; j < arr.length; j++)
			{
				if((arr[j] & fields[i].flags) == arr[j])
				{
					callback(fields[i].name, fields[i].dom, this, i);
					break;
				}
			}
		}
	}
	else
	{
		for(var i = 0; i < fields.length; i++)
			callback(fields[i].name, fields[i].dom, this, i);
	}
}





//////////////////////////////////////////////////////////////////
// NON-STATIC METHODS


// void
kigoForm2.prototype.define = function(field, element, flags)	// [, validator]
{
	// 13/07/2010
	if(field == null)
	{
		do 
		{
			field = '_AUTO_'+Math.round(Math.random() * 1000000);
		} while(this.getFieldIndex(field) != null);
	}
	else if(this.getFieldIndex(field) != null)
		throw 'kigoForm2::define(): field already define()\'d';

	try
	{
		element = new kigoDom(element);
	}
	catch(e)
	{
		throw 'kigoForm2::define(): Invalid DOM node ('+e+')';
	}

	if(!kigo.is_int(flags))
		throw 'kigoForm2::define(): invalid flags';

	// 23/02/2010 - validator
	var	validator;

	if(arguments.length > 3)
	{
		if(kigo.is_function(arguments[3]))
			validator = arguments[3];
		else if(kigo.is_array(arguments[3]))
		{
			// The first element must match a kigoVal method name
			if(
				arguments[3].length &&
				kigo.is_string(arguments[3][0]) &&
				kigo.is_function(kigoVal[arguments[3][0]]) &&
				kigoVal.hasOwnProperty(arguments[3][0])
			)
				validator = arguments[3];
			else
				throw 'kigoForm2::define(): invalid validator';
		}
		else
			throw 'kigoForm2::define(): invalid validator';
	}
	else
		validator = null;

	this.fields.push(
		{
			'name'			:	field,
			'dom'			:	element,
			'flags'			:	flags,
			'default_flags'	:	flags,
			'validator'		:	validator
		}
	);

	return field;
}

// bool
kigoForm2.prototype.defined = function(field)
{
	return this.getFieldIndex(field) != null ? true : false;
}


/*************************************/
/* Manipulating flags */


// void
kigoForm2.prototype.resetFlags = function()	// [field]
{
	if(arguments.length)
	{
		var	idx = this.getFieldIndex(arguments[0], 'resetFlags');
		this.fields[idx].flags = this.fields[idx].default_flags;
	}
	else
	{
		// Reset flags for all fields to their original values
		for(var i = 0; i < this.fields.length; i++)
			this.fields[i].flags = this.fields[i].default_flags;
	}
}

// void
kigoForm2.prototype.setFlags = function(field, flags)
{
	this.fields[this.getFieldIndex(field, 'setFlags')].flags = flags;
}

// void
kigoForm2.prototype.addFlags = function(field, flags)
{
	this.fields[this.getFieldIndex(field, 'addFlags')].flags |= flags;
}

// void
kigoForm2.prototype.removeFlags = function(field, flags)
{
	this.fields[this.getFieldIndex(field, 'removeFlags')].flags &= ~flags;
}

// bool
kigoForm2.prototype.hasFlags = function(field, flags)
{
	return (this.fields[this.getFieldIndex(field, 'hasFlags')].flags & flags) == flags;
}




/*************************************/
/* Reading & writing */


// void
kigoForm2.prototype.read = function(obj)
{
	var	self = this;

	(new kigoAssoc(obj)).forEach(
		function(field, value)
		{
			var	idx = self.getFieldIndex(field);

			// Skip unknown fields, or those that don't have the READ flag set
			if(
				idx == null ||
				!(self.fields[idx].flags & kigoForm2.FLAG_READ)
			)
				return;

			var	obj = self.fields[idx].dom.domNode();

			switch(obj.tagName)
			{
				case 'INPUT':

					switch(obj.getAttribute('type').toUpperCase())
					{
						case 'TEXT':
						case 'PASSWORD':
							obj.value = (kigo.is_string(value) || kigo.is_number(value)) ? value : '';
							break;

						case 'CHECKBOX':
							obj.checked = (value == 1 || value == true ? true : false);
							break;

						case 'RADIO':
							obj.checked = (value == 1 || value == true ? true : false);
							break;
					}
					break;

				case 'TEXTAREA':
					obj.value = (kigo.is_string(value) || kigo.is_number(value)) ? value : '';;
					break;

				case 'SELECT':
					(new kigoSelect2(obj)).setValue(value);
					break;

				default:
					throw 'kigoForm2::read(): unknown DOM element type for the ['+field+'] field';
			}
		}
	);
}

// object
kigoForm2.prototype.write = function()	// [object]
{
	var	obj, field;
	var	result = arguments.length && kigo.is_object(arguments[0]) ? arguments[0] : {};

	for(var i = 0; i < this.fields.length; i++)
	{
		if(!(this.fields[i].flags & kigoForm2.FLAG_WRITE))
			continue;

		field = this.fields[i].name;

		switch((obj = this.fields[i].dom.domNode()).tagName)
		{
			case 'INPUT':

				switch(obj.getAttribute('type').toUpperCase())
				{
					case 'TEXT':
					case 'PASSWORD':
						result[field] = obj.value;
						break;

					case 'CHECKBOX':
						result[field] = obj.checked;
						break;

					case 'RADIO':
						result[field] = obj.checked;
						break;

					default:
						throw 'kigoForm2::write(): unknown DOM element type for the ['+field+'] field';
				}
				break;

			case 'TEXTAREA':
				result[field] = obj.value;
				break;

			case 'SELECT':
				result[field] = (new kigoSelect2(obj)).getValue();
				break;

			default:
				throw 'kigoForm2::write(): unknown DOM element type for the ['+field+'] field';
		}
	}

	return result;
}


// object
kigoForm2.prototype.getDom = function(field)
{
	var	idx;

	if((idx = this.getFieldIndex(field)) == null)
		throw 'kigoForm2::value(): unknown field ['+field+']';

	return this.fields[idx].dom;
}


// object
kigoForm2.prototype.getValue = function(field)
{
	var	idx, obj;

	if((idx = this.getFieldIndex(field)) == null)
		throw 'kigoForm2::getValue(): unknown field ['+field+']';

	switch((obj = this.fields[idx].dom.domNode()).tagName)
	{
		case 'INPUT':

			switch(obj.getAttribute('type').toUpperCase())
			{
				case 'TEXT':
				case 'PASSWORD':
					return obj.value;

				case 'CHECKBOX':
					return obj.checked;

				case 'RADIO':
					return obj.checked;

				default:
					throw 'kigoForm2::getValue(): unknown DOM element type for the ['+field+'] field';
			}
			break;


		case 'TEXTAREA':
			return obj.value;

		case 'SELECT':
			return (new kigoSelect2(obj)).getValue();

		default:
			throw 'kigoForm2::getValue(): unknown DOM element type for the ['+field+'] field';
	}
	
	// Unreachable
	throw 'Unreachable';
}


/*************************************/
/* Monitoring */


// void
kigoForm2.prototype.setupMonitor = function(callback)	// [, period ]
{
	// 15/03/2010 - Stops any previous monitoring session, if any
	this.stopMonitor();

	if(typeof(callback) == 'function')
	{
		this.monitorCallback = callback;
		if(arguments.length > 1)
			this.monitorPeriod = arguments[1];
	}
}

// void
kigoForm2.prototype.startMonitor = function()
{
	var	i, cnt = 0;

	this.stopMonitor();

	if(!this.monitorCallback)
		return;

	for(i = 0; i < this.fields.length; i++)
	{
		if(this.fields[i].flags & kigoForm2.FLAG_MONITOR)
		{
			++cnt;
			this.monitor.addElement(this.fields[i].dom.domNode());
		}
	}

	if(cnt)
		this.monitor.monitor(this.monitorCallback, this.monitorPeriod);
}

// void
kigoForm2.prototype.stopMonitor = function()
{
	this.monitor.reset();
}




/*************************************/
/* Validating tools */



// bool
kigoForm2.prototype.validate = function(field)
{
	var	idx, value, dom, obj, validator;

	if((idx = this.getFieldIndex(field)) == null)
		throw 'kigoForm2::validate(): unknown field ['+field+']';

	if((validator = this.fields[idx].validator) == null)
		return true;

	value = null;
	dom = this.fields[idx].dom;
	obj = dom.domNode();

	switch(obj.tagName)
	{
		case 'INPUT':

			switch(obj.getAttribute('type').toUpperCase())
			{
				case 'TEXT':
				case 'PASSWORD':
					value = kigo.trim(obj.value);
					break;

				case 'CHECKBOX':
					value = obj.checked ? true : false;
					break;

				// Currently not supported
				// case 'RADIO':
			}

			break;

		case 'TEXTAREA':
			value = kigo.trim(obj.value);
			break;

		case 'SELECT':
			value = (new kigoSelect2(obj)).getValue();
			break;
	}

	if(kigo.is_function(validator))
	{
		if(!validator(value, field, dom, this))
			return false;
	}
	else if(kigo.is_array(validator))
	{
		if(!kigoVal[validator[0]].apply(kigoVal[validator[0]], [ value ].concat(validator.slice(1))))
			return false;
	}

	return true;
}









// array
kigoForm2.prototype.missingMandatory = function()	// [mark=true]
{
	var	mark = arguments.length ? (arguments[0] ? true : false) : true;
	var	missing = [];

	this.forEach(
		function(field, dom)
		{
			var	obj = dom.domNode();

			switch(obj.tagName)
			{
				case 'INPUT':

					switch(obj.getAttribute('type').toUpperCase())
					{
						case 'TEXT':
						case 'PASSWORD':
							if(!kigo.trim(obj.value).length)
							{
								missing.push(field);
								if(mark)
									vkDom.addClass(obj, 'missing');
							}
							break;
						/*
						case 'CHECKBOX':
						case 'RADIO':
							if(!obj.checked)
							{
								missing.push(field);
								if(mark)
									vkDom.addClass(obj, 'missing');
							}
							break;
						*/
					}

					break;

				case 'TEXTAREA':
					if(!kigo.trim(obj.value).length)
					{
						missing.push(field);
						if(mark)
							vkDom.addClass(obj, 'missing');
					}
					break;

				case 'SELECT':
					if((new kigoSelect2(obj)).getValue() == null)
					{
						missing.push(field);
						if(mark)
							vkDom.addClass(obj, 'missing');
					}
					break;
			}
		},
		kigoForm2.FLAG_MANDATORY
	);

	return missing;
}


kigoForm2.prototype.missingInvalid = function()	// [mark=true]
{
	var	mark = arguments.length ? (arguments[0] ? true : false) : true;
	var	missing = [];

	this.forEach(
		function(field, dom, me, idx)
		{
			var	validator = me.fields[idx].validator;

			function	failed()
			{
				missing.push(field);
				if(mark)
					vkDom.addClass(dom.domNode(), 'missing');
			}


			if(validator == null)
				return failed();

			// Get field value
			var	value = null;
			var	obj = dom.domNode();

			switch(obj.tagName)
			{
				case 'INPUT':

					switch(obj.getAttribute('type').toUpperCase())
					{
						case 'TEXT':
						case 'PASSWORD':
							value = kigo.trim(obj.value);
							break;

						case 'CHECKBOX':
							value = obj.checked ? true : false;
							break;

						// Currently not supported
						// case 'RADIO':
					}

					break;

				case 'TEXTAREA':
					value = kigo.trim(obj.value);
					break;

				case 'SELECT':
					value = (new kigoSelect2(obj)).getValue();
					break;
			}

			if(kigo.is_function(validator))
			{
				if(!validator(value, field, dom, me))
					return failed();
			}
			else if(kigo.is_array(validator))
			{
				if(!kigoVal[validator[0]].apply(kigoVal[validator[0]], [ value ].concat(validator.slice(1))))
					return failed();
			}
		},
		kigoForm2.FLAG_VALIDATE
	);

	return missing;
}






// void
kigoForm2.prototype.markMissing = function(field)
{
	if(!kigo.is_array(field))
		field = [ field ];

	for(var i = 0; i < field.length; i++)
		vkDom.addClass(this.fields[this.getFieldIndex(field[i], 'markMissing')].dom.domNode(), 'missing');
}

// void
kigoForm2.prototype.unmarkMissing = function(field)
{
	if(!kigo.is_array(field))
		field = [ field ];

	for(var i = 0; i < field.length; i++)
		vkDom.removeClass(this.fields[this.getFieldIndex(field[i], 'unmarkMissing')].dom.domNode(), 'missing');
}

// void
kigoForm2.prototype.unmarkMissingAll = function()
{
	this.forEachArray(
		function(field, dom)
		{
			vkDom.removeClass(dom.domNode(), 'missing');
		},
		arguments
	);
}


// void
kigoForm2.prototype.focus = function(field)
{
	vkDom.focus(this.fields[this.getFieldIndex(field, 'focus')].dom.domNode());
}


/*************************************/
/* Traversing */

// void
kigoForm2.prototype.forEach = function(callback)	// [flag(s) [,flag(s) ...] ]
{
	var	a = [];

	for(var i = 1; i < arguments.length; i++)
		a.push(arguments[i]);

	this.forEachArray(callback, a);
}




/*************************************/
/* Misc */



kigoForm2.prototype.missingMandatoryMessage = function()	// [field]
{
	var	self = this;
	var	focusField;

	if(arguments.length)
	{
		if(kigo.is_array(arguments[0]) && arguments[0].length)
			focusField = arguments[0][0];
		else if(kigo.is_string(arguments[0]))
			focusField = arguments[0];
		else
			focusField = null;
	}
	else
		focusField = null;

	vkPopup.message(
		'Mandatory fields',
			'Please fill-in the mandatory fields (in blue) and try again.',
		'warn',
		function()
		{
			if(focusField != null)
				self.focus(focusField);
		}
	);
}



kigoForm2.prototype.missingInvalidMessage = function()	// [field]
{
	var	self = this;
	var	focusField;

	if(arguments.length)
	{
		if(kigo.is_array(arguments[0]) && arguments[0].length)
			focusField = arguments[0][0];
		else if(kigo.is_string(arguments[0]))
			focusField = arguments[0];
		else
			focusField = null;
	}
	else
		focusField = null;

	vkPopup.message(
		'Mandatory fields',
			'Please fill-in the highlighted fields (in blue) correctly and try again.',
		'warn',
		function()
		{
			if(focusField != null)
				self.focus(focusField);
		}
	);
}




/* /js/kigoSelect.js */ /* UTF8 COOKIE: éà */


/*

	OBSOLETED BY kigoSelect2
	All the new projects must use kigoSelect2

*/


function	kigoSelect(el)
{
	this.el = vkDom.el(el);
}


kigoSelect.prototype.element = function()
{
	return this.el;
}

// 02/12/2009 - For naming compatibility with kigoDom
kigoSelect.prototype.domNode = function()
{
	return this.el;
}

kigoSelect.prototype.reset = function()
{
	vkDom.clean(this.el);
/*
	for(var i = this.el.options.length; i >=0; i--)
		this.el.options[i] = null;

	this.el.options.length = 0;
*/
}


kigoSelect.prototype.addOption = function(value, text)	// [, class(es)]
{
	var	o = document.createElement('option');
	o.value = value;
	if(arguments.length == 3)
		o.className = arguments[2];
	o.appendChild(document.createTextNode(text));
	this.el.appendChild(o);

	return this.el.options.length - 1;
}


kigoSelect.prototype.insertOption = function(value, text)	// [, insertIndex]
{
	if(arguments.length == 2)
		insertIndex = 0;
	else if(arguments.length == 3)
		insertIndex = arguments[2];
	else
		throw 'Bad usage';

	if(insertIndex >= this.el.options.length)
		return this.addOption(value, text);
	
	var	o = document.createElement('option');
	o.value = value;
	o.appendChild(document.createTextNode(text));

	this.el.options[insertIndex].parentNode.insertBefore(o, this.el.options[insertIndex]);

	if(this.el.selectedIndex >= insertIndex)
		++this.el.selectedIndex;

	return insertIndex;
}

kigoSelect.prototype.addOptionsFromArray = function(arr)	// [, valueKey, textKey]
{
	if(arguments.length == 1)
	{
		var	tmp;
		
		for(i in arr)
		{
			switch(typeof(arr[i]))
			{
				case 'number':
				case 'string':
					this.addOption(i, arr[i]);
					break;
			}

		}
	}
	else if(arguments.length == 3)
	{
		var	valueKey = arguments[1];
		var	textKey = arguments[2];

		for(var i = 0; i < arr.length; i++)
			this.addOption(arr[i][valueKey], arr[i][textKey]);
	}
	else
		throw 'Bad usage';
}



kigoSelect.prototype.addGroup = function(group)
{
	this.el.appendChild(group.el);
}

kigoSelect.prototype.getIndex = function()
{
	return this.el.selectedIndex;
}

kigoSelect.prototype.setIndex = function(idx)
{
	return this.el.selectedIndex = idx;
}

kigoSelect.prototype.getValue = function()
{
	if(this.el.selectedIndex < 0)
		return null;
	
	return this.el.options[this.el.selectedIndex].value;
}

kigoSelect.prototype.getOption = function()
{
	if(this.el.selectedIndex < 0)
		return null;
	
	return this.el.options[this.el.selectedIndex];
}

kigoSelect.prototype.setValue = function(value)
{
//	kigoDebug.text('setValue('+value+')');

	if(this.el.selectedIndex < 0)
		return false;
	
	for(var i = 0; i < this.el.options.length; i++)
	{
		if(this.el.options[i].value == value)
		{
			this.el.selectedIndex = i;

			// Stupid IE6 fix
			if(vkDom.hasClass(document.body, 'ua-msie-6'))
				this.el.options[i].setAttribute('selected', true);

			return true;
		}
	}

	this.el.selectedIndex = 0;

	// Stupid IE6 fix
	if(vkDom.hasClass(document.body, 'ua-msie-6'))
		this.el.options[0].setAttribute('selected', true);

	return false;
}



function	kigoOptgroup(text)
{
	this.el = document.createElement('optgroup');
	this.el.label = text;
}

kigoOptgroup.prototype.addOption = function(value, text)	// [, class(es)]
{
	var	o = document.createElement('option');
	o.value = value;
	if(arguments.length == 3)
		o.className = arguments[2];
	o.appendChild(document.createTextNode(text));
	this.el.appendChild(o);
}


/* /js/kigoSelect2.js */ /* UTF8 COOKIE: éà */

/*

06/01/2010		Release #0

Type	Returns				Method									Note


		kigoSelect2			kigoSelect2(element)					Creates an instance from a "select" DOM node

		kigoSelect2			kigoSelect2(id)							Creates an instance from a unique element id (string).

		kigoSelect2			kigoSelect2(kigoDom)					Creates an instance for a kigoDom instance

		kigoDom				dom()									Returns the kigoDom object

		Element				domNode()								Returns the DOM Element

		kigoSelect2			reset()									Resets (removes all OPTIONs and OPTGROUPs). OBSOLETE - PLEASE USE empty()

		kigoSelect2			empty()									Alias to reset(). This is the prefered name as of 14/04/2010

		kigoSelect2			addOption(value, text [, className])	Adds an option

		kigoSelect2			addSelectOption()						Adds the special '-- Please select --' option with empty-string value.

		kigoSelect2			addOptions(obj [, pleaseSelect=false])	Adds options from an object (ie. serialized PHP associative array).
																	If the second argument is set to true, a first option is inserted, with empty-string value and the '-- Please select --' text.

		kigoSelect2			addGroup(group)							Adds a kigoOptgroup2 instance

		int | null			getIndex()								Returns the currently selected index, NULL if there are no options

		bool				setIndex(idx)							Returns TRUE on success, FALSE otherwise (ie. index out of range)

		string | default	getValue([default=null])				Returns value of the currently selected index, default otherwise

		string | default	getText([default=null])					Returns the text of the currently selected index, default otherwise

		kigoDom | default	getOption([default=null])				Returns a kigoDom instance holding the OPTION element of the currently selected index, default otherwise

		bool				setValue(value [, triggerChange=false])	Selects the option having the given value. Only the first such an option is selected. Returns TRUE if found, FALSE otherwise.
																	(unlike the kigoSelect, we don't preselect the first value on failure)
																	The change handler is not triggered unless the triggerChange argument is set to TRUE.

		kigoSelect2			clone()									Clones the instance, including the DOM node (deep clone)


TODO: insertOption(value, text [, insertIndex])

*/

function	kigoSelect2(el)
{
	try
	{
		this.node = new kigoDom(el);
	}
	catch(e)
	{
		throw 'kigoSelect2::kigoSelect2(): Invalid DOM node';
	}

	// 29/06/2010 - Go further - make sure we got a <select> node
	if(this.node.domNode().tagName != 'SELECT')
		throw 'kigoSelect2::kigoSelect2(): Not a SELECT node';

}

kigoSelect2.prototype.dom = function()
{
	return this.node;
}

kigoSelect2.prototype.domNode = function()
{
	return this.node.domNode();
}

// kigoSelect2
kigoSelect2.prototype.reset = function()
{
	this.node.empty();
	return this;
}

// kigoSelect2
kigoSelect2.prototype.empty = function()
{
	this.node.empty();
	return this;
}


// kigoSelect2
kigoSelect2.prototype.addOption = function(value, text) // [, className]
{
	this.node.append(
		kigoDom.create(
			'option', 
			{ 
				'value'		: value, 
				'className'	: arguments.length == 3 ? arguments[2] : ''
			} 
		).append(
			text
		)
	);

	//return this.node.domNode().options.length - 1;
	return this;
}

// kigoSelect2
kigoSelect2.prototype.addSelectOption = function()	// [text = '--Please select--']
{
	// Prevent if there are already options here
	if(this.node.domNode().options.length)
		throw 'kigoSelect2::addSelectOption(): options were already added';

	// 20/04/2010 - Rewrote to use element attribute rather than value for identifying the "please select" option
	// this.addOption('', arguments.length ? arguments[0] : '-- Please select --', 'please_select');

	this.node.append(
		kigoDom.create(
			'option', 
			{ 
				'value'			:	'',
				'className'		:	'please_select',
				'please_select'	:	'1'
			} 
		).append(
			arguments.length ? arguments[0] : '-- Please select --'
		)
	);

	return this;
}

// kigoSelect2
kigoSelect2.prototype.addOptions = function(obj)	// [, pleaseSelect=false]
{
	var	self = this;

	if(arguments.length > 1 && arguments[1])
		this.addSelectOption();

	(new kigoAssoc(obj)).forEach(
		function(value, text)
		{
			self.addOption(value, text);
		}
	);

	return this;
}

// kigoSelect2
kigoSelect2.prototype.addGroup = function(group)
{
	if(!kigoOptgroup2.isInstance(group))
		throw 'kigoSelect2::addGroup(): bad argument';

	this.node.append(group.domNode());

	return this;
}

// int | null
kigoSelect2.prototype.getIndex = function()
{
	if(this.node.domNode().options.length)
		return this.node.domNode().selectedIndex;
	return null;
}

// bool
kigoSelect2.prototype.setIndex = function(idx)
{
	if(
		this.node.domNode().options.length &&
		idx >= 0 &&
		idx < this.node.domNode().options.length
	)
	{
		this.node.domNode().selectedIndex = idx;

		// Stupid IE6 fix
		if(vkDom.hasClass(document.body, 'ua-msie-6'))
			this.node.domNode().options[idx].setAttribute('selected', true);

		return true;
	}

	return false;
}

// text | default
kigoSelect2.prototype.getValue = function() // [ default = null ]
{
	if(
		!this.node.domNode().options.length ||
		this.node.domNode().selectedIndex < 0
	)
		return arguments.length ? arguments[0] : null;

	// Catch 'Please select' - null regardless of the 'default' argument

	var	tmp;
	
	//return (tmp = this.node.domNode().options[this.node.domNode().selectedIndex].value).length ? tmp : null;

	tmp = this.node.domNode().options[this.node.domNode().selectedIndex];

	if(tmp.getAttribute('please_select') == '1')
		return null;

	return tmp.value;
}


// text | default
kigoSelect2.prototype.getText = function() // [ default = null ]
{
	if(
		!this.node.domNode().options.length ||
		this.node.domNode().selectedIndex < 0
	)
		return arguments.length ? arguments[0] : null;
	
	return this.node.domNode().options[this.node.domNode().selectedIndex].text;
}


// kigoDom | default
kigoSelect2.prototype.getOption = function() // [ default = null ]
{
	if(
		!this.node.domNode().options.length ||
		this.node.domNode().selectedIndex < 0
	)
		return arguments.length ? arguments[0] : null;
	
	return new kigoDom(this.node.domNode().options[this.node.domNode().selectedIndex]);
}

// bool
kigoSelect2.prototype.setValue = function(value)	// [, triggerChange=false]
{

	if(
		!this.node.domNode().options.length ||
		this.node.domNode().selectedIndex < 0
	)
		return false;

	for(var i = 0; i < this.node.domNode().options.length; i++)
	{
		if(
			// When catching scalar values, make sure NOT to catch 'Please select', which would happen if value is an empty string
			(
				this.node.domNode().options[i].value === kigo.toString(value) &&
				this.node.domNode().options[i].getAttribute('please_select') != '1'
			) ||

			// Catch 'Please select'
			(
				!i &&
				value == null &&
				//this.node.domNode().options[i].value == ''
				this.node.domNode().options[i].getAttribute('please_select') == '1'
			)
		)
		{
			var	prevIndex = this.node.domNode().selectedIndex;

			this.node.domNode().selectedIndex = i;

			// Stupid IE6 fix
			if(vkDom.hasClass(document.body, 'ua-msie-6'))
				this.node.domNode().options[i].setAttribute('selected', true);

			if(
				arguments.length > 1 &&
				arguments[1] &&
				prevIndex != i &&
				kigo.is_function(this.node.domNode().onchange)
			)
				this.node.domNode().onchange();

			return true;
		}
	}

	// Not found
	return false;
}

// kigoSelect2
kigoSelect2.prototype.clone = function()
{
	return new kigoSelect2((new kigoDom(this.node)).clone());
}



/**************************************************************************************/
/* kigoOptgroup2 */
/*

06/01/2010		Release #0

Type	Returns				Method								Note


		kigoOptgroup2		kigoOptgroup2(text)					Creates an instance

		Element				domNode()							Returns the DOM Element

		kigoOptgroup2		addOption(value, text [,className])	Adds an option

		kigoOptgroup2		addOptions(obj)						Adds options from an object (ie. PHP associative array).


*/


function	kigoOptgroup2(text) // [, className]
{
	this.node = kigoDom.create(
		'optgroup', 
		{ 
			'label'		:	text,
			'className'	:	arguments.length > 1 ? arguments[1] : ''
		}
	);
}

kigoOptgroup2.prototype.domNode = function()
{
	return this.node.domNode();
}

kigoOptgroup2.isInstance = function(i)
{
	return kigo.is_object(i) && i.constructor === kigoOptgroup2;
}

// void
kigoOptgroup2.prototype.addOption = function(value, text) // [, className]
{
	this.node.append(
		kigoDom.create(
			'option', 
			{ 
				'value'		: value, 
				'className'	: arguments.length == 3 ? arguments[2] : ''
			} 
		).append(
			text
		)
	);

	return this;
}


// void
kigoOptgroup2.prototype.addOptions = function(obj)
{
	var	self = this;

	(new kigoAssoc(obj)).forEach(
		function(value, text)
		{
			self.addOption(value, text);
		}
	);

	return this;
}




/* kigoDatePicker.js */ /* UTF8-Côôkie */

/*

29/06/2010		Release #0

Type	Returns				Method										Note

		kigoDatePicker		kigoDatePicker(element)						Creates an instance from an "input type=text" DOM node

		kigoDatePicker		kigoDatePicker(id)							Creates an instance from a unique element id (string).

		kigoDatePicker		kigoDatePicker(kigoDom)						Creates an instance for a kigoDom instance

		kigoDom				dom()										Returns the kigoDom object

		Element				domNode()									Returns the DOM Element

		kigoDatePicker		setYearsRange(minYear, maxYear)				Sets the minimum and maximum selectable years (default: 2000 - 2030)

		kigoDatePicker		open()										Opens the date picker.
																		Note that the open on text input mouse click is automatic.

		kigoDatePicker		close()										Closes the date picker, if open.

		kigoDatePicker		empty(										Alias to setDate(null)
								[triggerChange=false 
									[, forceTrigger=false]
								]
							)	

		kigoDatePicker		setDate(									Sets a new date. Use null to empty the text input field.
								kigoDate								Calling this method implicitly closes the date picker, if open.
								[, triggerChange=false					The change handler is not triggered unless the triggerChange argument is set to TRUE.
									[, forceTrigger=false]				Setting forceTrigger to TRUE will trigger the change handler even if the new date is equal
								]										to the previous date.
							)

		kigoDatePicker		setText(									Sets a text content instead of date in the textfield.
								text									The change handler is not triggered unless the triggerChange argument is set to TRUE.
								[, triggerChange=false					Setting forceTrigger to TRUE will trigger the change handler even if the new date is equal
									[, forceTrigger=false]				to the previous date.
								]
							)

		kigoDate | null		getDate()									Returns the current date, or NULL if no date is set.
		
		kigoDatePicker		onOpen(callback)							Defines the callback to call when the calendar is about to be open.
																		The callback received the date object.
																		If the callback does not return TRUE, the date picker won't open.
																		Set the callback argument to NULL to disable it.
																		Set the callback argument to FALSE to always prevent the date picker from being open.

		kigoDatePicker		onChange(callback)							Defines the callback to call on date change.
																		The callback will receive two arguments:
																			1. The new date (either a kigoDate instance, or NULL if no date is set)
																			2. The old date (either a kigoDate instance, or NULL if no date was set)

		kigoDatePicker		disableDates(callback)						Defines the callback (or null to disable the feature) that may be used to disable some dates on the picker.
																		Dates for which the callback returns TRUE are disabled, while the dates for which the callback returns FALSE are not disabled.

		kigoDatePicker		disableDates(beforeDate, afterDate)			Disables all dates before (not including) "beforeDate" and/or all dates after (not including) afterDate.
																		Both arguments may be NULL.

		kigoDatePicker		clone()										Clones the instance, including the DOM node (deep clone)

*/


function	kigoDatePicker(el)
{
	try
	{
		this.node = new kigoDom(el);
	}
	catch(e)
	{
		throw 'kigoDatePicker::kigoDatePicker(): Invalid DOM node';
	}

	// 29/06/2010 - Go further - make sure we got a <input type="text"> node
	if(
		this.node.domNode().tagName != 'INPUT' || 
		typeof(this.node.domNode().type) != 'string' ||
		this.node.domNode().type.toUpperCase() != 'TEXT'
	)
		throw 'kigoSelect2::kigoSelect2(): Not an INPUT TYPE="TEXT" node';

	this.isDate = true;			// Whether the textfield stores a date...
	this.current = null;		// Reference to the currently open picker, so that we may close it from outside
	this.openCallback = null;
	this.changeCallback = null;
	this.disableCallback = null;
	this.minYear = 2000;
	this.maxYear = 2030;

	// Setup the onclick handler on the input element
	var	self = this;

	this.node.domNode().onclick = function()
	{
		self.node.domNode().blur();
		self.open();
	}

	// Finally, set an empty date by default - prevent inheriting an invalid date!
	this.empty();
}

// STATIC PRIVATE
kigoDatePicker.dateFormat = '%a, %d %b %Y';

kigoDatePicker.prototype.dom = function()
{
	return this.node;
}

kigoDatePicker.prototype.domNode = function()
{
	return this.node.domNode();
}




// kigoDatePicker
kigoDatePicker.prototype.setYearsRange = function(from, to)
{
	if(
		!kigo.is_int(from) ||
		!kigo.is_int(to) ||
		to < from
	)
		throw 'kigoDatePicker::setYearsRange(): Invalid range';

	this.minYear = from;
	this.maxYear = to;

	if(this.current != null)
		this.current.setRange(this.minYear, this.maxYear);

	return this;
}



// kigoDatePicker
kigoDatePicker.prototype.open = function()
{
	var	self = this;

	this.close();

	// 12/07/2010 - Always prevent from opening!
	if(this.openCallback == false)
		return this;

	if(kigo.is_function(this.openCallback))
	{
		if(this.openCallback(this.getDate()) != true)
			return this;
	}

	this.current = new Calendar(
		1,												//	firstDayOfWeek
		this.isDate ? this.node.domNode().value : '',	//	dateStr
		function(cal)									//	onSelected
		{
			if(cal.dateClicked)
			{
				var	previous = self.getDate();

				self.node.domNode().value = cal.date.print(kigoDatePicker.dateFormat);
				self.isDate = true;

				self.close();	// 12/07/2010 - close before calling the callback

				if(
					kigo.is_function(self.changeCallback) &&
					(previous == null || previous.compare(kigoDate.createFromDate(cal.date)) != 0)
				)
					self.changeCallback(kigoDate.createFromDate(cal.date), previous);

				//self.close();
			}
		},
		function(cal)									//	onClose
		{
			self.close();
		}
	);

	this.current.setDateFormat(kigoDatePicker.dateFormat);
	this.current.setRange(this.minYear, this.maxYear);
	this.current.setDisabledHandler(
		function(date)
		{
			return kigo.is_function(self.disableCallback) ? self.disableCallback(kigoDate.createFromDate(date)) : false;
		}
	);
	this.current.create();
	this.current.refresh();
	this.current.showAtElement(this.node.domNode());

	return this;
}

// kigoDatePicker
kigoDatePicker.prototype.close = function()
{
	if(this.current)
	{
		this.current.hide();
		this.current.destroy();
		this.current = null;
	}

	return this;
}




// kigoDatePicker
kigoDatePicker.prototype.setText = function(text)	// [, triggerChange=false]
{
	var	previous = this.getDate();

	this.close();

	this.node.domNode().value = text;
	this.isDate = false;

	if(
		arguments.length > 1 && arguments[1] &&
		kigo.is_function(this.changeCallback) &&
		(
			(arguments.length > 2 && arguments[2]) ||
			previous != null
		)
	)
		this.changeCallback(null, previous);

	return this;
}






// kigoDatePicker
kigoDatePicker.prototype.setDate = function(date)	// [, triggerChange=false [, forceTrigger=false]]
{
	var	previous = this.getDate();

	this.close();

	if(date == null)
		this.node.domNode().value = '';
	else
	{
		if(!kigoDate.isInstance(date))
			throw 'kigoDatePicker::setDate(): kigoDate instance expected.';

		this.node.domNode().value = date.date().print(kigoDatePicker.dateFormat);
	}

	this.isDate = true;


	if(
		arguments.length > 1 && arguments[1] &&
		kigo.is_function(this.changeCallback) &&
		(
			(arguments.length > 2 && arguments[2]) ||
			(previous == null ? date != null : (date == null ? true : date.compare(previous) != 0))
		)
	)
		this.changeCallback(date, previous);

	return this;
}

// kigoDate
kigoDatePicker.prototype.getDate = function()
{
	if(!this.isDate || this.node.domNode().value == '')
		return null;

	return kigoDate.createFromDate(new Date(this.node.domNode().value));
}



// kigoDatePicker
kigoDatePicker.prototype.empty = function()
{
	return this.setDate(null, arguments.length && arguments[0] ? true : false);
}



// kigoDatePicker
kigoDatePicker.prototype.onOpen = function(callback)
{
	this.openCallback = callback;
	return this;
}



// kigoDatePicker
kigoDatePicker.prototype.onChange = function(callback)
{
	this.changeCallback = callback;
	return this;
}



// kigoDatePicker
kigoDatePicker.prototype.disableDates = function()
{
	if(
		!arguments.length || 
		(arguments.length == 1 && arguments[0] == null) ||
		(arguments.length == 2 && arguments[0] == null && arguments[1] == null)
	)
		this.disableCallback = null;
	else if(
		arguments.length == 1 && 
		kigo.is_function(arguments[0])
	)
		this.disableCallback = arguments[0];
	else if(
		arguments.length == 1 && 
		kigoDate.isInstance(arguments[0])
	)
	{
		var	start = arguments[0];

		this.disableCallback = function(date)
		{
			return (start != null && date.compare(start) > 0);
		}
	}
	else if(
		arguments.length == 2 &&
		(arguments[0] == null || kigoDate.isInstance(arguments[0])) &&
		(arguments[1] == null || kigoDate.isInstance(arguments[1]))
	)
	{
		var	start = arguments[0], end = arguments[1];

		this.disableCallback = function(date)
		{
			return (
				(start != null && date.compare(start) > 0) ||
				(end != null && date.compare(end) < 0)
			);
		}
	}
	else
		throw 'kigoDatePicker::disableDates(): Bad arguments';

	return this;
}











// kigoDatePicker
kigoDatePicker.prototype.clone = function()
{
	return new kigoDatePicker((new kigoDom(this.node)).clone());
}





// kigoDatePicker - UNIMPLEMENTED - UNDOCUMENTED
/*
kigoDatePicker.create = function()	//	[ value
{
	return new kigoDatePicker(
		kigoDom.create(
			'input',
			kigo.object_merge(
				{
					'type'	:	'text',
					'value'	:	''
				},
			),
			
			{
			},
			{
			}
		)
	);

	//throw 'Not implemented yet. The signature is still being designed.';
}
*/

/* /js/kigoList.js */ /* UTF8 COOKIE: éà */

/*

02/12/2009		Release #0



Type	Returns				Method											Note

		kigoList			kigoList()										Creates an empty instance

		kigoList			kigoList(array)									Create an instance for an existing Array

static	kigoList			kigoList.createFromCSV(csv, separator)			Creates an instance from a Comma Separated Values string

		kigoList			empty()											Empties the list.

		int					add(value)										Returns the index of the added value. Note that the index is not constant.
																			Indexes are altered by move(), remove(), ...

		int	| null			insertBefore(value, idx)						Adds the value before index idx.
																			However, if idx is NULL, the value is added to the end (same behaviour as the add() method).
																			Returns the index for the inserted value, or NULL if the provided index was invalid 
																			and value could not be inserted.

		int					length()										Returns the number of elements

		bool				find(value [, strict=false])

		int | null			pos(value, [, strict=false])

		bool | null			isFirst(idx)									Returns null if the index is out of range.

		bool | null			isLast(idx)										Returns null if the index is out of range.

		mixed | null		get(idx)										Returns the value at index idx

		mixed | null		getFirst()										Returns the value at first index

		mixed | null		getLast()										Returns the value at last index

		int | null			move(idx, where)								Moves the value indexed by idx to index where.
																			Returns the new position or null if the move failed.
																			On success, All elements' indexes may be reassigned.

		mixed | null		remove(idx)										Returns the removed value, null if nonexistent.
																			On success, the specified index, idx, may be reassigned to another element.

		void				forEach(callback [, data])						Runs the callback for every element. 
																			The callback takes the following parameters:
																			callback(value, idx, list [, data])
																			The loop is done on a list clone, therefore changes
																			made to the list are not visible during the loop.

		array				array()											Returns the list as an Array object.

		string				CSV(separator)									Returns the list as a Comma Separated Values string, separated
																			by the mandatory argument 'separator'.
																			Makes sense only if list values are scalar values.

Possible extensions (only if really required):

		kigoList			sort(callback [, data])							Sorts the array using the callback() comparison function.
																			The callback received values of the two entries to compare, and must return negative, 
																			zero or positive values if the first entry is to be placed before, at same level, or after 
																			the second value. The optional data argument, if any, is also passed to the callback function.
																			A new instance is return, the original instance is unchanged.

*/




function	kigoList()	// [array]
{
	this.list = (arguments.length && kigo.is_array(arguments[0])) ? arguments[0] : [];
}

kigoList.createFromCSV = function(csv, separator)
{
	return new kigoList(
		(kigo.is_string(csv) && csv.length && kigo.is_string(separator)) ? csv.split(separator) : []
	);
}




/********************************************/
/* Manipulating */

// kigoList
kigoList.prototype.empty = function()
{
	this.list = [];
}

// int
kigoList.prototype.length = function()
{
	return this.list.length;
}

// bool
kigoList.prototype.find = function(value) // [,strict=false]
{
	return kigo.in_array(value, this.list, arguments.length > 1 && arguments[1]);
}

// int | null
kigoList.prototype.pos = function(value) // [,strict=false]
{
	if(arguments.length > 1 && arguments[1])
	{
		// strict
		for(var i = 0; i < this.list.length; i++)
		{
			if(this.list[i] === value)
				return i;
		}
	}
	else
	{
		// non-strict
		for(var i = 0; i < this.list.length; i++)
		{
			if(this.list[i] == value)
				return i;
		}
	}

	return null;
}


// bool | null
kigoList.prototype.isFirst = function(idx)
{
	return (idx >= 0 && idx < this.list.length) ? (idx ? false : true) : null;
}

// bool | null
kigoList.prototype.isLast = function(idx)
{
	return (idx >= 0 && idx < this.list.length) ? (idx == this.list.length - 1 ? true : false) : null;
}


// bool (private)
kigoList.prototype.exists = function(idx)
{
	return (kigo.is_int(idx) || kigo.is_string(idx)) && ''+kigo.intval(idx) == ''+idx && idx >= 0 && idx < this.list.length;
}


// mixed | null
kigoList.prototype.remove = function(idx)
{
	var	removed;

	if(!this.exists(idx))
		return null;

	removed = this.list[idx];

	for(var i = idx; i < this.list.length - 1; i++)
		this.list[i] = this.list[i+1];
	
	this.list.pop();

	return removed;
}

// idx
kigoList.prototype.add = function(value)
{
	return this.list.push(value) - 1;
}


// idx | null
kigoList.prototype.insertBefore = function(value, idx)
{
	if(idx == null)
		return this.add(value);

	if(!this.exists(idx))
		return null;

	for(i = this.list.length; i > idx; i--)
		this.list[i] = this.list[i-1];

	this.list[idx] = value;

	return idx;
}




// idx | null
kigoList.prototype.move = function(idx, where)
{
	if(
		!this.exists(idx) ||
		!this.exists(where)
	)
		return null;

	if(idx == where)
		return idx;

	var	i, tmp;

	if(where < idx)
	{
		tmp = this.list[idx];

		for(i = idx; i > where; i--)
			this.list[i] = this.list[i-1];

		this.list[where] = tmp;
	}
	else if(where > idx)
	{
		tmp = this.list[idx];

		for(i = idx; i < where; i++)
			this.list[i] = this.list[i+1];

		this.list[where] = tmp;
	}

	return where;
}

// mixed | null
kigoList.prototype.get = function(idx)
{
	if(!this.exists(idx))
		return null;

	return this.list[idx];
}

// mixed | null
kigoList.prototype.getFirst = function()
{
	return this.get(0);
}

// mixed | null
kigoList.prototype.getLast = function()
{
	return this.get(this.list.length - 1);
}


// void	
kigoList.prototype.forEach = function(callback) // [,data]
{
	// Work on a clone
	var	clone = this.list.slice(0);

	for(var i = 0; i < clone.length; i++)
		callback(clone[i], i, this, arguments[1]);
}




// kigoList
kigoList.prototype.sort = function(callback)	// [,data]
{
	// Work on a clone
	var	clone = this.list.slice(0);
	var	data = arguments.length > 1 ? arguments[1] : undefined;

	clone.sort(
		function(a, b)
		{
			return callback(a, b, data);
		}
	);

	return new kigoList(clone);
}



/********************************************/
/* Formatting */

kigoList.prototype.array = function()
{
	return this.list.slice(0);	// Return a clone
}

// This one makes sense only if values are scalar
kigoList.prototype.CSV = function(separator)
{
	return this.list.length ? this.list.join(separator) : '';
}

/* /js/kigoDate.js */ /* UTF8 COOKIE: éà */

/*
??/??/20??		Release #0
05/07/2010		Documented; Now throws exceptions on invalid dates.


Type	Returns				Method										Note

		kigoDate			kigoDate(day, month, year)					Creates an instance from day, month and year numeric values.
																		Throws exception on invalid values.

static	kigoDate			today()										Creates an instance holding current date (as provided by the browser)

static	kigoDate			createFromDate(date)						Creates an instance from native Date object.
																		Throws exception on invalid object.

static	kigoDate			createFromMysql(mysql)						Creates an instance from date string in MySQL format.
																		Throws exception on invalid format or date.

static	kigoDate			createFromCalendar(calendar)				Creates an instance from date string in calendar format.
																		Throws exception on invalid format or date.
																		OBSOLETED BY kigoDatePicker. DO NOT USE.

		kigoDate			clone()										Clones the instance.


		int					getDay()									Returns the day of month

		int					getMonth()									Returns the month

		int					getYear()									Returns the year


		string				mysql()										Returns the date as a mysql DATE string.

		string				display()									Returns the date in format suitable for calendar display.

		string				calendar()									Alias to display()
																		OBSOLETED BY kigoDatePicker. DO NOT USE.

		Date				date()										Returns the date as a native Date object.

		kigoDate			addYears(signed int years)					Returns new kigoDate instance.

		kigoDate			addMonths(signed int months)				Returns new kigoDate instance.

		kigoDate			addDays(signed int days)					Returns new kigoDate instance.

		kigoDate			firstMonthDay()								Returns new kigoDate instance with date set to the first month day.

		kigoDate			lastMonthDay()								Returns new kigoDate instance with date set to the last month day.

		signed int			daysDiff(kigoDate other)					Returns the difference, in number of days, between the given instance, and other instance
																		Returns < 0 if other < this; > 0 if other > this

		signed int			compare(kigoDate other)						Compares the given instance with other instance.
																		Returns -1 if other < this; 0 if other = this; 1 if other > this

static	kigoDate			min(date [, date [, date [, ... ] ] ])		Returns the smallest date (cloned). Only one date argument is mandatory

static	kigoDate			max(date [, date [, date [, ... ] ] ])		Returns the smallest date (cloned). Only one date argument is mandatory




// NOTE: Makes use of the Date object extended by "The DHTML Calendar"
// TODO: Get rid of obsolete methods ::display() and ::calendar().

*/

/***********************************************/
/* CONSTRUCTORS */

function	kigoDate(day, month, year)
{
	// 05/07/2010 - Validate the date and throw exception on error... 
	if(
		(this.day = parseInt(day, 10)) < 1 || this.day > 31 ||
		(this.month = parseInt(month, 10)) < 1 || this.month > 12 ||
		this.day < 1 || this.day > monthDays(this.month, this.year = parseInt(year, 10))
	)
		throw 'kigoDate::kigoDate(): invalid date';

	function	monthDays(month, year)
	{
		switch(month)
		{
			case 1: case 3: case 5: case 7: case 8: case 10: case 12:
				return 31;
			case 4: case 6: case 9: case 11:
				return 30;
			case 2:
				return ((year % 4 == 0) && ((!(year % 100 == 0)) || (year % 400 == 0))) ? 29 : 28;
			default:
				return 0;
		}
	}
}


kigoDate.today = function()
{
	return kigoDate.createFromDate(new Date());
}

kigoDate.createFromDate = function(date)
{
	if(date.constructor != Date.prototype.constructor)
		throw 'kigoDate::createFromDate(): invalid Date object';

	return new kigoDate(
		date.getDate(),
		date.getMonth()+1,
		date.getFullYear()
	);
}

kigoDate.createFromMysql = function(mysql)
{
	if(
		typeof(mysql) != 'string' ||
		mysql.length < 10 ||							// Tolerates mysql DATETIME string (that is, DATE followed by time)
		!mysql.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}/i)	// Rough format check, the constructor is doing a better one
	)
		throw 'kigoDate::createFromMysql(): invalid input';

	// 0123-56-89
	return new kigoDate(
		mysql.substr(8, 2),
		mysql.substr(5, 2),
		mysql.substr(0, 4)
	);
}

kigoDate.createFromCalendar = function(calendar)
{
	return kigoDate.createFromDate(Date.parseDate(calendar, '%a, %d %b %Y'));
}

kigoDate.prototype.clone = function()
{
	return new kigoDate(this.day, this.month, this.year);
}



/***********************************************/
/* FRIENDS */

kigoDate.isInstance = function(i)
{
	return kigo.is_object(i) && i.constructor === kigoDate;
}


/***********************************************/
/* HELPERS */

kigoDate.prototype.getDay = function()
{
	return this.day;
}

kigoDate.prototype.getMonth = function()
{
	return this.month;
}

kigoDate.prototype.getYear = function()
{
	return this.year;
}


/***********************************************/
/* FORMATTING */


kigoDate.prototype.mysql = function()
{
	function	pad(number, digits)
	{
		number = ''+number;
		while(number.length < digits)
			number = '0' + number;
		return number;
	}

	// Returns date string suitable for MySQL
	return pad(this.year, 4)+'-'+pad(this.month, 2)+'-'+pad(this.day, 2);
}


kigoDate.prototype.display = function()
{
	// Returns date string suitable for Kigo display (
	return this.date().print('%a, %d %b %Y');
}

// ALIAS
kigoDate.prototype.calendar = function()
{
	return this.display();
}

kigoDate.prototype.date = function()
{
	// Returns the native Date object instance
	//return Date.parseDate(this.mysql(), '%Y-%m-%d');
	var	d = Date.parseDate(this.mysql(), '%Y-%m-%d');
	d.setHours(0, 0, 0, 0);
	return d;
}


/***********************************************/
/* MANIPULATING */

/*

	All the methods below return a NEW object.
	They do NOT alter the original object

*/


kigoDate.prototype.addYears = function(years)
{
	/*
		Implementation compatible with MySQL's "+/- INTERVAL x YEAR",
		and therefore compatible with server-side kigoDate
	*/

	years = kigo.intval(years);

	// Straightforward, except when dealing with february...
	if(this.month != 2)
		return new kigoDate(this.day, this.month, this.year + years);

	var	year = this.year + years;

	return new kigoDate(Math.min(this.day, (new kigoDate(1, this.month, year)).date().getMonthDays()), this.month, year);
}

/*

This one gives unwanted results.
Ie: 
	2009-05-31 - 1 month = 2009-05-01
	2009-05-31 - 2 months = 2009-03-31

kigoDate.prototype.addMonths = function(months)
{
	var	d = this.date();

	d.setMonth(d.getMonth() + months);

	return kigoDate.createFromDate(d);
}
*/

kigoDate.prototype.addMonths = function(months)
{
	/*
		Implementation compatible with MySQL's "+/- INTERVAL x MONTH",
		and therefore compatible with server-side kigoDate
	*/

	var	month, year;

	months = kigo.intval(months);

	if( (month = this.month - 1 /* 0-11 for computations */ + months) >= 0)
	{
		year = this.year + Math.floor(month / 12);
		month = (month%12) + 1; 
	}
	else
	{
		year = this.year - 1 - Math.floor( (Math.abs(month)-1) / 12);
		month = 12 - (Math.abs(month)-1)%12;
	}

	return new kigoDate(Math.min(this.day, (new kigoDate(1, month, year)).date().getMonthDays()), month, year);
}

kigoDate.prototype.addDays = function(days)
{
	var	d = this.date();

	d.setDate(d.getDate() + kigo.intval(days));

	return kigoDate.createFromDate(d);

}

kigoDate.prototype.firstMonthDay = function()
{
	return new kigoDate(1, this.month, this.year);
}

kigoDate.prototype.lastMonthDay = function()
{
	return new kigoDate(this.date().getMonthDays(), this.month, this.year);
}


// < 0 if other < this; > 0 if other > this
kigoDate.prototype.daysDiff = function(other)
{
	var	current = this.date();
	var	other = other.date();

	return Math.round((other.getTime() - current.getTime()) / 86400000);	// 24 hours x 60 minutes x 60 seconds x 1000 milliseconds

	/*
	Why doesn't this work???

	// Avoid dealing with timezone problem by rounding the result...
	return Math.round(
		(
			Date.UTC(other.getYear(), other.getMonth(), other.getDay(), 12, 0, 0, 0) - 
			Date.UTC(this.year, this.month, this.day, 12, 0, 0, 0)
		) / 
		86400000 // 24 hours x 60 minutes x 60 seconds x 1000 milliseconds
	);
	*/
}

// -1 if other < this; 1 if other > this
kigoDate.prototype.compare = function(other)
{
	var	diff;

	if(
		(diff = other.getYear() - this.year) ||
		(diff = other.getMonth() - this.month) ||
		(diff = other.getDay() - this.day)
	)
		return diff;

	return 0;
}


kigoDate.min = function(date)
{
	if(!kigoDate.isInstance(date))
		throw 'kigoDate::min(): invalid date';

	var	current = date;

	for(var i = 1; i < arguments.length; i++)
	{
		if(!kigoDate.isInstance(arguments[i]))
			throw 'kigoDate::min(): invalid date';

		if(current.compare(arguments[i]) < 0)
			current = arguments[i];
	}

	return current.clone();
}

kigoDate.max = function(date)
{
	if(!kigoDate.isInstance(date))
		throw 'kigoDate::max(): invalid date';

	var	current = date;

	for(var i = 1; i < arguments.length; i++)
	{
		if(!kigoDate.isInstance(arguments[i]))
			throw 'kigoDate::max(): invalid date';

		if(current.compare(arguments[i]) > 0)
			current = arguments[i];
	}

	return current.clone();
}

/* /js/kigoVal.js */ /* UTF8 COOKIE: éà */



var	kigoVal = {

	
	/**************************************************************************************************************/
	/* PUBLIC  */


	'CHR'			:	function(value, len)
	{
		return (value = kigo.trim(value)).length == len;
	},

	'CHRCONTENT'	:	function(value, pool, len)
	{
		return kigoVal.CHR((value = kigo.trim(value)), len) && kigoVal.STRCONTENT(value, pool);
	},

	'VCHR'			:	function(value, max) // [, min=0 ]
	{
		var	min = arguments.length < 3 ? 0 : arguments[2];
		var	len = (value = kigo.trim(value)).length;

		return len <= max && len >= min;
	},

	'VCHRCONTENT'	:	function(value, pool, max) // [, min=0 ] 
	{
		return kigoVal.VCHR((value = kigo.trim(value)), max, arguments.length < 3 ? 0 : arguments[3]) && kigoVal.STRCONTENT(value, pool);
	},



	'INT8'			:	function(value) // [, min [, max]]
	{
		return kigoVal.INTX(value, 0xFF, arguments.length > 1 ? arguments[1] : null, arguments.length > 2 ? arguments[2] : null);
	},

	'INT16'			:	function(value) // [, min [, max]]
	{
		return kigoVal.INTX(value, 0xFFFF, arguments.length > 1 ? arguments[1] : null, arguments.length > 2 ? arguments[2] : null);
	},

	'INT31'			:	function(value) // [, min [, max]]
	{
		return kigoVal.INTX(value, 0x7FFFFFFF, arguments.length > 1 ? arguments[1] : null, arguments.length > 2 ? arguments[2] : null);
	},


	'BOOL' 			: function(value)
	{
		if(	
			value === true ||
			value === 1 ||
			value === '1' 
		)
			value = true;
		else if(
			value === false ||
			value === 0 ||
			value === '0'
		)
			value = false;
		else
			return false;

		return true;
	},
	

	'EMAIL'			:	function(value) // [, mandatory=true ]
	{
		var	mandatory = arguments.length < 2 ? true : (arguments[1] ? true : false);
		var value = kigo.trim(value);
		if(
			!mandatory &&
			!value.length
		)
			return true;

		if(!kigoVal.VCHR(value, kigo.CFG.MISC.MAXLEN_EMAIL, 0))	// Limit to 200 characters
			return false;

		// http://atranchant.developpez.com/code/validation/index.php
		// Validation RFC1035 & RFC2822

		var atom   = '[-a-z0-9!#$%&\'*+\\/=?^_`{|}~]';		// caractères autorisés avant l'arobase
		var domain = '([a-z0-9]([-a-z0-9]*[a-z0-9]+)?)';	// caractères autorisés après l'arobase (nom de domaine)

		var regex = '^' + atom + '+' +						// Une ou plusieurs fois les caractères autorisés avant l'arobase
		'(\\.' + atom + '+)*' +								// Suivis par zéro point ou plus
															// séparés par des caractères autorisés avant l'arobase
		'@' +												// Suivis d'un arobase
		'(' + domain + '{1,63}\\.)+' +						// Suivis par 1 à 63 caractères autorisés pour le nom de domaine
															// séparés par des points
		domain + '{2,63}$';									// Suivi de 2 à 63 caractères autorisés pour le nom de domaine

		return (new RegExp(regex, 'i')).test(value) ? true : false;
	},
	
	'USERNAME'				:	function(value) 
	{
		return kigoVal.VCHRCONTENT(value, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_', kigo.CFG.ACCOUNT.USERNAME_MAX_CHARS, kigo.CFG.ACCOUNT.USERNAME_MIN_CHARS);
	},
	
	'PASSWORD'				:	function(value) 
	{
		return kigoVal.VCHR(value, 100, kigo.CFG.ACCOUNT.PASSWORD_MIN_CHARS);
	},
	
	
	'HOUR'					:	function(value)  
	{
		return kigoVal.INT8(value, 0,23);
	},	
	
	'MINUTE'				:	function(value)  
	{
		return kigoVal.INT8(value, 0,59);
	},		
	
	'AMOUNT'				: 	function(value)	// [, allowNegative=true [, allowEmpty=true ] ]
	{
		var	allowNegative = arguments.length > 1 ? arguments[1] : true;
		var	allowEmpty = arguments.length > 2 ? arguments[2] : true;

		// - Remove the ' separator
		// - Replace ',' by '.'
		value = kigo.trim(value);		
		
		value = value.split('\'').join('');
		value = value.split(',').join('.');
		
		if(!kigoVal.STRCONTENT(value, '0123456789.' + (allowNegative ? '-' : '') ))		
			return false;
		var tmp = value.split('.');
		var s = tmp.length;

		if(s == 1)
		{
			// Only decimal part...
			var decimal = tmp[0];
			var float = null;
		}
		else if(s == 2)
		{
			var decimal = tmp[0];
			var float = tmp[1];
		}
		else
			return false;

		if (decimal.length && decimal.substring(0,1) == '-')
		{
			var negative = true;
			decimal = decimal.substr(1,decimal.length -1);
		}
		else 		
			var negative = false;

		// Okay, the "decimal" part may store up to 8 digits, while the "float" part may store up to 2 digits

		if(	decimal.length > 8 ||
			(float !== null && float.length > 2)
		)
			return false;
	
		return true;
	},

	'GMAPCOORD'			:	function(value) // [, mandatory=true ]
	{
		// [-]XXX.XXXXXXXXXXXXXXX
		var tmp = value.split('.');
		var s = tmp.length;
		
		if( s == 1)
		{
			// Only decimal part...
			decimal = tmp[0];
			float = null;
		}
		else if(s == 2)
		{
			decimal = tmp[0];
			float = tmp[1];

		}
		else
			return false;


		if( decimal.length && decimal.substring(0,1) == '-')
		{
			var negative = true;
			decimal = decimal.substr(1,decimal.length -1);
		}
		else
			var negative = false;

		if(!kigoVal.INT8(decimal, 0, 180))
			return false;
		
		if(float === null)
		{			
			return false;
		}

		// treat float as a string...
		if(!kigoVal.VCHRCONTENT(float, '0123456789', 20, 1))
			return false;

		return true;
		
	},
	
	'GMAPZOOM'			:	function(value) 
	{	
		return kigoVal.INT8(value, 0, 23);
	},
	
	'GANALYTICS'			:	function(value) // [, mandatory=true ]
	{
		var	mandatory = arguments.length < 2 ? true : (arguments[1] ? true : false);
		var value = kigo.trim(value);
		if(
				!mandatory &&
				!value.length
			)
				return true;

		if(!kigoVal.VCHR(value, kigo.CFG.WS.GOOGLE_ANALYTICS_MAX_LENGTH, kigo.CFG.WS.GOOGLE_ANALYTICS_MIN_LENGTH))
			return false;
						
		var googleCodeFilter = /^UA-[0-9]+-[0-9]+$/i;

		if(!value.match(googleCodeFilter))
			return false;
			
		return true;
	},

	'URL'			:	function(value) // [, mandatory=true ] 
	{	
		var	mandatory = arguments.length < 2 ? true : (arguments[1] ? true : false);
		var value = kigo.trim(value);
		var	https;

		if(
			!mandatory &&
			!value.length
		)
			return true;

		if(!kigoVal.VCHR(value, kigo.CFG.MISC.MAXLEN_URL, 0))	// Limit to 200 characters
			return false;
		
		if(value.substr(0, 7).toLowerCase() == 'http://')	
		{		
			https = false;
			value = value.substr(7,value.length-1);
		}
		else if(value.substr(0, 8).toLowerCase() == 'https://')
		{
			https = true;
			value = value.substr(8,value.length-1);
		}
		else
			https = false;

		if(!value.length)
		{
			if(mandatory)
				return false;
			return true;
		}

		// Okay, check if the rest looks like valid url

		var exp = /^[a-z0-9-]+(\.[a-z0-9-]+)+(:[0-9]+)?(\/.*)?$/i;
		
		if(!exp.test(value))
			return false;
	
		return true;
	},	

	'KIGO_DOMAIN'			:	function(value)
	{
		// Rules: a-z, 0-9, cannot start with a digit
		var exp = /^([a-z0-9]([-a-z0-9]*[a-z0-9]+)?)$/i;
	
		if(
			!kigoVal.VCHR(value, kigo.CFG.WS.CFG_KIGODOMAIN_MAXLEN, kigo.CFG.WS.CFG_KIGODOMAIN_MINLEN) ||
			!exp.test(value)
		)
			return false;
	
		return true;
	},

	'DOMAIN'			:	function(value)
	{
		// see php definition
		var rgxpDomain = "([a-z0-9]([-a-z0-9]*[a-z0-9]+)?)";
		var exp = new RegExp('^(' + rgxpDomain + '{1,63}\.)+' + rgxpDomain + '{2,63}$','i');
	
		if(
			!kigoVal.VCHR(value, kigo.CFG.WS.CFG_CUSTOMDOMAIN_MAXLEN, kigo.CFG.WS.CFG_CUSTOMDOMAIN_MINLEN) ||
			!exp.test(value)
		)
			return false;
	
		return true;
	},
	
	'REQKEY'			:	function(value)
	{
		return kigoVal.CHRCONTENT(value, '0123456789abcdef', 32);
	},
	
	/**************************************************************************************************************/
	/* PUBLIC / PRIVATE  */


	'STRCONTENT'	:	function(value, pool)
	{
		var len = (value = kigo.trim(value)).length;

		for(var i = 0; i < len; i++)
		{
			if(pool.indexOf(value.substr(i, 1)) == -1)
				return false;
		}

		return true;
	},


	'INTX'			:	function(value, Xmax, min, max)
	{
		if(!kigoVal.VCHRCONTENT(value, '0123456789', 99, 1))
			return false;

		min = (min == null) ? 0 : Math.min(Xmax, Math.max(0, min));
		max = (max == null) ? Xmax : Math.max(1, Math.min(Xmax, max));

		return (value = parseInt(kigo.trim(value), 10)) >= min && value <= max;
	}

};

/* /js/kigoPager.js */ /* UTF8 COOKIE: éà */

/*

kigoPager(el, text, itemsPerPage, callback)
int page()
int perPage()
updateFromReply(result)
setPage(newPage)
writeRequest(po) // [resetPage; default: false]


*/



function	kigoPager(el, text, itemsPerPage, callback)
{
	this.containers = null;

	if(typeof(callback) != 'function')
		return;

	if(!kigo.is_array(el))
		el = [ el ];

	this.containers = [];

	for(var i = 0; i < el.length; i++)
	{
		this.containers[i] = {
			'el'		:	null,
			'sel'		:	null,
			'total'		:	null,
			'prevLink'	:	null,
			'nextLink'	:	null
		};

		if(!(this.containers[i].el = vkDom.el(el[i])))
		{
			this.containers = null;
			return;
		}
	}

	this.itemsPerPage = itemsPerPage;
	this.callback = callback;
	this.currentTotal = 0;
	this.currentPage = 1;
	this.currentPages = 0;

	// Okay, prepare the container...
	this._init(text);
}


kigoPager.prototype._init = function(text)
{
	var	self = this;
	var	sel, span;

	
	function	createSpan(text)	// [, class]
	{
		var	span = document.createElement('span');

		if(arguments.length > 1)
			span.className = arguments[1];

		span.appendChild(document.createTextNode(text));

		return span;
	}


	function	createLink(text, callback)	// [, class]
	{
		var	a = document.createElement('a');
		a.href = '#';
		a.onclick = callback;
		a.appendChild(document.createTextNode(text));

		if(arguments.length > 2)
			a.className = arguments[2];

		return a;
	}


	// Build the pager(s)
	for(var i = 0; i < this.containers.length; i++)
	{
		vkDom.addClass(this.containers[i].el, 'pager');
		vkDom.clean(this.containers[i].el);

		this.containers[i].el.appendChild(createSpan(text, 'label'));
		this.containers[i].el.appendChild(sel = document.createElement('select'));
		this.containers[i].sel = new kigoSelect(sel);

		sel.onchange = function()
		{
			self.setPage(this.selectedIndex+1);
		}

		this.containers[i].el.appendChild(createSpan('/', 'outof'));
		this.containers[i].el.appendChild(this.containers[i].total = createSpan('', 'total'));
		

		this.containers[i].el.appendChild(
				this.containers[i].prevLink = createLink(
				'< Previous',
				function()
				{
					if(!this.disabled)
					{
						//self.setPage(self.currentPage-1);
						// 18/11/2009 - Take changesDetected into account

						if(
							typeof(window['changesDetected']) != 'undefined' &&
							window['changesDetected']
						)
							askExitWithoutSaving(function() { self.setPage(self.currentPage-1) });
						else
							self.setPage(self.currentPage-1);
					}
					return false;
				},
				'prev'
			)
		);

		this.containers[i].el.appendChild(createSpan('|', 'sep'));

		this.containers[i].el.appendChild(
			this.containers[i].nextLink = createLink(
				'Next >',
				function()
				{
					if(!this.disabled)
					{
						//self.setPage(self.currentPage+1);
						// 18/11/2009 - Take changesDetected into account
						if(
							typeof(window['changesDetected']) != 'undefined' &&
							window['changesDetected']
						)
							askExitWithoutSaving(function() { self.setPage(self.currentPage+1) });
						else
							self.setPage(self.currentPage+1);
					}
					return false;
				},
				'next'
			)
		);
	}

}


kigoPager.prototype.page = function()
{
	return this.currentPage;
}

kigoPager.prototype.perPage = function()
{
	return this.itemsPerPage;
}

// 27/07/2009
kigoPager.prototype.total = function()
{
	return this.currentTotal;
}

kigoPager.prototype.updateFromReply = function(result)
{
	var	self=this;

	function	disableContainers()
	{
		for(var i = 0; i < self.containers.length; i++)
			vkDom.addClass(self.containers[i].el, 'disabled');
	}

	// Disable the pagers if:
	// - the result is not an object
	// - there are no pages...

	if(
		typeof(result['PAGER']) == 'undefined' ||
		!(result = result.PAGER)
	)
	{
		this.currentPages = 0;
		this.currentTotal = 0;
		return disableContainers();
	}

	this.currentPages = result.PAGES;
	this.currentTotal = result.TOTAL;

	if(
		!result.TOTAL || 
		result.PAGES == 1
	)
		return disableContainers();


	for(var i = 0; i < this.containers.length; i++)
	{

		this.containers[i].sel.reset();

		if(this.itemsPerPage > 1)
		{
			for(var j = 1; j <= result.PAGES; j++)
			{
				var	from = ((j - 1) * this.itemsPerPage) + 1;
				var	to = Math.min(j * this.itemsPerPage, result.TOTAL);

				if(from == to)
					this.containers[i].sel.addOption(j, from);
				else
					this.containers[i].sel.addOption(j, from+'-'+to);
			}
		}
		else
		{
			for(var j = 1; j <= result.PAGES; j++)
				this.containers[i].sel.addOption(j, j);
		}

		this.containers[i].sel.setValue(result.PAGE);

		vkDom.setText(this.containers[i].total, result.TOTAL);

		if(result.PAGE == 1)
		{
			this.containers[i].prevLink.disabled = true;
			vkDom.addClass(this.containers[i].prevLink, 'disabled');
		}
		else
		{
			this.containers[i].prevLink.disabled = false;
			vkDom.removeClass(this.containers[i].prevLink, 'disabled');
		}

		if(result.PAGE == result.PAGES)
		{
			this.containers[i].nextLink.disabled = true;
			vkDom.addClass(this.containers[i].nextLink, 'disabled');
		}
		else
		{
			this.containers[i].nextLink.disabled = false;
			vkDom.removeClass(this.containers[i].nextLink, 'disabled');
		}

		vkDom.removeClass(this.containers[i].el, 'disabled');
	}
}

kigoPager.prototype.setPage = function(newPage)
{
	this.currentPage = newPage;

	if(this.currentPage < 1)
		this.currentPage = 1;
	else if(this.currentPage > this.currentPages)
		this.currentPage = this.currentPages;

	this.callback(this.currentPage);
}

kigoPager.prototype.writeRequest = function(po) // [resetPage; default: false]
{
	po.PAGER = {
		'PERPAGE'	:	this.itemsPerPage,
		'PAGE'		:	(arguments.length > 1 && arguments[1] == true) ? 1 : this.currentPage
	};
}

/* /js/kigoCalendar.js */ /* UTF8 COOKIE: éa */




/**
 * Retrieve the absolute coordinates of an element.
 *
 * @param element
 *   A DOM element.
 * @return
 *   A hash containing keys 'x' and 'y'.
 */
function getAbsolutePosition(element) {
  var r = { x: element.offsetLeft, y: element.offsetTop };
  if (element.offsetParent) {
    var tmp = getAbsolutePosition(element.offsetParent);
    r.x += tmp.x;
    r.y += tmp.y;
  }
  return r;
};

/**
 * Retrieve the coordinates of the given event relative to the center
 * of the widget.
 *
 * @param event
 *   A mouse-related DOM event.
 * @param reference
 *   A DOM element whose position we want to transform the mouse coordinates to.
 * @return
 *    A hash containing keys 'x' and 'y'.
 */
function getRelativeCoordinates(event, reference) {
  var x, y;
  event = event || window.event;

  var el = event.target || event.srcElement;
    
  if (!window.opera && typeof event.offsetX != 'undefined') {
    // Use offset coordinates and find common offsetParent
    var pos = { x: event.offsetX, y: event.offsetY };

    // Send the coordinates upwards through the offsetParent chain.
    var e = el;
    while (e) {
      e.mouseX = pos.x;
      e.mouseY = pos.y;
      pos.x += e.offsetLeft;
      pos.y += e.offsetTop;
      e = e.offsetParent;
    }

    // Look for the coordinates starting from the reference element.
    var e = reference;
    var offset = { x: 0, y: 0 }
    while (e) {
      if (typeof e.mouseX != 'undefined') {
        x = e.mouseX - offset.x;
        y = e.mouseY - offset.y;
        break;
      }
      offset.x += e.offsetLeft;
      offset.y += e.offsetTop;
      e = e.offsetParent;
    }

    // Reset stored coordinates
    e = el;
    while (e) {
      e.mouseX = undefined;
      e.mouseY = undefined;
      e = e.offsetParent;
    }
  }
  else {
    // Use absolute coordinates
	  kigoDebug.text('absolute calculus');
    var pos = getAbsolutePosition(reference);
    x = event.pageX  - pos.x;
    y = event.pageY - pos.y;
  }
  // Subtract distance to middle
  return { x: x, y: y };
}



/*
 * 
 * @see details about rendering described after this function.
 * 
 * @param data { This info is used in the displaying of tool tips and additional informations
	'i'		:	Property Id
	'p'		:	Property Name
	'f'		:	Property Photo
	'a'		:	Property Address
	'o'		:	Property Owner ? (Check accuracy)
	'r'		:	reservations information. Set of reservations of the property.
	}			
 *	@param searchDate {
    's'		:	Start date
    'e'		:   End date
    }
 *  @param limits {
	'p'		:   Prior resevation for the entire group
	'e' 	: 	Next reservation for the group
    }
 *    
 */
function	kigoCalendar(startMonth, startYear, endMonth, endYear, datas, searchDate, limits)
{
	this.datas				=	{
			'i'		:	datas.i,
			'p'		:	datas.p,
			'f'		:	datas.f,
			'a'		:	datas.a,
			'o'		:	datas.o
	};
	this.sm					=	startMonth			;
	this.sy					=	startYear			;
	this.em					=	endMonth			;
	this.ey					=	endYear				;
	this.res				=	datas.r				;
	this.nbMonth									;
	this.resTooltips		=	new Array()			;
	this.areaTooltips		=	new Array()			;
	this.md					=	new Array()			;
	this.stack				=	new Array()			; 
	this.length										;
	this.onReservationClick	=	null				;
	this.onSearchClick		=	null				;
	this.onAvailableClick	=	null				;
	this.parent				=	null				;
	this.limits				=	limits	||	null	;
	this.lim				=	0					;
	this.search				=	null				;


	this.div = document.createElement('div');

	if(searchDate != null)
	{
		var searchStart		=	new Date.parseDate(searchDate.s, '%Y-%m-%d');
		var searchEnd		=	new Date.parseDate(searchDate.e, '%Y-%m-%d');
		var newResTab		=	new Array();
		var calStart		=	new Date(this.sy, this.sm - 1	);
		var calEnd			=	new Date(this.ey, this.em		);
		var isOk			=	true;
		
		if(this._compareDate(calStart, searchEnd) >0  || this._compareDate(searchStart, calEnd) >0)
		{
			isOk				=	false;
			this.onSearchClick	=	null;
		}

		if(isOk)
		{
			searchDate.t		=	{'n' : 'Your search' };
			if(this.res.length == 0){
				this.search = newResTab.length;
				newResTab.push(searchDate);
			}
			else{
				var added = false; 
				for(var nbr=0; nbr<this.res.length; nbr++)
				{
					if(!added){
						var resStart	=	new Date.parseDate(this.res[nbr].s, '%Y-%m-%d');
						if(this._compareDate(searchEnd, resStart) <=0){
							this.search	=	newResTab.length;
							newResTab.push(searchDate);
							added = true;
						}
					}
					newResTab.push(this.res[nbr]);
					if(!added && nbr == this.res.length-1){
						this.search	=	newResTab.length;
						newResTab.push(searchDate);
					}
				}
			}
			this.res	=	newResTab;
		}
	}
}

kigoCalendar.bei = 1;

/*
 * Calendar rendering
 * 
 * The calendar renders it's days and month labels plus a container with the availabilities.
 * 
 * The calendar reservation info is composed out of a series of divs.
 * Reservations, hold dates and available time. 
 * 
 * The availability area divs are displayed as blocks floating left. Their width and height is computed
 * based on the width and height of the day specified in the css. (See dump function for details).
 * 
 * 
 */

/*
 * Determines class for reservation div
 */
kigoCalendar.prototype._getClass = function(m, c)
{
	if((parseInt(m) != 0 && parseInt(m) != 1) || (parseInt(c) != 0 && parseInt(c) != 1)) return 's';
	else{
		m	=	parseInt(m);
		c	=	parseInt(c);
		if(m == c)
		{
			if(m == 0) 	return 'oh'; // other hold
			else		return 'mc'; // me confirmed
		}
		else if(m == 0)	return 'oc'; // other confirmed
		else			return 'mh'; // me confirmed
	}
}
/*
 * Date function helper
 */
kigoCalendar.prototype._getDaysBetween = function(d1, d2)
{
	var days;
	var d, m ,y;
	if(this._compareDate(d1, d2) == -1)	return	Math.round((d2.getTime() - d1.getTime()) / (1000*60*60*24));
	else								return	Math.round((d1.getTime() - d2.getTime()) / (1000*60*60*24));
}
/*
 * Date function helper
 */
kigoCalendar.prototype._compareDate = function(d1, d2)
{
	if		(d1.getYear() <  d2.getYear()																	)	return -1;
	else if	(d1.getYear() == d2.getYear() && d1.getMonth() <  d2.getMonth()									)	return -1;
	else if	(d1.getYear() == d2.getYear() && d1.getMonth() == d2.getMonth() && d1.getDate() <  d2.getDate()	)	return -1;
	else if	(d1.getYear() == d2.getYear() && d1.getMonth() == d2.getMonth() && d1.getDate() == d2.getDate()	)	return  0;
	else																										return  1;
}
/*
 * Compute the width of an availability element (reservetions, holds, free time).
 * It is computed based on the stack which represents how many days to exapnd the div, 5 more days left until end of month, then div should be extended to day.width * 5px.
 */
kigoCalendar.prototype._getWidth 				= 	function(){ return (this.length < this.stack[0]) ? this.length : this.stack[0]; }
/*
 * Dom helper
 */
kigoCalendar.prototype._createBlock				=	function(width, className)
{
	var b					 =	this.div.cloneNode(false);
	b.style.width			 =	width*this.daySize	+'px';
	b.className				 =	className;
	return b;
}
/*
 * Creates the first availability element if it is an availability
 */
kigoCalendar.prototype._createFirstArea			=	function(dateStart, dateEnd)
{
	var area;
	var self	=	this;
	this.length	=	this._getDaysBetween(dateStart, dateEnd);
	
	var i = 0;
	
	while(this.length != 0 && this.stack.length != 0)
	{
		area	=	this._createBlock(this._getWidth(), 'n');
		this.parent.appendChild(area);
		this._updateLengthAndStack(this._getWidth());
		
		var month = dateStart.getMonth();
		
		if (typeof(this.onAvailableClick) == 'function') 
		{
			area.modifiedMonth = month+i;
			area.onclick 	 = function(event){
				
				var e = event || window.event;
				var pos = getRelativeCoordinates(event, area.parentNode);
				var day = Math.ceil(pos.x/self.daySize);
				
				if (day==0) day = 1;
				
				var modifiedDate = new Date(
						dateStart.getFullYear() + (Math.floor(this.modifiedMonth/12))
						, (this.modifiedMonth%12)
						, day);
				
				self.onAvailableClick(self.datas,modifiedDate.print('%a, '+ day +' %b %Y')); return false;

				
			}
			area.className	+= ' clickable';
		}
		if (this.limits != null && typeof(this.limits) == 'object')
		{
			kigoToolTips.add(area, this.areaTooltips[0]);
		}
		i++;
		
	}
	this.length++;
	this._createBlockEnd('n', this._getClass(this.res[0].m, this.res[0].c), null, 0);
	if(this.lim != null)	this.lim++;
}
/*
 * Creates the first availability element if it is a reservation
 */
kigoCalendar.prototype._createFirstReservation	=	function(numRes, resStart, resEnd)
{
	var res;
	var self = this;
	this.length		=	this._getDaysBetween(resStart, resEnd);
	while(this.length != 0 && this.stack.length != 0)
	{
		res	=	this._createBlock(this._getWidth(), this._getClass(this.res[numRes].m, this.res[numRes].c));
		if (typeof(this.onReservationClick) == 'function' && this.res[numRes].i != null)
		{
			res.onclick 	 = function(){ self.onReservationClick(parseInt(self.res[numRes].i, 10)); return false; }
			res.className	+= ' clickable';
		}
		else if (typeof(this.onSearchClick) == 'function' && this.search == numRes) 
		{
			res.onclick 	= function(){ self.onSearchClick(self.datas); return false; }
			res.className	+= ' clickable';
		}
		this.parent.appendChild(res);
		if (this.res[0].t != null && typeof(this.res[0].t) == 'object')
		{
			kigoToolTips.add(res, this.resTooltips[0]);
		}
		this._updateLengthAndStack(this._getWidth());
	}
}
/*
 * Creates availability element of the available type used after the first element has been generated.
 */
kigoCalendar.prototype._createFreeArea			=	function(dateStart, dateEnd)
{
	var area;
	var self	=	this;
	this.length	=	this._getDaysBetween(dateStart, dateEnd)-1;		
	
	// prior reservation ended at end of the month
	if (parseInt(dateStart.print('%d')) == dateStart.getMonthDays()) {	
		var i = 1;
	} else {
		var i = 0;
	}
			
	while(this.length != 0 && this.stack.length != 0)
	{
		area	=	this._createBlock(this._getWidth(), 'n');
		this.parent.appendChild(area);
		this._updateLengthAndStack(this._getWidth());
		
		
		var month = dateStart.getMonth();
		
		if (typeof(this.onAvailableClick) == 'function') 
		{
			area.modifiedMonth = month+i;
			area.onclick 	 = function(event){ 
				
				var e = event || window.event;
				var pos = getRelativeCoordinates(event, area.parentNode);
								
				var day = Math.ceil(pos.x/self.daySize);
				
				if (day==0) day = 1;
				
				var modifiedDate = new Date(
						dateStart.getFullYear() + (Math.floor(this.modifiedMonth/12))
						, (this.modifiedMonth%12)
						, day);
				
				self.onAvailableClick(self.datas,modifiedDate.print('%a, '+ day +' %b %Y')); return false;
								
			}
			area.className	+= ' clickable';
		}
		
		i++;
		
		if(this.limits != null)
		{
			kigoToolTips.add(area, this.areaTooltips[this.lim]);
		}
	}
}
/*
 * Creates availability element of the reservation type used after the first element has been generated.
 */
kigoCalendar.prototype._createReservation		=	function(numRes, resStart, resEnd)
{
	var res;
	var self 	= this;
	this.length	=	this._getDaysBetween(resStart, resEnd)-1;
		
	while(this.length != 0 && this.stack.length != 0)
	{
		res	=	this._createBlock(this._getWidth(), this._getClass(this.res[numRes].m, this.res[numRes].c));
		if (typeof(this.onReservationClick) == 'function' && this.res[numRes].i != null) 
		{
			res.onclick = function(){ self.onReservationClick(parseInt(self.res[numRes].i, 10)); return false; }
			res.className	 += ' clickable';
		}
		else if (typeof(this.onSearchClick) == 'function' && this.search == numRes) 
		{
			res.onclick 	= function(){ self.onSearchClick(self.datas); return false; }
			res.className	+= ' clickable';
		}
		this.parent.appendChild(res);
		if (this.res[numRes].t != null && typeof(this.res[numRes].t) == 'object')
		{
			kigoToolTips.add(res, this.resTooltips[numRes]);
		}
		this._updateLengthAndStack(this._getWidth());
	}
}
/*
 * Creates a separation element between availability elements.
 * This element is a div that contains 2 othere divs each with the classes coresponding to the previous and next
 * availibility elements.
 *                 		   |---                           Separation element                           ---|
 * [Free time{class:cn1}] [ (Free time end block{class:'e'+cn1}) (Reservation start block{class:'b'+cn2}) ] [Reservation{class:cn2}]
 * 
 * @param cn1 prior element class
 * @param cn2 next element class
 * @param r1 prior reservation info
 * @param r2 next reservation info
 * 
 */
kigoCalendar.prototype._createBlockEnd			=	function(cn1, cn2, r1, r2)
{
	
		
	if(r1 != null || r2 != null){
		var date	=	(r1 == null)	?	new Date.parseDate(this.res[r2].s, '%Y-%m-%d')	:	new Date.parseDate(this.res[r1].e, '%Y-%m-%d');
	}
	
	var block;
	var self 	=	this;
	
	var area1	=	document.createElement('div')	;
	area1.className	+=	' e' +cn1+ ' ';
	
	var area2	=	document.createElement('div')	;
	area2.className	+=	' b' +cn2+ ' ';
	
	area1.style.width	= area2.style.width	=	(this.daySize/2) +'px';
	
	this._updateLengthAndStack(1);
	block	=	this._createBlock(1, 'start_end_block');
	block.style.display = "inline";
	//block.style.cssFloat = block.style.styleFloat = "left";	
	
	if(r1 != null && typeof(this.onReservationClick) == 'function' && this.res[r1].i != null)
	{
		area1.onclick = function(){ kigoDebug.text('reserv'); self.onReservationClick(parseInt(self.res[r1].i, 10)); return false; }
		area1.className	 += ' clickable';
	}
	else if (typeof(this.onSearchClick) == 'function' && this.search != null) 
	{
		if(this.search == r1){
		area1.onclick 	= 	function(){ kigoDebug.text('search'); self.onSearchClick(self.datas); return false; }
		area1.className	+= 	' clickable';
		}
	}
	else if (typeof(this.onAvailableClick) == 'function' && cn1 == 'n') 
	{
		area1.onclick 	 = function(event){ 
		
			var e = event || window.event;
			var pos = getRelativeCoordinates(event, area1.parentNode.parentNode);
			var day = Math.ceil(pos.x/self.daySize);					
			self.onAvailableClick(self.datas,date.print('%a, '+ day +' %b %Y')); 
			return false;
			
		}
		area1.className	+= ' clickable';
	}
	if(r1 != null && this.res[r1].t != null && typeof(this.res[r1].t) == 'object')
	{
		kigoToolTips.add(area1, this.resTooltips[r1]);
	}
	if(cn1 == 'n' && typeof(this.limits) == 'object' && this.limits != null )
	{
		kigoToolTips.add(area1, this.areaTooltips[this.lim]);
	}
	
	if(r2 != null && typeof(this.onReservationClick) == 'function' && this.res[r2].i != null)
	{
		area2.onclick = function(){ self.onReservationClick(parseInt(self.res[r2].i, 10)); return false; }
		area2.className	 += ' clickable';
		//area2.setAttribute('href', '#');
	}
	else if (typeof(this.onSearchClick) == 'function' && this.search != null) 
	{
		if(this.search == r2){		
		area2.onclick 	= 	function(){ self.onSearchClick(self.datas); return false;}
		area2.className	+= 	' clickable';
		//area2.setAttribute('href', '#');
		}
	}
	else if (typeof(this.onAvailableClick) == 'function' && cn2 == 'n') 
	{
		area2.onclick 	 = function(event){
			
			var e = event || window.event;
			var pos = getRelativeCoordinates(event, area1.parentNode.parentNode);
			var day = Math.ceil(pos.x/self.daySize);
			
			/*

			var pos2 = getAbsolutePosition (area2.parentNode.parentNode);									
			var pos = getAbsolutePosition (area2.parentNode);
			var day = Math.ceil((pos.x-pos2.x)/self.daySize)+1;
			*/
			
			self.onAvailableClick(self.datas,date.print('%a, '+ day +' %b %Y')); return false;
									
		}
		area2.className	+= ' clickable';
	}
	if(r2 != null && this.res[r2].t != null && typeof(this.res[r2].t) == 'object')
	{
		kigoToolTips.add(area2, this.resTooltips[r2]);
	}
	if(cn2 == 'n' && typeof(this.limits) == 'object' && this.limits != null )
	{
		kigoToolTips.add(area2, this.areaTooltips[this.lim]);
	}
	

	block.appendChild(area1);
	//block.appendChild(area3);
	block.appendChild(area2);
	//block.appendChild(img);
	//block.appendChild(map);
	
	
	//var bl = block; 

	
	if(r1 != null || r2 != null){
		var date	=	(r1 == null)	?	new Date.parseDate(this.res[r2].s, '%Y-%m-%d')	:	new Date.parseDate(this.res[r1].e, '%Y-%m-%d');
		if(date.getMonthDays() == date.getDate() && this.parent.lastChild.className == 'eom')	this.parent.insertBefore(block, this.parent.lastChild);
		else	this.parent.appendChild(block);
	}
	else 
		this.parent.appendChild(block);
	
	kigoCalendar.bei++;
}
/*
 * Appends and empty div for months that have less than 31 days.
 */
kigoCalendar.prototype._updateLengthAndStack	=	function(width)
{
	this.length		-=	width;
	this.stack[0]	-=	width;
	if(this.stack[0]	==	0)
	{
		this.stack.shift();
		var dif 	 =	31-this.md[0];
		if(dif > 0)	this.parent.appendChild(this._createBlock(dif, 'eom'));
		this.md.shift();
	}
}
/*
 * Create reservation tooltips and availability elements. 
 * See end of function for the latter.
 */
kigoCalendar.prototype._createReservations		=	function(parent)
{
	var calStart				=	new Date(this.sy, this.sm - 1	);
	var calEnd					=	new Date(this.ey, this.em		);
	var self					=	this;
	
	this.parent	=	parent;
	if(this.res.length == 0){
		var area;
		this.length	=	this._getDaysBetween(calStart, calEnd);
		if(this.limits != null)
		{
			var tt			=	this.div.cloneNode(false);
			tt.className	=	'tooltip';
			this.container.appendChild(tt);
			tt.setAttribute('pos', 's');
			
			
			
			var l5f				=	this.div.cloneNode(false);
			l5f.className		=	'ttClickForAction';
			l5f.appendChild(document.createTextNode('Click to reserve!'));
			tt.appendChild(l5f);
			
			var lf				=	this.div.cloneNode(false);
			lf.className		=	'ttFreeArea';
			lf.appendChild(document.createTextNode('Available dates'));
			tt.appendChild(lf);
			
			var l1			=	this.div.cloneNode(false);
			l1.className	=	'ttlcout';
			var lcout;
			if(this.limits.p == null)	lcout	=	'No earlier dates';
			else
			{
				var d 	=	new Date.parseDate(this.limits.p, '%Y-%m-%d');
				lcout	=	d.print('%a, %d %b %Y');
			}
			l1.appendChild(document.createTextNode('Last check-out : ' + lcout));
			tt.appendChild(l1);
			
			var l2			=	this.div.cloneNode(false);
			l2.className	=	'ttncin';
			var ncin;
			if(this.limits.n == null)	ncin	=	'No further dates';
			else
			{
				var d 	=	new Date.parseDate(this.limits.n, '%Y-%m-%d');
				ncin	=	d.print('%a, %d %b %Y');
			}
			l2.appendChild(document.createTextNode('Next check-in : ' + ncin));
			tt.appendChild(l2);

			
		}
		
		var month = calStart.getMonth(); 
		var i = 0;
		
		while(this.length != 0 && this.stack.length != 0)
		{
			area	=	this._createBlock(this._getWidth(), 'n');
			this.parent.appendChild(area);
			this._updateLengthAndStack(this._getWidth());
			if (typeof(this.onAvailableClick) == 'function') 
			{
				area.modifiedMonth = month + i;
				area.onclick 	 = function(event){ 
					
					var e = event || window.event;
					var pos = getRelativeCoordinates(event, area.parentNode);
					var day = Math.ceil(pos.x/self.daySize);
					
					if (day==0) day = 1;
					
					var modifiedDate = new Date(
							calStart.getFullYear() + (Math.floor(this.modifiedMonth/12))
							, (this.modifiedMonth%12)
							, day);
					
					self.onAvailableClick(self.datas,modifiedDate.print('%a, '+ day +' %b %Y')); return false;
								
					return false;
					}
				area.className	+= ' clickable';
			}
			if(this.limits != null)
			{
				kigoToolTips.add(area, tt);
			}
			i++;
		}
	}
	else
	{
		for(var nbr=0; nbr<this.res.length; nbr++)
		{
			var res = this.res[nbr];

			if(res.t != null && typeof(res.t) == 'object')
			{
				var resStart	=	new Date.parseDate(res.s, '%Y-%m-%d');
				var resEnd		=	new Date.parseDate(res.e, '%Y-%m-%d');
				
				var ttRes			=	this.div.cloneNode(false);
				ttRes.className	=	'tooltip';
				ttRes.setAttribute('pos', 's');
				
				if((parseInt(res.m) == 0 || parseInt(res.m) == 1) && (parseInt(res.c) == 0 || parseInt(res.c) == 1))
				{
					ttRes.className	+=	' m_'+res.m;
					ttRes.className	+=	' c_'+res.c;
					ttRes.className	+=	' resTooltip';
					
					var l1			=	this.div.cloneNode(false);
					l1.className	=	'ttAgency';
					l1.appendChild(document.createTextNode(res.t.a));
					ttRes.appendChild(l1);
					
					if(typeof(res.t.g) == 'string' && res.t.g.length)
					{
						var l2			=	this.div.cloneNode(false);
						l2.className	=	'ttGuest';
						l2.appendChild(document.createTextNode(res.t.g));
						ttRes.appendChild(l2);
					}
				}
				else
				{
					ttRes.className	+=	' searchTooltip';
					var l1			=	this.div.cloneNode(false);
					l1.className	=	'ttSearch';
					l1.appendChild(document.createTextNode(res.t.n));
					
					var l5f				=	this.div.cloneNode(false);
					l5f.className		=	'ttClickForAction';
					l5f.appendChild(document.createTextNode('Click to reserve!'));
					ttRes.appendChild(l5f);
					
					ttRes.appendChild(l1);
				}
				
				var l3			=	this.div.cloneNode(false);
				l3.className	=	'ttNights';
				var nights		=	this._getDaysBetween(resStart, resEnd);
				var l3t			=	nights +' night';
				if(nights >1)	l3t += 's';
				l3.appendChild(document.createTextNode(l3t));
				ttRes.appendChild(l3);
				
				var l4			=	this.div.cloneNode(false);
				l4.className	=	'ttCheckIn';
				l4.appendChild(document.createTextNode('Check-in : '));
				if(res.c)
				{
					if(res.t.i != null)	
						l4.appendChild(document.createTextNode(res.t.i +', '));
					else
					{
						var span		=	document.createElement('span');
						span.className	=	'warn2';
						span.appendChild(document.createTextNode('time? '));
						l4.appendChild(span);
					}
				}
				l4.appendChild(document.createTextNode(resStart.print('%a, %d %b %Y')));
				ttRes.appendChild(l4);
				
				var l5			=	this.div.cloneNode(false);
				l5.className	=	'ttCheckOut';
				l5.appendChild(document.createTextNode('Check-out : '));
				if(res.c)
				{
					if(res.t.o != null)	
						l5.appendChild(document.createTextNode(res.t.o +', '));
					else
					{
						var span		=	document.createElement('span');
						span.className	=	'warn2';
						span.appendChild(document.createTextNode('time? '));
						l5.appendChild(span);
					}
				}
				l5.appendChild(document.createTextNode(resEnd.print('%a, %d %b %Y')));
				ttRes.appendChild(l5);
				
				if(res.t.e!= null)
				{
					var l6			=	this.div.cloneNode(false);
					l6.className	=	'ttExpiry';
					var nbSec		=	res.t.e;
					if(nbSec < 0)	l6.appendChild(document.createTextNode('Expiry : About to expire.'));
					else
					{
						var fd		=	Math.floor(nbSec/86400);
						var fh		=	Math.floor((nbSec - (fd*86400))/3600);
						var fm		=	Math.floor((nbSec - (fd*86400) - (fh*3600)) / 60);
						if(fd != 0 && fh != 0)	l6.appendChild(document.createTextNode('Expiry : ' + fd + ' days ' + fh + 'h ' + fm + 'm'));
						else if(fh != 0)		l6.appendChild(document.createTextNode('Expiry : ' + fh + 'h ' + fm + 'm'));
						else					l6.appendChild(document.createTextNode('Expiry : ' + fm + 'm'));
					}

					ttRes.appendChild(l6);
				}
				

								
				this.container.appendChild(ttRes);
				this.resTooltips.push(ttRes);				
				
				if (typeof(this.limits) == 'object' && this.limits != null )
				{
					if(nbr == 0)
					{
						if(this._compareDate(resStart, calStart) >= 0)
						{
							var firstAreaStart	=	(this.limits.p != null)	?	new Date.parseDate(this.limits.p, '%Y-%m-%d')	:	new Date(this.sy, this.sm-1);
							var firstAreaEnd	=	resStart;
							
							var fTtArea			=	this.div.cloneNode(false);
							fTtArea.className	=	'tooltip';
							fTtArea.className	+=	' freeAreaTooltip';
							fTtArea.setAttribute('pos', 's');
							
							
							var l5f				=	this.div.cloneNode(false);
							l5f.className		=	'ttClickForAction';
							l5f.appendChild(document.createTextNode('Click to reserve!'));
							fTtArea.appendChild(l5f);								
							
							var l1f				=	this.div.cloneNode(false);
							l1f.className		=	'ttFreeArea';
							l1f.appendChild(document.createTextNode('Available dates'));
							fTtArea.appendChild(l1f);
							
							if(this._compareDate(firstAreaStart, calStart) != 0)
							{
								var l2f					=	this.div.cloneNode(false);
								l2f.className			=	'ttNights';
								var nights				=	this._getDaysBetween(firstAreaStart, firstAreaEnd);
								var l2tf				=	nights +' night';
								if(nights >1)	l2tf 	+= 's';
								l2f.appendChild(document.createTextNode(l2tf));
								fTtArea.appendChild(l2f);
							}
							
							var l3f				=	this.div.cloneNode(false);
							l3f.className		=	'ttCheckIn';
							l3f.appendChild(document.createTextNode('Last check-out : '));
							if(this._compareDate(firstAreaStart, calStart) == 0)	
								l3f.appendChild(document.createTextNode('No earlier dates'));
							else
								l3f.appendChild(document.createTextNode(firstAreaStart.print('%a, %d %b %Y')));
							fTtArea.appendChild(l3f);
							
							var l4f				=	this.div.cloneNode(false);
							l4f.className		=	'ttCheckOut';
							l4f.appendChild(document.createTextNode('Next check-in : '));
							l4f.appendChild(document.createTextNode(firstAreaEnd.print('%a, %d %b %Y')));
							fTtArea.appendChild(l4f);
												
							
							this.container.appendChild(fTtArea);
							
							this.areaTooltips.push(fTtArea);
						}
					}
					
					var dateStart		=	resEnd;
					var dateEnd			=	(nbr != this.res.length-1)	?	new Date.parseDate(this.res[nbr+1].s, '%Y-%m-%d')	:	new Date(this.ey, this.em);
					
					if(this._compareDate(dateStart, dateEnd) != 0)
					{
						if(this._compareDate(dateEnd, calEnd) == 0 && this.limits.n != null)
							dateEnd			=	new Date.parseDate(this.limits.n, '%Y-%m-%d');
						var ttArea			=	this.div.cloneNode(false);
						ttArea.className	=	'tooltip';
						ttArea.className	+=	' freeAreaTooltip';
						ttArea.setAttribute('pos', 's');
							
						
						var l5f				=	this.div.cloneNode(false);
						l5f.className		=	'ttClickForAction';
						l5f.appendChild(document.createTextNode('Click to reserve!'));
						ttArea.appendChild(l5f);						
						
						var l1			=	this.div.cloneNode(false);
						l1.className	=	'ttFreeArea';
						l1.appendChild(document.createTextNode('Available dates'));
						ttArea.appendChild(l1);
						
						if(this._compareDate(dateEnd, calEnd) != 0)
						{
							var l2			=	this.div.cloneNode(false);
							l2.className	=	'ttNights';
							var nights		=	this._getDaysBetween(dateStart, dateEnd);
							var l2t			=	nights +' night';
							if(nights >1)	l2t += 's';
							l2.appendChild(document.createTextNode(l2t));
							ttArea.appendChild(l2);
						}
						
						var l3			=	this.div.cloneNode(false);
						l3.className	=	'ttCheckIn';
						l3.appendChild(document.createTextNode('Check-in : '));
						l3.appendChild(document.createTextNode(dateStart.print('%a, %d %b %Y')));
						ttArea.appendChild(l3);
						
						var l4			=	this.div.cloneNode(false);
						l4.className	=	'ttCheckOut';
						l4.appendChild(document.createTextNode('Check-out : '));
						if(this._compareDate(dateEnd, calEnd) == 0)	
							l4.appendChild(document.createTextNode('No further dates'));
						else
							l4.appendChild(document.createTextNode(dateEnd.print('%a, %d %b %Y')));
						ttArea.appendChild(l4);

						
						this.container.appendChild(ttArea);
						
						this.areaTooltips.push(ttArea);
					}
				}
			}
			else	this.resTooltips.push('');
		}	
		
		for(var nbr=0; nbr<this.res.length; nbr++)
		{
			var res = this.res[nbr];
			var resStart			=	new Date.parseDate(res.s, '%Y-%m-%d');
			var resEnd				=	new Date.parseDate(res.e, '%Y-%m-%d');

			// if first reservation
			if(nbr == 0)
			{   // first block cand be empty, a reservation last day or reservation start
				switch(this._compareDate(calStart, resStart))
				{
					case -1:	this._createFirstArea(calStart, resStart);												
								break;
					case  0:	this._createBlockEnd('n', this._getClass(res.m, res.c), null, nbr);
								if(this.lim != null)	this.lim++;	
								break;
					case  1:	this._createFirstReservation(nbr, calStart, resEnd);break;
				}
				
			}
			// create reservation			
			if(this._compareDate(calStart, resStart) != 1)	this._createReservation(nbr, resStart, resEnd);
			// create reservation end and separation element
			if(nbr != this.res.length-1)
			{
				var nextResStart	=	new Date.parseDate(this.res[nbr+1].s, '%Y-%m-%d');
				
				if(this._compareDate(resEnd, nextResStart) != 0)
				{
					this._createBlockEnd(this._getClass(res.m, res.c), 'n', nbr, null);
					this._createFreeArea(resEnd, nextResStart);
					this._createBlockEnd('n', this._getClass(this.res[nbr+1].m, this.res[nbr+1].c), null, nbr+1);
					if(this.lim != null)	this.lim++;
				}
				else	this._createBlockEnd(this._getClass(res.m, res.c), this._getClass(this.res[nbr+1].m, this.res[nbr+1].c), nbr, nbr+1);
			}
			else if(this.stack.length > 0)
			{
				// fill with empty time
				this._createBlockEnd(this._getClass(res.m, res.c), 'n', nbr, null);
				this._createFreeArea(resEnd, calEnd);
				if(this.lim != null)	this.lim++;
			}
		}
	}
}
/*
 * Dump the calendar in a container DOM element.
 * 
 * Computes out of the css the dimensions for days and months.
 * To do this we'll be generating a  dummy container set to pick up the computed CSS values.
 * And then we remove it.	
 * 
 * Creates, days and months headers, then starts the generation of availability items.
 */
kigoCalendar.prototype.dump 					= 	function(elm)
{

	
	var months			        =	document.createElement('div');	
		elm.appendChild(months);								
		months.className		=	'months';
	
	var b	=	this.div.cloneNode(false);		
		b.className				=	'month';
		b.innerHTML = '<p>&nbsp;</p>';
		months.appendChild(b);					
	// we need to compute display values for the items out of CSS.
	// To do this we'll be generating a  dummy container set to pick up the computed CSS values.	
	//values required
	var dim = new Object();			
		dim.monthSize	=	b.clientWidth;
		dim.daySize	=	b.clientHeight;
						
	elm.removeChild(months);	
	//remove the dummy container
	
	var pointer =  elm.parentNode;
				
	// We are detaching the calendar from the DOM HERE in order to speed up the generation
	// Otherwise it will take too much time to render everything.
	
	this.container		=	vkDom.el(elm).cloneNode(false);		
	
	this.daySize		=	null;
	this.monthSize		=	null;
	var lines			=	(this.ey - this.sy) * 12 + (this.em - this.sm) + 1;
	var day, month;
	
	var days			=	this.div.cloneNode(false);	this.container.appendChild(days					);
	var months			=	this.div.cloneNode(false);	this.container.appendChild(months				);
	var reservations	=	this.div.cloneNode(false);	this.container.appendChild(reservations	);
	var clear			=	this.div.cloneNode(false);	this.container.appendChild(clear				);
	
	var todaysMonth = (new Date().print('%b %y'));
	var todaysDay = (new Date().print('%d'));
	
	
	for(month=0; month<lines; month++)
	{
		var m	=	this.sm + month - 1 - 12 * (Math.ceil((this.sm + month - 1) / 12) - 1);
		var y	=	this.sy + Math.ceil((this.sm + month - 1) / 12) - 1;
		var d	=	new Date(y, m);
		var b	=	this.div.cloneNode(false);
		var r	;
		var nbr	;
		
		this.md.push(d.getMonthDays());
		this.stack.push(d.getMonthDays());
		
		b.className				=	'month';
		b.appendChild(document.createTextNode(d.print('%b %y')));
		months.appendChild(b);
	
		if(this.monthSize 	== null)	this.monthSize	=	dim.monthSize;
		if(this.daySize 	== null)	this.daySize	=	dim.daySize;
		
		if(month == 0)				b.className	+=	' first';
		else if(month == lines-1)	b.className	+=	' last';
		else						b.className +=	' middle';
		
		if (d.print('%b %y') == todaysMonth) b.className +=	' today';
		
		b.className +=	(month % 2) ? ' even' : ' odd'; 
	}
	
	this.nbMonth	=	this.stack.length;
	
	for(day=0; day<=31; day++)
	{
		var b	=	this.div.cloneNode(false);
		var r;
		
		if (day == 0)	b.className				=	'month';
		else
		{
			b.className	=	'day';
			var z		=	(day<10)	?	'0'	:	'';
			b.appendChild(document.createTextNode(z+day));
		}
		if(day == 0 )		b.className +=	' corner';
		else if (day ==  1)	b.className += 	' first';
		else if (day == 31)	b.className += 	' last';
		else b.className += 	' middle';
		
		if (z+day == todaysDay) b.className +=	' today';
		
		if(day != 0)		b.className +=	(day % 2) ? ' odd' : ' even'; 
		days.appendChild(b);
	}
	
	days.style.width				=	this.monthSize	+ 	31*this.daySize		+'px';	days.style.height				=				this.daySize	+'px';	
	months.style.width				=	this.monthSize							+'px';	months.style.height				=	lines 	* 	this.daySize	+'px';	
	reservations.style.width	=							31*this.daySize		+'px';	reservations.style.height		=	lines	*	this.daySize	+'px';
	
	this.container.style.width		=	days.style.width;
	this.container.height			=	parseInt(days.style.height)	+	parseInt(months.style.height)	+'px';

	months.className			=	'months';
	days.className				=	'days';
	reservations.className		=	'reservations';
	clear.className				=	'clear';

	this._createReservations(reservations);
	
	pointer.replaceChild(this.container,elm);

	// 18/11/2009 - return the new container...
	return this.container;
}



/* /js/kigoMPCalendar.js */ /* UTF8 COOKIE: éà */

// 25/11/2009 - Removed limits argument, limits were already part of propList

function	kigoMPCalendar(startMonth, startYear, endMonth, endYear, propList, searchDate)
{
	this.sm					=	startMonth			;
	this.sy					=	startYear			;
	this.em					=	endMonth			;
	this.ey					=	endYear				;
	this.prop				=	propList			;
	this.res				=	null				;
	this.resTooltips		=	[]					;
	this.areaTooltips		=	[]					;
	this.md					=	[]					;
	this.stack				=	null				;
	this.length										;
	this.onReservationClick	=	null				;
	this.onSearchClick		=	null				;
	this.onPropertyClick	=	null				;
	this.onAvailableClick	=	null				;
	this.search				=	null				;
	this.daySize			=	null				;
	this.nbDays				=	0					;
	this.nbMonths									;
	this.pr											;
	this.lim				=	0					;
	this.ie6				=	vkDom.hasClass(document.body, 'ua-msie-6');
	
	this.div = document.createElement('div');
	this.span = document.createElement('span');
	this.h1 = document.createElement('h1');
	this.h2 = document.createElement('h2');
	this.br = document.createElement('br');

	var p;	
	for(p=0; p<this.prop.length; p++)	this.resTooltips[p]	=	[];
	
	if(searchDate != null)
	{
		var searchStart		=	Date.parseDate(searchDate.s, '%Y-%m-%d');
		var searchEnd		=	Date.parseDate(searchDate.e, '%Y-%m-%d');
		var calStart		=	new Date(this.sy, this.sm - 1	);
		var calEnd			=	new Date(this.ey, this.em		);
		var isOk			=	true;
		
		if(this._compareDate(calStart, searchEnd) >0  || this._compareDate(searchStart, calEnd) >0)
		{
			isOk				=	false;
			this.onSearchClick	=	null;
		}

		if(isOk)
		{
			var	prop;

			this.search			=	[];
			searchDate.t		=	{'n' : 'Your search' };
			for(var p=0; p<this.prop.length; p++)
			{
				prop = this.prop[p];

				var newResTab		=	[];
				//if no res
				if(prop.r.length == 0){
					this.search.push(newResTab.length);
					newResTab.push(searchDate);
				}
				else{
					var added = false; 
					for(var nbr=0; nbr<prop.r.length; nbr++)
					{
						if(!added){
							var resStart	=	Date.parseDate(prop.r[nbr].s, '%Y-%m-%d');
							if(this._compareDate(searchEnd, resStart) <=0){
								this.search.push(newResTab.length);
								newResTab.push(searchDate);
								added = true;
							}
						}
						newResTab.push(prop.r[nbr]);
						if(!added && nbr == prop.r.length-1){
							this.search.push(newResTab.length);
							newResTab.push(searchDate);
						}
					}
				}
				this.prop[p].r	=	newResTab;
			}

		}
	}
}

kigoMPCalendar.bei = 1;

kigoMPCalendar.prototype._ac		= function	(parent, child)		{ parent.appendChild(child);				}
kigoMPCalendar.prototype._sa		= function	(node, name, value)	{ node.setAttribute(name, value);			}
kigoMPCalendar.prototype._ga		= function	(node, name)		{ return node.getAttribute(name);			}
kigoMPCalendar.prototype._ra		= function	(node, name)		{ node.removeAttribute(name);				}

kigoMPCalendar.prototype._getClass = function(m, c)
{
	if(
		((m = parseInt(m)) != 0 && m != 1) ||
		((c = parseInt(c)) != 0 && c != 1)
	)
		return 's'; // search

	if(m == c)
	{
		if(!m)
			return 'oh'; // other hold
		return 'mc'; // mine confirmed
	}

	if(!m)
		return 'oc'; // other confirm

	return 'mh'; // mine hold
}

kigoMPCalendar.prototype._getDaysBetween 		= function(d1, d2)
{
	/*
	var days;
	var d, m ,y;
	if(this._compareDate(d1, d2) == -1)	return	Math.round((d2.getTime() - d1.getTime()) / (1000*60*60*24));
	else								return	Math.round((d1.getTime() - d2.getTime()) / (1000*60*60*24));
	*/
	return Math.round( Math.abs(d1.getTime() - d2.getTime()) / 86400000 );	// 1000*60*60*24
}

kigoMPCalendar.prototype._compareDate 			= function(a, b)
{
	if(a.getFullYear() < b.getFullYear())
		return -1;
	if(a.getFullYear() > b.getFullYear())
		return 1;

	if(a.getMonth() < b.getMonth())
		return -1;
	if(a.getMonth() > b.getMonth())
		return 1;

	if(a.getDate() < b.getDate())
		return -1;
	if(a.getDate() > b.getDate())
		return 1;

	return 0;
}

kigoMPCalendar.prototype._createClickOnProperty	=	function(elm, obj)
{
	var	self = this;
	elm.onclick 	= function(){ self.onPropertyClick(obj); return false; }
}

kigoMPCalendar.prototype._getScrollBarWidth		=	function()
{
	try
	{
		var	d1, d2, w1, w2;

		d1 = this.div.cloneNode(false);
		d2 = this.div.cloneNode(false);

		d1.style.width = '50px';
		d1.style.height = '50px';
		d1.style.overflow = 'hidden';
		d1.style.position = 'absolute';
		d1.style.top = '-200px';
		d1.style.left = '-200px';

		d2.style.height = '100px';

		d1.appendChild(d2);

		document.body.appendChild(d1);

		w1 = d1.clientWidth;
		d1.style.overflowY = 'scroll';
		w2 = d1.clientWidth;

		if(
			typeof(w1) != 'number' ||
			w1 <= w2
		)
			throw 'Failed to determine the width';

		return w1 - w2;
	}
	catch(e)
	{
		return 20;
	}
}



// 02/08/2010 - The moron had 5 (FIVE!!!) different (!!!) copies of this code, each one with its own bugs (dates were offset'ed by a couple of days..)
kigoMPCalendar.prototype._setupAvailableClick	=	function(prop, area)
{
	if(!kigo.is_function(this.onAvailableClick))
		return;

	var	self = this;

	area.onclick = function(event)
	{
		var e = event || window.event;
		var	x;

		if(typeof(e['layerX']) != 'undefined')
			x = e.layerX;
		else if(typeof(e['x']) != 'undefined')
			x = e.x;
		else
			x = null;
		
		self.onAvailableClick(
			prop,
			x != null ? 
				(new kigoDate(1, self.sm, self.sy)).addDays(Math.floor(x / self.daySize)).calendar()
			: null
		);

		return false;
	}

	area.className += ' clickable';
}





kigoMPCalendar.prototype._getWidth 				= 	function(){ return (this.length < this.stack) ? this.length : this.stack; }

kigoMPCalendar.prototype._createBlock			=	function(width, className)
{
	var b					 =	this.div.cloneNode(false);
	b.style.width			 =	width*this.daySize	+'px';
	b.className				 =	className;
	return b;
}

kigoMPCalendar.prototype._createReservation		=	function(numRes, resStart, resEnd)
{
	var res;
	var self 	= this;
	this.length	=	this._getDaysBetween(resStart, resEnd)-1;
	res	=	this._createBlock(this._getWidth(), this._getClass(this.res[numRes].m, this.res[numRes].c));
	this._ac(this.reservations, res);
	
	if (typeof(this.onReservationClick) == 'function' && this.res[numRes].i != null) 
	{
		var propObject	= this.prop[this.pr];
		var resId		= this.res[numRes].i;
		res.onclick 	= function(){ self.onReservationClick(resId, propObject); return false; }
		res.className	+= ' clickable';
	}
	else if (typeof(this.onSearchClick) == 'function' && this.search != null) 
	{
		if(this.search[this.pr] == numRes){
			var propObject	= this.prop[this.pr];
			res.onclick 	= function(){ self.onSearchClick(propObject); return false; }
			res.className	+= ' clickable';
		}
	}
	
	if (this.res[numRes].t != null && typeof(this.res[numRes].t) == 'object')
	{
		kigoToolTips.add(res, this.resTooltips[this.pr][numRes]);
	}
	this._updateLengthAndStack(this._getWidth());
}

kigoMPCalendar.prototype._createFirstArea			=	function(dateStart, dateEnd)
{
	var area;
	this.length	=	this._getDaysBetween(dateStart, dateEnd);
	
	area	=	this._createBlock(this._getWidth(), 'n');
	this._ac(this.reservations, area);
	this._updateLengthAndStack(this._getWidth());
	this.length++;
	this._createBlockEnd('n', this._getClass(this.res[0].m, this.res[0].c), null, 0);
	
	if (typeof(this.prop[this.pr].l) == 'object' && this.prop[this.pr].l != null)
	{
		kigoToolTips.add(area, this.areaTooltips[this.lim]);
		this.lim++;
	}

	this._setupAvailableClick(this.prop[this.pr], area);
}






kigoMPCalendar.prototype._createFirstReservation	=	function(numRes, resStart, resEnd)
{
	var res;
	var self = this;
	this.length		=	this._getDaysBetween(resStart, resEnd);
	res	=	this._createBlock(this._getWidth(), this._getClass(this.res[numRes].m, this.res[numRes].c));
	if (typeof(this.onReservationClick) == 'function' && this.res[numRes].i != null)
	{
		var propObject	= this.prop[this.pr];
		var resId= this.res[numRes].i;
		res.onclick = function(){ self.onReservationClick(resId, propObject); return false; }
		res.className	+= ' clickable';
	}
	else if (typeof(this.onSearchClick) == 'function' && this.search != null) 
	{
		if(this.search[this.pr] == numRes){
			var propObject	= this.prop[this.pr];
			res.onclick 	= function(){ self.onSearchClick(propObject); return false; }
			res.className	+= ' clickable';
		}
	}
	
	this._ac(this.reservations, res);
	if (this.res[0].t != null && typeof(this.res[0].t) == 'object')
	{
		kigoToolTips.add(res, this.resTooltips[this.pr][0]);
	}
	this._updateLengthAndStack(this._getWidth());
}

kigoMPCalendar.prototype._createFreeArea			=	function(dateStart, dateEnd)
{
	var area;
	this.length	=	this._getDaysBetween(dateStart, dateEnd)-1;
	
	area	=	this._createBlock(this._getWidth(), 'n');
	this._ac(this.reservations, area);
	this._updateLengthAndStack(this._getWidth());
	
	if (typeof(this.prop[this.pr].l) == 'object' && this.prop[this.pr].l != null)
	{				
		kigoToolTips.add(area, this.areaTooltips[this.lim]);
	}

	this._setupAvailableClick(this.prop[this.pr], area);
}

kigoMPCalendar.prototype._createBlockEnd			=	function(cn1, cn2, r1, r2)
{
	var block;
	var self 	=	this;
	//var pos		=	(this.pr >= (this.prop.length/2))	?	'n'	:	's';
	
	//var map		=	document.createElement('map') 	;	this._sa(map, 	'id',		'bei' +kigoMPCalendar.bei	);	this._sa(map, 	'name', 	'bei' +kigoMPCalendar.bei	);
	
	//var area1	=	document.createElement('area')	;	this._sa(area1, 	'shape', 	'polygon'		);	this._sa(area1, 'coords', '0, 0, '+this.daySize+', 0, 0, '+this.daySize+'');									this._ac(map, area1);
	//var area2	=	document.createElement('area')	;	this._sa(area2, 	'shape', 	'polygon'		);	this._sa(area2, 'coords', ''+this.daySize+', 0, '+this.daySize+', '+this.daySize+', 0, '+this.daySize+'');	this._ac(map, area2);
	
	//var img	=	document.createElement('img');
	//img.src	=	'/img/calendars/map.png';

	//img.useMap			=	'#bei' +kigoMPCalendar.bei;
	//img.style.width		=	this.daySize +'px';
	//img.style.height	=	this.daySize +'px';
	
	var area1	=	document.createElement('div')	;
	area1.className	+=	' e' +cn1+ ' ';
	
	var area2	=	document.createElement('div')	;
	area2.className	+=	' b' +cn2+ ' ';
	
	area1.style.width	= area2.style.width	=	(this.daySize/2) +'px';
	
	if(r1 != null && typeof(this.onReservationClick) == 'function' && this.res[r1].i != null)
	{
		var propObject	 = this.prop[this.pr];
		var resId= this.res[r1].i;
		area1.onclick	 = function(){ self.onReservationClick(resId, propObject); return false; }
		area1.className	+= ' clickable';
		//this._sa(area1, 'href', '#');
	}
	else if (typeof(this.onSearchClick) == 'function' && this.search != null) 
	{
		if(this.search[this.pr] == r1){
			var propObject	= 	this.prop[this.pr];
			area1.onclick 	= 	function(){ self.onSearchClick(propObject); return false }
			area1.className	+= 	' clickable';
			//this._sa(area1, 'href', '#');
		}
	}

	if(cn1 == 'n')
		this._setupAvailableClick(this.prop[this.pr], area1);

	if(r1 != null && this.res[r1].t != null && typeof(this.res[r1].t) == 'object')
	{
		kigoToolTips.add(area1, this.resTooltips[this.pr][r1]);
	}
	if(cn1 == 'n' && typeof(this.prop[this.pr].l) == 'object' && this.prop[this.pr].l != null )
	{
		kigoToolTips.add(area1, this.areaTooltips[this.lim]);
	}
	
	if(r2 != null && typeof(this.onReservationClick) == 'function' && this.res[r2].i != null)
	{
		var propObject	= this.prop[this.pr];
		var resId= this.res[r2].i;
		area2.onclick 	= function(){ self.onReservationClick(resId, propObject); return false; }
		area2.className += ' clickable';
		//this._sa(area2, 'href', '#');
	}
	else if (typeof(this.onSearchClick) == 'function' && this.search != null) 
	{
		if(this.search[this.pr] == r2){
			var propObject	= 	this.prop[this.pr];
			area2.onclick 	= 	function(){ self.onSearchClick(propObject); return false; }
			area2.className	+= 	' clickable';
			//this._sa(area2, 'href', '#');
		}
	}


	if(cn2 == 'n')
		this._setupAvailableClick(this.prop[this.pr], area2);

	if(r2 != null && this.res[r2].t != null && typeof(this.res[r2].t) == 'object')
	{						
		kigoToolTips.add(area2, this.resTooltips[this.pr][r2]);		
	} 
	if((cn2 == 'n') && typeof(this.prop[this.pr].l) == 'object' && this.prop[this.pr].l != null )
	{			
		kigoToolTips.add(area2, this.areaTooltips[this.lim]);
	}
	
	this._updateLengthAndStack(1);
	block	=	this._createBlock(1, 'start_end_block' );
	block.style.display = "inline";
	//block.className	+=	' ' +cn2+ 'Img ';
	
	//this._ac(block, img);
	//this._ac(block, map);
	
	this._ac(block, area1);
	this._ac(block, area2);
	
	this._ac(this.reservations, block);
	
	kigoMPCalendar.bei++;
}

kigoMPCalendar.prototype._updateLengthAndStack	=	function(width)
{
	this.length	-=	width;
	this.stack	-=	width;
	if(this.stack	==	0)	this.stack	=	this.nbDays;
}

kigoMPCalendar.prototype._createReservations		=	function()
{
	
	var calStart				=	new Date(this.sy, this.sm - 1	);
	var calEnd					=	new Date(this.ey, this.em		);
	
	if(this.res.length == 0){
		var area;
		this.length	=	this._getDaysBetween(calStart, calEnd);
		
		area	=	this._createBlock(this._getWidth(), 'n');
		this._ac(this.reservations, area);
		
		this._setupAvailableClick(this.prop[this.pr], area);

		// Limits are now always available
		{
			var tt			=	this.div.cloneNode(false);
			tt.className	=	'tooltip';
			this._ac(this.container, tt);
			tt.setAttribute('pos', 's');
			
			var l7f				=	this.div.cloneNode(false);
			l7f.className		=	'ttClickForAction';
			this._ac(l7f, document.createTextNode('Click to reserve!'));
			this._ac(tt, l7f);
			
			var lf				=	this.div.cloneNode(false);
			lf.className		=	'ttFreeArea';
			this._ac(lf, document.createTextNode('Available dates'));
			this._ac(tt, lf);
			
			var l1			=	this.div.cloneNode(false);
			l1.className	=	'ttlcout';
			var lcout;
			if(this.prop[this.pr].l.p == null)	lcout	=	'No earlier dates';
			else
			{
				var d 	=	Date.parseDate(this.prop[this.pr].l.p, '%Y-%m-%d');
				lcout	=	d.print('%a, %d %b %Y');
			}
			this._ac(l1, document.createTextNode('Last check-out : ' + lcout));
			this._ac(tt, l1);
			
			var l2			=	this.div.cloneNode(false);
			l2.className	=	'ttncin';
			var ncin;
			if(this.prop[this.pr].l.n == null)	ncin	=	'No further dates';
			else
			{
				var d 	=	Date.parseDate(this.prop[this.pr].l.n, '%Y-%m-%d');
				ncin	=	d.print('%a, %d %b %Y');
			}
			this._ac(l2, document.createTextNode('Next check-in : ' + ncin));
			this._ac(tt, l2);
			
			kigoToolTips.add(area,tt);
		}
	}
	else
	{
		var	res;

		for(nbr=0; nbr<this.res.length; nbr++)
		{
			res = this.res[nbr];

			if(res.t != null && typeof(res.t) == 'object')
			{
				var resStart	=	Date.parseDate(res.s, '%Y-%m-%d');
				var resEnd		=	Date.parseDate(res.e, '%Y-%m-%d');
				
				var div			=	this.div.cloneNode(false);
				div.className	=	'tooltip';
				div.setAttribute('pos', 's');
				
				var m = parseInt(res.m);
				var c = parseInt(res.c);

				if((m == 0 || m == 1) && (c == 0 || c == 1))
				{
					div.className	+=	' m_'+m;
					div.className	+=	' c_'+c;
					div.className	+=	' resTooltip';
															
					var l1			=	this.div.cloneNode(false);
					l1.className	=	'ttAgency';

					/* 17/03/2010 - Why is there no such difference in single calendar?
						Remove this for now, the flag was removed from server queries...

					if(m == 0)
					{
						if(res.t.t == 0)
							this._ac(l1, document.createTextNode('Owner : '));
						else
							this._ac(l1, document.createTextNode('Agency : '));
					}
					*/


					this._ac(l1, document.createTextNode(res.t.a));
					this._ac(div, l1);
					
					if(typeof(res.t.g) == 'string' && res.t.g.length)
					{
						var l2			=	this.div.cloneNode(false);
						l2.className	=	'ttGuest';
						this._ac(l2, document.createTextNode(res.t.g));
						this._ac(div, l2);
					}
				}
				else
				{
					div.className	+=	' searchTooltip';
					var l1			=	this.div.cloneNode(false);
					l1.className	=	'ttSearch';
					this._ac(l1, document.createTextNode(res.t.n));
					
					var l5f				=	this.div.cloneNode(false);
					l5f.className		=	'ttClickForAction';
					this._ac(l5f, document.createTextNode('Click to reserve!'));
					this._ac(div, l5f);
					
					this._ac(div, l1);
				}
										
				var l3			=	this.div.cloneNode(false);
				l3.className	=	'ttNights';
				var nights		=	this._getDaysBetween(resStart, resEnd);
				var l3t			=	nights +' night';
				if(nights >1)	l3t += 's';
				this._ac(l3, document.createTextNode(l3t));
				this._ac(div, l3);
				
				var l4			=	this.div.cloneNode(false);
				l4.className	=	'ttCheckIn';
				this._ac(l4, document.createTextNode('Check-in : '));
				if(c)
				{
					if(res.t.i != null)	this._ac(l4, document.createTextNode(res.t.i +', '));
					else
					{
						var span		=	document.createElement('span');
						span.className	=	'warn2';
						this._ac(span, document.createTextNode('time? '));
						this._ac(l4, span);
					}
				}
				this._ac(l4, document.createTextNode(resStart.print('%a, %d %b %Y')));
				this._ac(div, l4);
				
				var l5			=	this.div.cloneNode(false);
				l5.className	=	'ttCheckOut';
				this._ac(l5, document.createTextNode('Check-out : '));
				if(c)
				{
					if(res.t.o != null)	this._ac(l5, document.createTextNode(res.t.o +', '));
					else
					{
						var span		=	document.createElement('span');
						span.className	=	'warn2';
						this._ac(span, document.createTextNode('time? '));
						this._ac(l5, span);
					}
				}
				this._ac(l5, document.createTextNode(resEnd.print('%a, %d %b %Y')));
				this._ac(div, l5);
				
				if(res.t.e!= null)
				{
					var l6			=	this.div.cloneNode(false);
					l6.className	=	'ttExpiry';
					if(nbSec < 0)	this._ac(l6, document.createTextNode('Expiry : About to expire.'));
					else
					{
						var nbSec		=	res.t.e;
							var fd		=	Math.floor(nbSec/86400);
							var fh		=	Math.floor((nbSec - (fd*86400))/3600);
							var fm		=	Math.floor((nbSec - (fd*86400) - (fh*3600)) / 60);
						if(fd != 0 && fh != 0)	this._ac(l6, document.createTextNode('Expiry : ' + fd + ' days ' + fh + 'h ' + fm + 'm'));
						else if(fh != 0)		this._ac(l6, document.createTextNode('Expiry : ' + fh + 'h ' + fm + 'm'));
						else					this._ac(l6, document.createTextNode('Expiry : ' + fm + 'm'));
					}
					this._ac(div, l6);
				}

				
				this._ac(this.container, div);
				this.resTooltips[this.pr].push(div);
				
				if (typeof(this.prop[this.pr].l) == 'object' && this.prop[this.pr].l != null )
				{
					if(nbr == 0)
					{
						if(this._compareDate(resStart, calStart) >= 0)
						{
							var firstAreaStart	=	calStart;
							var firstAreaEnd	=	resStart;
							
							var fTtArea			=	this.div.cloneNode(false);
							fTtArea.className	=	'tooltip';
							fTtArea.className	+=	' freeAreaTooltip';
							fTtArea.setAttribute('pos', 's');
							
							
							var l5f				=	this.div.cloneNode(false);
							l5f.className		=	'ttClickForAction';
							this._ac(l5f, document.createTextNode('Click to reserve!'));
							this._ac(fTtArea, l5f);
							
							var l1f				=	this.div.cloneNode(false);
							l1f.className		=	'ttFreeArea';
							this._ac(l1f, document.createTextNode('Available dates'));
							this._ac(fTtArea, l1f);
							
							if(this._compareDate(firstAreaStart, calStart) != 0)
							{
								var l2f					=	this.div.cloneNode(false);
								l2f.className			=	'ttNights';
								var nights				=	this._getDaysBetween(firstAreaStart, firstAreaEnd);
								var l2tf				=	nights +' night';
								if(nights >1)	l2tf 	+= 's';
								this._ac(l2f, document.createTextNode(l2tf));
								this._ac(fTtArea, l2f);
							}

							// Limits are now always available
							{
								var l3f				=	this.div.cloneNode(false);
								l3f.className		=	'ttCheckIn';
								this._ac(l3f, document.createTextNode('Check-in : '));
								if(this.prop[this.pr].l.p == null)	
									this._ac(l3f, document.createTextNode('No earlier dates'));
								else
								{
									var d	=	Date.parseDate(this.prop[this.pr].l.p, '%Y-%m-%d');
									this._ac(l3f, document.createTextNode(d.print('%a, %d %b %Y')));
								}
								this._ac(fTtArea, l3f);
							}
							
							var l4f				=	this.div.cloneNode(false);
							l4f.className		=	'ttCheckOut';
							this._ac(l4f, document.createTextNode('Check-out : '));
							this._ac(l4f, document.createTextNode(firstAreaEnd.print('%a, %d %b %Y')));
							this._ac(fTtArea, l4f);

							
							this._ac(this.container, fTtArea);
							
							this.areaTooltips.push(fTtArea);
						}
					}				
					
					var dateStart		=	resEnd;
					var dateEnd			=	(nbr != this.res.length-1)	?	Date.parseDate(this.res[nbr+1].s, '%Y-%m-%d')	:	new Date(this.ey, this.em);
					if(this._compareDate(dateStart, dateEnd) != 0)
					{
						var ttArea			=	this.div.cloneNode(false);
						ttArea.className	=	'tooltip';
						ttArea.className	+=	' freeAreaTooltip';
						ttArea.setAttribute('pos', 's');
							
						
						var l5f				=	this.div.cloneNode(false);
						l5f.className		=	'ttClickForAction';
						this._ac(l5f, document.createTextNode('Click to reserve!'));
						this._ac(ttArea, l5f);
						
						var l1			=	this.div.cloneNode(false);
						l1.className	=	'ttFreeArea';
						this._ac(l1, document.createTextNode('Available dates'));
						this._ac(ttArea, l1);
						
						if(this._compareDate(dateEnd, calEnd) != 0)
						{
							var l2			=	this.div.cloneNode(false);
							l2.className	=	'ttNights';
							var nights		=	this._getDaysBetween(dateStart, dateEnd);
							var l2t			=	nights +' night';
							if(nights >1)	l2t += 's';
							this._ac(l2, document.createTextNode(l2t));
							this._ac(ttArea, l2);
						}
						
						var l3			=	this.div.cloneNode(false);
						l3.className	=	'ttCheckIn';
						this._ac(l3, document.createTextNode('Check-in : '));
						this._ac(l3, document.createTextNode(dateStart.print('%a, %d %b %Y')));
						this._ac(ttArea, l3);
						
						// Limits are now always available
						{
							var l4			=	this.div.cloneNode(false);
							l4.className	=	'ttCheckOut';
							this._ac(l4, document.createTextNode('Check-out : '));
							if(this._compareDate(dateEnd, calEnd) == 0 && this.prop[this.pr].l.n == null)
								this._ac(l4, document.createTextNode('No further dates'));
							else if(this._compareDate(dateEnd, calEnd) == 0)
							{
								var d	=	Date.parseDate(this.prop[this.pr].l.n, '%Y-%m-%d');
								this._ac(l4, document.createTextNode(d.print('%a, %d %b %Y')));
							}
							else
								this._ac(l4, document.createTextNode(dateEnd.print('%a, %d %b %Y')));
							this._ac(ttArea, l4);
							this._ac(this.container, ttArea);
						}

						
						this.areaTooltips.push(ttArea);
					}
				}
			}
			else	this.resTooltips[this.pr].push('');

		}	
		
		for(var nbr=0; nbr<this.res.length; nbr++)
		{
			
			var res = this.res[nbr];
			
			var resStart			=	Date.parseDate(res.s, '%Y-%m-%d');
			var resEnd				=	Date.parseDate(res.e, '%Y-%m-%d');
			
			var em;
			var ey	=	this.ey;
			if(this.em-1 < 0)
			{
				em = 11;
				ey--;
			}
			else	em	=	this.em-1;
			
			var newCalEnd			=	new Date(ey, em);
				newCalEnd.setDate(newCalEnd.getMonthDays());			
			
			if(nbr == 0)
			{
				switch(this._compareDate(calStart, resStart))
				{
					case -1:	this._createFirstArea(calStart, resStart);															break;
					case  0:	this._createBlockEnd('n', this._getClass(res.m, res.c), null, nbr);	this.lim++;						break;
					case  1:	this._createFirstReservation(nbr, calStart, resEnd);												break;
				}
			}
			if(this._compareDate(calStart, resStart) != 1 && this._compareDate(newCalEnd, resStart) != 0)	this._createReservation(nbr, resStart, resEnd);
			
			if(nbr != this.res.length-1)
			{
				var nextResStart	=	Date.parseDate(this.res[nbr+1].s, '%Y-%m-%d');
				if(this._compareDate(resEnd, nextResStart) != 0)
				{					
					this._createBlockEnd(this._getClass(res.m, res.c), 'n', nbr, null);
					this._createFreeArea(resEnd, nextResStart);				
					this._createBlockEnd('n', this._getClass(this.res[nbr+1].m, this.res[nbr+1].c), null, nbr+1);
					if (typeof(this.prop[this.pr].l) == 'object' && this.prop[this.pr].l != null )	this.lim++;
				}
				else
				{
					this._createBlockEnd(this._getClass(res.m, res.c), this._getClass(this.res[nbr+1].m, this.res[nbr+1].c), nbr, nbr+1);
				}
			}
			else if(this._compareDate(resEnd, newCalEnd) <= 0)
			{
				this._createBlockEnd(this._getClass(res.m, res.c), 'n', nbr, null);
				this._createFreeArea(resEnd, calEnd);
				if (typeof(this.prop[this.pr].l) == 'object' && this.prop[this.pr].l != null )	this.lim++;
			}
			else if(this._compareDate(resEnd, calEnd) != 0)
				if (typeof(this.prop[this.pr].l) == 'object' && this.prop[this.pr].l != null )	this.lim++;
		}
	}
}

kigoMPCalendar.prototype.dump 					= 	function(elm)
{
	var pointer =  elm.parentNode;
	
	//See kigoCalendar.dump for reason to computing this here.
	
	var divCal						=	elm;
		
	var properties			=	this.div.cloneNode(false);	properties.className	=	'mpcal_properties'	;	divCal.appendChild(properties		);		
	var calendar			=	this.div.cloneNode(false);	calendar.className		=	'mpcal_calendar'	;	divCal.appendChild(calendar			);
		var dates			=	this.div.cloneNode(false);	dates.className			=	'mpcal_dates'		;	calendar.appendChild(dates			);
			var months		=	this.div.cloneNode(false);	months.className		=	'mpcal_months'		;	dates.appendChild(months			);
			var days		=	this.div.cloneNode(false);	days.className			=	'mpcal_days'		;	dates.appendChild(days				);
		var reservations	=	this.div.cloneNode(false);	reservations.className	=	'mpcal_reservations';	divCal.appendChild( reservations	);
			
	
	
	// we need to compute display values for the items.
	// To do this we'll be generating a one time dummy container set.
	var mb			=	this.div.cloneNode(false);
	mb.className	=	'mpcal_month';
	mb.innerHTML = '<p>&nbsp;</p>';
	months.appendChild(mb);
	var db			=	this.div.cloneNode(false);
	db.className	=	'mpcal_day';
	db.innerHTML = '<p>&nbsp;</p>';
	days.appendChild(db);
	var prop				=	this.div.cloneNode(false);
	prop.className			=	'mpcal_property';
	prop.innerHTML = '<p>&nbsp;</p>';
	properties.appendChild(prop);
	//values required

	/* 25/11/2009
	var dim = new Object();
	dim.mbH = mb.clientHeight;
	dim.dbW = db.clientWidth;
	dim.dbH = db.clientHeight;
	dim.propW = prop.clientWidth;
	dim.propH = prop.clientHeight;			
	*/
	var	dim = {
		'mbH'	:	mb.clientHeight,
		'dbW'	:	db.clientWidth,
		'dbH'	:	db.clientHeight,
		'propW'	:	prop.clientWidth,
		'propH'	:	prop.clientHeight
	};


	//detach the new container, now that we have the computed values we need
	vkDom.clean(elm);

	var dimensions = dim;
	
	//dimensions = pre computed CSS widths and heights
	
	this.container				=	vkDom.el(elm).cloneNode(false);	
	
		var properties			=	this.div.cloneNode(false);	properties.className	=	'mpcal_properties'	;	this._ac(this.container, properties	);		
		var calendar			=	this.div.cloneNode(false);	calendar.className		=	'mpcal_calendar'	;	this._ac(this.container, calendar	);
			var dates			=	this.div.cloneNode(false);	dates.className			=	'mpcal_dates'		;	this._ac(calendar, dates			);
				var months		=	this.div.cloneNode(false);	months.className		=	'mpcal_months'		;	this._ac(dates, months				);
				var days		=	this.div.cloneNode(false);	days.className			=	'mpcal_days'		;	this._ac(dates, days				);
				var days_names	=	this.div.cloneNode(false);	days_names.className	=	'mpcal_days_names'	;	this._ac(dates, days_names			);
			var reservations	=	this.div.cloneNode(false);	reservations.className	=	'mpcal_reservations';	this._ac(calendar, reservations		);
		var clear				=	this.div.cloneNode(false);	clear.className			=	'clear'				;	this._ac(this.container, clear		);
	
		calendar.id = 'mpcal_scroller';

	reservations.style.position = "relative";	
					
	var p, m, d, sbs;
	var nbProp					=	this.prop.length;
	this.nbMonths				=	(this.ey - this.sy) * 12 + (this.em - this.sm) + 1;
	
	var ligne1			=	this.div.cloneNode(false);
	ligne1.style.width = "1px";
	ligne1.className	=	'week_delimiter';
	ligne1.style.borderStyle = "none"; 
	ligne1.style.borderLeft = ligne1.style.borderTop = ligne1.style.borderBottom = "none";
	ligne1.style.height = (nbProp * dimensions.propH)  +  "px";
	ligne1.style.position = "absolute";	
	ligne1.style.top = "0px";
	
	var lastSplitter = - dimensions.dbW;
	
	for(m=0; m<this.nbMonths; m++)
	{
		var month	=	this.sm + m - 1 - 12 * (Math.ceil((this.sm + m - 1) / 12) - 1);
		var year	=	this.sy + Math.ceil((this.sm + m - 1) / 12) - 1;
		var date	=	new Date(year, month);
		var mb			=	this.div.cloneNode(false);
		mb.className	=	'mpcal_month';
		this._ac(mb, document.createTextNode(date.print('%B %Y')));
		this._ac(months, mb);
						
		for(d=1; d<=date.getMonthDays(); d++)
		{			
			var curDate = new Date(year,month,d).print('%a');
			
			if((curDate) == 'Mon') {
				var ligne = ligne1.cloneNode(false);				
				ligne.style.left = (lastSplitter  + (dimensions.dbW * d)) + "px";				
				reservations.appendChild(ligne);
			}
			
			var isToday = ((new Date(year,month,d).print('%Y %m %d')) == new Date().print('%Y %m %d'));
			
			if(isToday) {
				var ligneT = ligne1.cloneNode(false);				
				ligneT.style.backgroundColor = "#006600";
				if (ligne)
				ligneT.style.zIndex = kigo.intval(ligne.style.zIndex)+1;
				ligneT.style.left = (lastSplitter  + (dimensions.dbW * d)) + "px";				
				reservations.appendChild(ligneT);
			}
			
			var db			=	this.div.cloneNode(false);
			db.className	=	'mpcal_day';
			var z		=	(d<10)	?	'0'	:	'';
			this._ac(db, document.createTextNode(z+d));
			this._ac(days, db);
								
			if(m == 0 && d == 1)									db.className	+=	' first_day';
			if(m == this.nbMonths-1 && d == date.getMonthDays())	db.className	+=	' last_day';
			
			if(d == 1)												db.className	+=	' first';
			else if(d == date.getMonthDays())						db.className	+=	' last';
			else													db.className	+=	' middle';
			
			if(isToday)
				db.className	+=	' today';
			
			db.className	+=	(d % 2)	?	' odd'	:	' even';
			
			var db_name = this.div.cloneNode(false);
			db_name.className	=	'mpcal_day';
			this._ac(db_name, document.createTextNode(curDate.substring(0,1)));
			this._ac(days_names, db_name);
			
			if(m == 0 && d == 1)									db_name.className	+=	' first_day';
			if(m == this.nbMonths-1 && d == date.getMonthDays())	db_name.className	+=	' last_day';
			
			if(d == 1)												db_name.className	+=	' first';
			else if(d == date.getMonthDays())						db_name.className	+=	' last';
			else													db_name.className	+=	' middle';
			
			if(isToday)
				db_name.className	+=	' today';
			
			db_name.className	+=	(d % 2)	?	' odd'	:	' even';
			
			
			this.nbDays++;
			if(this.daySize	==	null)	this.daySize	=	dimensions.dbW;
		}
		
		lastSplitter = lastSplitter + date.getMonthDays() * dimensions.dbW;
		
		if(m == 0)													mb.className	+=	' first';
		else if(m	==	this.nbMonths-1)							mb.className	+=	' last';
		else														mb.className	+=	' middle';
		
		mb.className	+=	(m % 2)	?	' even'	:	' odd';
		mb.style.width		=	date.getMonthDays()	*	dimensions.dbW	+	'px';
	}	

	for(p=0; p<nbProp; p++)
	{
		if(p == 0)
		{
			var corner			=	this.div.cloneNode(false);
			corner.className	=	'mpcal_corner';
			this._ac(properties, corner);
		}
		
		var prop				=	this.div.cloneNode(false);
		prop.className			=	'mpcal_property';
		prop.className 			+=	(p % 2) ? ' even' : ' odd'; 


		//this._ac(prop, document.createTextNode(this.prop[p].p));

		var	propNameDiv;	// So that the property tooltip is set on this div only!

		prop.appendChild(
			(propNameDiv = kigoDom.create(
				'div',
				null,
				this.prop[p].ib != null ? { 'marginLeft' : '18px' } : null	// ib = null -> "ok for instant booking" concept doesn't apply
			).append(
				this.prop[p].p
			)).domNode()
		);

		// 05/05/2010
		if(this.prop[p].ib == true)
		{
			prop.appendChild(
				kigoDom.create(
					'div',
					null,
					{ 'position' : 'relative' }
				).append(
					kigoTooltip.register(
						kigoDom.create('img', { 'src' : '/img/instant-book.gif' }, { 'position' : 'absolute', 'left' : '0px', 'top' : '-16px' } ),
						'OK for instant booking',
						'se'
					)
				).domNode()
			);
		}


		this._ac(properties, prop);
		
		if(p == 0)				prop.className	+=	' first';
		else if(p == nbProp-1)	prop.className	+=	' last';
		else					prop.className	+=	' middle';
		
		if (typeof(this.onPropertyClick) == 'function') 
		{
			var propObject	= this.prop[p];
			this._createClickOnProperty(prop, propObject);
			prop.className	+= ' clickable';
		}
		
		/************************************************/
		/* Tooltip starts here */

		var tt				=	this.div.cloneNode(false);
		tt.className		=	'tooltip_grouped_calendar_prop';

		this._sa(tt, 'pos', 'se');
		//this._ac(this.container, tt);	// 17/05/2010 - the tooltip is NOT appended to the document
		//tt.setAttribute('pos', 'se');	// 25/11/2009 - already done above?

		/*
		var propName		=	this.div.cloneNode(false);
		propName.className	=	'ttPropName';
		this._ac(propName, document.createTextNode(this.prop[p].p));
		this._ac(tt, propName);
		*/
		// 25/11/2009 - Property name is now h1
		
		var	propName		=	this.h1.cloneNode(false);
		this._ac(propName, document.createTextNode(this.prop[p].p));
		this._ac(tt, propName);


		// 25/11/2009 - Now displays owner name both for owners and ra's
		// 17/05/2010 - Owner name (again) removed for owners
		if(this.prop[p].o != null)
		{
			var ownerName		=	this.div.cloneNode(false);
			ownerName.className	=	'ttPropOwner';
			this._ac(ownerName, document.createTextNode('[' + this.prop[p].o + ']'));
			this._ac(tt, ownerName);
		}						
		
		var	moreInfo;

		if(this.prop[p].b != null)
		{
			var	bedrooms = this.div.cloneNode(false);
			this._ac(bedrooms, document.createTextNode('Bedrooms: ' + this.prop[p].b));

			this._ac(tt, bedrooms);
		}

		if(this.prop[p].g != null)
		{
			var	guests = this.div.cloneNode(false);
			this._ac(guests, document.createTextNode('Max. guests: ' + this.prop[p].g));

			this._ac(tt, guests);
		}

		/*
		var moreInfo		=	document.createElement('span');		
		this._ac(moreInfo, document.createTextNode('Bedrooms: ' + this.prop[p].b));
		this._ac(tt, moreInfo);
		this._ac(tt, document.createElement('br'));
		moreInfo		=	document.createElement('span');		
		this._ac(moreInfo, document.createTextNode(' Max. guests:  ' + this.prop[p].g));
		this._ac(tt, moreInfo);		
		
		this._ac(tt, document.createElement('br'));
		*/


		var picture			=	this.div.cloneNode(false);
		var img				=	document.createElement('img');
		img.style.paddingTop 	=	img.style.paddingBottom 	=	"8px";
		img.src				=	(this.prop[p].f == null)	?	'/img/prop_default_list_photo.jpg'	:	'/tools/property_photos/SMALL/'+this.prop[p].f+'.jpg';
		this._ac(picture, img);
		picture.className	=	'ttPropPicture';
		this._ac(tt, picture);
		

		if(this.prop[p].a != null)
		{
			var	addr = this.div.cloneNode(false);
			this._ac(addr, document.createTextNode(this.prop[p].a));

			this._ac(tt, addr);
		}

		if(this.prop[p].c != null)
		{
			var	city = this.div.cloneNode(false);
			this._ac(city, document.createTextNode(this.prop[p].c));

			this._ac(tt, city);
		}


		// 24/11/2009 - Add property rates, if any

		if(this.prop[p].rt != null)
		{
			var	title = this.h2.cloneNode(false);
			var	content = this.div.cloneNode(false);

			this._ac(title, document.createTextNode('Rental Details'));
			content.className = 'rate_details';
			vkDom.setTextBr(content, this.prop[p].rt);

			this._ac(tt, title);
			this._ac(tt, content);

		
	
		}
		


		
		//kigoToolTips.add(propNameDiv.domNode(), tt);
		// Attempt to use the new tooltips...
		kigoTooltip.register(propNameDiv.domNode(), tt, 'se');


		/* 17/03/2010
		if(this.prop[p].rt != null)
		{
			kigoDebug.text('-------------------------------');
			kigoDebug.text(tt.parentNode.innerHTML);
			kigoDebug.text('-------------------------------');
		}
		*/

	}

	sbs	=	this._getScrollBarWidth();
	
	months.style.height			=	dimensions.mbH																			+	'px';
	months.style.width			=	this.nbDays		*	dimensions.dbW   													+	'px';
	days.style.height			=	dimensions.dbH																			+	'px';
//	kigoDebug.text(days.style.height);
	days.style.width			=	this.nbDays		*	dimensions.dbW														+	'px';
//	kigoDebug.text(days.style.width);
	dates.style.height			=	kigo.intval(days.style.height) + kigo.intval(days.style.height) +	kigo.intval(months.style.height)				+	'px';
	dates.style.width			=	this.nbDays								*	dimensions.dbW								+	'px';
	corner.style.height			=	(dimensions.mbH + 2*dimensions.dbH)																	+	'px';
	corner.style.width			=	dimensions.propW																		+	'px';
	properties.style.height		=	nbProp			*	dimensions.propH	+	kigo.intval(corner.style.height) - Math.ceil(dimensions.dbH/2)	+ 15 +sbs	+	'px';
	properties.style.width		=	dimensions.propW																		+	'px';
	reservations.style.height	=	nbProp			*	dimensions.propH										+ 15		+	'px';
	reservations.style.width	=	this.nbDays								*	dimensions.dbW								+	'px';
	calendar.style.height		=	kigo.intval(dates.style.height) - Math.ceil(dimensions.dbH/2) +	kigo.intval(reservations.style.height)	  +sbs	+	'px';

	this.reservations			=	reservations;
	
	for(this.pr=0; this.pr<nbProp; this.pr++)
	{
		this.stack	=	this.nbDays;
		this.res	=	this.prop[this.pr].r;
		this._createReservations();
	}
	
	pointer.replaceChild(this.container,elm);
	
}
/* /js/kigoUpload.js */ /* UTF8 COOKIE: éà */



/* 
	kigoUpload
*/



/*


var	tmp = new kigoUpload(
	{
		'upload_url'		:	'/upload.php',
		'multiple'			:	false,

		'file_types'		:	'*',
		'file_types_label'	:	'All files'
		'max_file_size'		:	10,
		'max_files'			:	7,		// ignored if multiple = false

		'title'				:	'Add photos',




		'legend'			:	'JPEG Image\n'+
								'Recommended width: 600 pixels\n'+
								'Recommended height: 400 pixels',



		//------ OLD VERSION ------//
		'title'				:	'Add photos',
		'subtitle'			:	'Select photos for your computer',
		'legend'			:	'JPEG Image\n'+
								'Recommended width: 600 pixels\n'+
								'Recommended height: 400 pixels',
		'button_text'		:	'Browse your computer'
		'button_width'		:	180,
		'button_height'		:	18,


		'upload_url'		:	'/upload.php',
		'close'				:	'Cancel',

		'on_upload'			:	function() { },

		'popup_width'		:	400,
		'popup_height'		:	280,

		'file_types'		:	'*',
		'file_types_label'	:	'All files'
		'max_file_size_mb'	:	10,
		'max_files'			:	7		// ignored if multiple = false
	}
);

tmp.onClose = function() { };


tmp.onFileUploaded() = function(serverData)
{

}


tmp.onDone = function(failedFilesArray)
{

}



tmp.open();	// Optionally reuses an open window




*/


function	kigoUpload(args)
{

	/************************/
	/* PROCESS ARGUMENTS    */
	
	function	getDefault(what, def)
	{
		if(typeof(what) == 'undefined')
			what = def;
		return what;
	}

	if(!kigo.is_object(args))
		args = {};

	// Mandatory arguments first

//	var	basePath = window.location.protocol + '//' + window.location.hostname;


	if((this.uploadUrl = getDefault(args['upload_url'], null)) == null)
		throw 'upload_url argument missing';
	this.uploadUrl = window.location.protocol + '//' + window.location.hostname + '/' + this.uploadUrl;

	this.multiple = kigo.intval(getDefault(args['multiple'], 0)) ? true : false ;

/*
	this.fileTypes = getDefault(args['file_types'], '*');
	this.fileTypesLabel = getDefault(args['file_types_label'], this.fileTypes == '*' ? 'All files' : this.fileTypes+' Files');
*/
	// file types parsing is now a bit better...
	
	this.fileTypes = getDefault(args['file_types'], []);
	if(!kigo.is_array(this.fileTypes))
	{
		if(kigo.is_string(this.fileTypes))
			this.fileTypes = [ this.fileTypes ];
		else
			this.fileTypes = [];
	}
	
	// See if we need to compile a default value for types label..
	if(!kigo.is_string(this.fileTypesLabel = getDefault(args['file_types_label'], null)))
	{
		if(!this.fileTypes.length)
			this.fileTypesLabel = 'All files';
		else
			this.fileTypesLabel = '(*.'+this.fileTypes.join(';*.').toUpperCase()+')' + ' files';
	}

	// Then build flash-friendly file types string
	// Linux files being case sensitive, d
	{
		var	tmp = '';

		// Loop on file types, use each in both lowercase and uppercase
		for(var i = 0; i < this.fileTypes.length; i++)
			tmp += (tmp.length ? ';*.' : '*.') + this.fileTypes[i].toLowerCase() + ';*.' + this.fileTypes[i].toUpperCase();

		this.fileTypes = tmp;
	}


	this.maxFileSizeKB = kigo.intval(getDefault(args['max_file_size'], 10240 /* 10 MB */));

	if(this.multiple)
		this.maxFiles = kigo.intval(getDefault(args['max_files'], 0));
	else
		this.maxFiles = 1;

	this.title = getDefault(args['title'], this.multiple ? 'Select multiple files' : 'Select a file');
	this.buttonLabel = getDefault(args['button_label'], this.multiple ? 'Click here, then use your mouse to select multiple files' : 'Click here to select a file');
	
	this.uploadProgressSingleText = getDefault(args['upload_text_single'], 'Uploading : %n');
	this.uploadProgressMultipleText = getDefault(args['upload_text_multiple'], 'Uploading file %c / %t : %n');

	/************************/
	/* INIT                 */

	this.isOpen = false;
	this.isDestroyed = false;

	this.instanceName = 'kigoUpload_'+(++kigoUpload.instanceCounter);
	kigoUpload.instances[this.instanceName] = this;
	this.movieId = this.instanceName+'_movie';
	//this.uploader = null;

	this.uploadPopup = null;
}


/************************/
/* CONSTANTS            */


// /!\ SYNC THESE CONSTANTS WITH kigoUpload.as AND kigoUpload.inc.php /!\

kigoUpload.FAIL_SYS = 1;
kigoUpload.FAIL_TOOMANY = 2;
kigoUpload.FAIL_EMPTY = 3;
kigoUpload.FAIL_BIG = 4;
kigoUpload.FAIL_TIMEOUT = 5;
kigoUpload.FAIL_SERVER = 6;
kigoUpload.FAIL_AUTH = 7;

// /!\ UPDATE WHEN NEW CODES ARE BEING ADDED /!\
kigoUpload.FAIL_MAX = kigoUpload.FAIL_AUTH;


/************************/
/* CONFIG               */


kigoUpload.CFG = {
	'url'					:	'/swf/kigoUpload.swf',
	'install_url'			:	'/swf/kigoUploadFlashUpgrade.swf',
	'min_version'			:	'9.0.28',
	'min_upgrade_version'	:	'6.0.65'
};

kigoUpload.flashUpdated = false;
kigoUpload.instanceCounter = 1;
kigoUpload.instances = {};
kigoUpload.popupsContainer = null;
kigoUpload.installPopup = null;
kigoUpload.progressBar = null;







/************************************************************************************************************************************************************************************/
/************************************************************************************************************************************************************************************/
//
//		PUBLIC API
//
/************************************************************************************************************************************************************************************/
/************************************************************************************************************************************************************************************/




// bool | null (flash install in progress)
kigoUpload.prototype.open = function()
{
	var	self = this;

	if(this.isDestroyed)
		return false;

	function	onFlashUnavailable()
	{
		if(!self.close())
			vkPopup.closeModal();

		vkPopup.yesno(
			'Adobe Flash Player required',
				'The Adobe Flash Player is required for this feature to work.\n'+
				'Please intall the Adobe Flash Player from the Adobe website: \n'+
				'\n'+
				'http://get.adobe.com/fr/flashplayer/\n'+
				'\n'+
				'Do you want to download and install Adobe Flash Player now (recommended)?',
			'ask',
			function(yn)
			{
				if(yn)
					window.open('http://get.adobe.com/fr/flashplayer/');
			},
			true
		);
	}

	function	onFlashInstalled()
	{
		if(!self.close())
			vkPopup.closeModal();

		vkPopup.message(
			'Adobe Flash Player installed',
				'The Adobe Flash Player was successfully installed.\n'+
				'You might need to restart your browser and/or your computer.',
			'ok'
		);
	}


	// Before going further, see if any flash version is installed...
	// (6.0.65 is the version that enable us to upgrade flash via an install swf file)
	if(!swfobject.hasFlashPlayerVersion(kigoUpload.CFG.min_upgrade_version))
	{
		if(kigoUpload.flashUpdated)
			kigoUpload.restartBrowserMessage();
		else
			kigoUpload.remoteFlashInstallMessage();
		return false;
	}


	// Before potentially running flash upgrade, make sure flash removal functions and popup container are ready...



	if(!kigoUpload.popupsContainer)
	{
		// Create shared popups container!

		kigoUpload.popupsContainer = document.createElement('div');
		kigoUpload.popupsContainer.style.display = 'none';

		vkDom.el('container').appendChild(kigoUpload.popupsContainer);

		// Fix Flashes own cleanup code so if the SWFMovie was removed from the page
		// it doesn't display errors.
		// (I dont have a clue what this means... ??)

		if(typeof(window['__flash__removeCallback']) != 'function')
		{
			window.__flash__removeCallback = function (instance, name) 
			{
				kigoDebug.text('__flash__removeCallback() call');
				try 
				{
					if (instance)
						instance[name] = null;

				} catch (flashEx) { }
			};
		}
	}



	// Okay, see if the version required by the upload script is implemented...
	if(!swfobject.hasFlashPlayerVersion(kigoUpload.CFG.min_version))
	{
		if(kigoUpload.flashUpdated)
			kigoUpload.restartBrowserMessage();
		else
			kigoUpload.installFlashPlayer();
		return null;
	}



	/********************************************************************************************/
	/* MAIN */

	if(
		this.isOpen &&
		!this.close()
	)
		return false;


	// Upload popup is now required, the user has to click the "select files" button since Flash 10
	
	if(this.uploadPopup)
	{
		vkDom.clean(this.uploadPopup);
		this.uploadPopup = null;
	}

	this.uploadPopup = document.createElement('div');
	this.uploadPopup.className = 'form kigo_upload';
	

	// Flash container - it has a class that is likely to be used as "loading" icon until flash is fully loaded...
	{
		var	flashContainer = document.createElement('div');
		flashContainer.id = this.movieId;
		flashContainer.className = 'flash_container';
		flashContainer.appendChild(document.createTextNode('Please wait...'));

		this.uploadPopup.appendChild(flashContainer);
	}

	// Cancel button
	{
		var	btnbar = document.createElement('div');
		btnbar.className = 'btnbar';

		var	button = document.createElement('button');
		// Opera crashes when the cancel button is hit while flash is loading (in fact, it is the only browser that is
		// responsible while the flash is loading).
		// Therefore, disable the cancel button until the load is finished
		/*
		if(vkDom.hasClass(document.body, 'ua-opera'))
			button.disabled = true;
		*/
		// Finally, do this for all browser
		button.disabled = true;

		button.appendChild(document.createTextNode('Cancel'));
		button.onclick = function()
		{
			self.close();
			return false;
		}

		btnbar.appendChild(button);
		this.uploadPopup.appendChild(btnbar);
	}

	kigoUpload.popupsContainer.appendChild(this.uploadPopup);


	vkPopup.localModal(
		this.title,
		400,
		70,
		this.uploadPopup
	);


	// Okay, open the flash movie
	swfobject.embedSWF(
		kigoUpload.CFG.url+'?'+(new Date()).getTime()+Math.floor(Math.random()*999999999),
		this.movieId,
		flashContainer.clientWidth,
		flashContainer.clientHeight,
		kigoUpload.CFG.min_version,	// swfobject requires it
		'', // Install movie is handled by us
		{
		},
		{
			'menu'				:	'false',
			'quality'			:	'high',
			'base'				:	'/',
			'wmode'				:	'opaque',
			'allowScriptAccess'	:	'sameDomain',
			'allowFullScreen'	:	'true',
			'devicefont'		:	'false',
			'salign'			:	'TL',
			'flashvars'			:	[
										// 06/07/2009 - new kigoUpload Flash
										'instance=',					encodeURIComponent(this.instanceName),
										'&debug=',						'true',
										'&session=',					encodeURIComponent(vkDom.getCookie('KIGO_SESSION')),
										'&maxFiles=',					encodeURIComponent(this.maxFiles),
										'&maxFileSizeKB=',				encodeURIComponent(this.maxFileSizeKB),
										'&uploadUrl=',					encodeURIComponent(this.uploadUrl),
										'&fileTypes=',					encodeURIComponent(this.fileTypes),
										'&fileTypesLabel=',				encodeURIComponent(this.fileTypesLabel),
										'&buttonLabel=',				encodeURIComponent(this.buttonLabel),
										'&uploadProgressSingleText=',	encodeURIComponent(this.uploadProgressSingleText),
										'&uploadProgressMultipleText=',	encodeURIComponent(this.uploadProgressMultipleText),
										''
									].join('')
		},
		{
			'align'				:	'middle'
		},
		function(e)
		{
			if(e.success)
			{
				// Another stupid IE bug
				window[self.movieId] = vkDom.el(self.movieId);
			}
		}
	);

	this.isOpen = true;

	return true;
}








// bool
kigoUpload.prototype.close = function()
{
	var	movieEl = vkDom.el(this.movieId);

	if(
		!this.isOpen ||
		this.isDestroyed ||
		!movieEl
	)
		return false;


	// Stop the movie (stop uploads, ...)

	try
	{
		movieEl.CallFunction('<invoke name="cancelUpload" returntype="javascript"></invoke>');
	}
	catch (e) { }


	if(this.uploadPopup)
	{
		// Close the modal
		vkPopup.closeModal();

		// Distroy the modal and everything it contains (including flash uploader)
		if(this.uploadPopup)
		{
			vkDom.clean(this.uploadPopup);
			this.uploadPopup = null;
		}
	}


	// Do NOT kill instances - we still exist!

	this.isOpen = false;
	return true;
}



// bool
kigoUpload.prototype.destroy = function()
{
	if(
		this.isDestroyed ||
		(this.isOpen && !this.close())
	)
		return false;

	kigoUpload.instances[this.instanceName] = null;
	delete kigoUpload.instances[this.instanceName];
	
	this.isDestroyed = true;
	return false;
}





/************************************************************************************************************************************************************************************/
/************************************************************************************************************************************************************************************/
//
//		FLASH CALLBACKS
//
/************************************************************************************************************************************************************************************/
/************************************************************************************************************************************************************************************/

// STATIC
kigoUpload.flashDebug = function(msg)
{
	kigoDebug.text('FLASH DEBUG > '+kigoUpload.unserialize(msg));
}

// Currently unimplemented
kigoUpload.prototype.flashReady = function()
{
	/*
	if(vkDom.hasClass(document.body, 'ua-opera'))	// Re-enable the button
		this.uploadPopup.getElementsByTagName('button')[0].disabled = false;
	*/
	// Finally do this for all browsers, their behaviour might change
	this.uploadPopup.getElementsByTagName('button')[0].disabled = false;
}




kigoUpload.prototype.browseCanceled = function()
{
	var	self = this;
	setTimeout(
		function()
		{
			if(self.close())
				self.onCancel();
		},
		1
	);
}


kigoUpload.prototype.uploadComplete = function(data)
{
	var	self = this;

	setTimeout(
		function()
		{
			data = kigoUpload.unserialize(data);
			self.onComplete(data.transfered, data.failed);
			self.close();
		},
		1
	);
}



// PRIVATE
kigoUpload.unserialize = function(data)
{
	try
	{
		eval('data = '+kigoUpload.base64decode(data)+';');
		return data;
	}
	catch (e) 
	{ 
		return null;
	}
}


/************************************************************************************************************************************************************************************/
/************************************************************************************************************************************************************************************/
//
//		DEFAULTS
//
/************************************************************************************************************************************************************************************/
/************************************************************************************************************************************************************************************/

kigoUpload.prototype.onCancel = function()
{
}

kigoUpload.prototype.onComplete = function(transfered, failed)
{
}


/************************************************************************************************************************************************************************************/
/************************************************************************************************************************************************************************************/
//
//		FLASH PLAYER INSTALL
//
/************************************************************************************************************************************************************************************/
/************************************************************************************************************************************************************************************/



kigoUpload.installFlashPlayer = function()
{
	var	installId = 'kigoUpload_flashInstall';

	// Only one instance at a time!
	if(kigoUpload.installPopup)
		return;	


	// CREATE INSTALL POPUP

	kigoUpload.installPopup = document.createElement('div');
	kigoUpload.installPopup.className = 'form kigo_upload_flash_install';

	/*
	{
		var	title = document.createElement('h2');
		title.appendChild(document.createTextNode('Adobe Flash Player Upgrade'));
		kigoUpload.installPopup.appendChild(title);
	}
	*/

	{
		var	placeholder = document.createElement('div');
		placeholder.id = installId;
		kigoUpload.installPopup.appendChild(placeholder);
	}


	{
		var	btnbar = document.createElement('div');
		btnbar.className = 'btnbar';

		var	button = document.createElement('button');
		button.appendChild(document.createTextNode('Cancel'));
		button.onclick = function()
		{
			vkPopup.closeModal();
			kigoUpload.popupsContainer.removeChild(kigoUpload.installPopup);
			kigoUpload.installPopup = null;
			return false;
		}

		btnbar.appendChild(button);
		kigoUpload.installPopup.appendChild(btnbar);
	}

	kigoUpload.popupsContainer.appendChild(kigoUpload.installPopup);

	// Open popup before loading the flash file...

	vkPopup.localModal(
		'Adobe Flash Player Upgrade',
		330,
		200,
		kigoUpload.installPopup
	);

	// Set the loading popup until the upgrade flash file is loaded
	vkPopup.status(
		'Adobe Flash Player Upgrade',
			'Connecting to Adobe Flash Player servers, please wait...',
		'wait'
	);


	swfobject.embedSWF(
		kigoUpload.CFG.install_url+'?'+(new Date()).getTime()+Math.floor(Math.random()*999999999),
		installId,
		310,
		137,
		kigoUpload.CFG.min_upgrade_version,
		'',
		{
		},
		{
			'play'				:	'true',
			'menu'				:	'false',
			'loop'				:	'false',
			'quality'			:	'high',
			'scale'				:	'noscale',
			'base'				:	'/',
			'wmode'				:	'opaque',
			'allowScriptAccess'	:	'sameDomain',
			'allowFullScreen'	:	'true',
			'devicefont'		:	'false',
			'salign'			:	'TL'
		},
		{
			'align'				:	'middle'
		}
	);

}

/******************************************************/
/* CALLBACKS */


kigoUpload.flashInstallReady = function()
{
	if(kigoUpload.installPopup)
	{
		vkPopup.closePopup();	// Close the loading popup...

		vkPopup.message(
			'Adobe Flash Player upgrade',
				'Please proceed to the Adobe Flash Player upgrade.\n'+
				'\n'+
				'This upgrade is required for using Kigo\'s file upload feature.',
			'info',
			null
		);
	}
}

kigoUpload.flashInstallTimedOut = function()
{
	kigoUpload._flashInstallFailed();
}

kigoUpload.flashInstallCanceled = function()
{
	kigoUpload._flashInstallFailed();
}

kigoUpload.flashInstallFailed = function()
{
	kigoUpload._flashInstallFailed();
}

kigoUpload.flashInstallComplete = function()
{
	kigoUpload._closeflashInstall();

	vkPopup.message(
		'Adobe Flash Player upgraded',
			'The Adobe Flash Player was successfully upgraded to the latest version.\n'+
			'\n'+
			//'It is highly recommended that you restart your browser before you continue using Kigo.',
			'You need to restart your browser for the changes to take effect.',
		'ok',
		null
	);

	kigoUpload.flashUpdated = true;	// for appropriate message when retrying to use it without closing the browser
}



/******************************************************/
/* PRIVATE */


kigoUpload._closeflashInstall = function()
{
	if(kigoUpload.installPopup)
	{
		vkPopup.closeModal();
		kigoUpload.installPopup.parentNode.removeChild(kigoUpload.installPopup);
		kigoUpload.installPopup = null;
	}
}

kigoUpload._flashInstallFailed = function()
{
	if(kigoUpload.installPopup)
	{
		kigoUpload._closeflashInstall();
		kigoUpload.remoteFlashInstallMessage();
	}
}


/******************************************************/
/* MESSAGES */




/************************************************************************************************************************************************************************************/
/************************************************************************************************************************************************************************************/
//
//		FLASH AVAILABILITY MESSAGES (NOT LOGIC)
//
/************************************************************************************************************************************************************************************/
/************************************************************************************************************************************************************************************/

kigoUpload.restartBrowserMessage = function()
{
	vkPopup.message(
		'Please restart your browser',
			'The Adobe Flash Player was successfully upgraded to the latest version.\n'+
			'\n'+
			'However, you need to restart your browser for the changes to take effect.',
		'warn',
		null
	);
}


kigoUpload.remoteFlashInstallMessage = function()
{
	vkPopup.yesno(
		'Adobe Flash Player required',
			'The Adobe Flash Player is required for this file upload to work.\n'+
			'Please intall the Adobe Flash Player from the Adobe website: \n'+
			'\n'+
			'http://get.adobe.com/fr/flashplayer/\n'+
			'\n'+
			'Do you want to download and install Adobe Flash Player now (recommended)?',
		'ask',
		function(yn)
		{
			if(yn)
				window.open('http://get.adobe.com/fr/flashplayer/');
		},
		true
	);

}







/************************************************************************************************************************************************************************************/
/************************************************************************************************************************************************************************************/
//
//		EXTERNAL TOOLS
//
/************************************************************************************************************************************************************************************/
/************************************************************************************************************************************************************************************/

kigoUpload.base64decode = function(input)
{
	// Slightly modified from http://www.webtoolkit.info/javascript-base64.html

	function	utf8_decode(utftext)
	{
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;
 
		while ( i < utftext.length ) {
 
			c = utftext.charCodeAt(i);
 
			if (c < 128) {
				string += String.fromCharCode(c);
				i++;
			}
			else if((c > 191) && (c < 224)) {
				c2 = utftext.charCodeAt(i+1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			}
			else {
				c2 = utftext.charCodeAt(i+1);
				c3 = utftext.charCodeAt(i+2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}
 
		}
 
		return string;
	}

	var	keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

	var output = '';
	var chr1, chr2, chr3;
	var enc1, enc2, enc3, enc4;
	var i = 0;

	input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');

	while (i < input.length) {

		enc1 = keyStr.indexOf(input.charAt(i++));
		enc2 = keyStr.indexOf(input.charAt(i++));
		enc3 = keyStr.indexOf(input.charAt(i++));
		enc4 = keyStr.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);
		}

	}

	output = utf8_decode(output);

	return output;
}


/* /js/kigoAccordion.js */ /* UTF8 COOKIE: éà */

/*

Javascript based accordion.
Since this is being lately integrated this into existing code, we don't generate the DOM tree, but accept references to existing DOM elements.

*/

function	kigoAccordion()	// [ onlyOneOpen = true ]
{
	this.onlyOneOpen = !arguments.length || arguments[0] == true;
	// Register myself
	kigoAccordion.instances[this.myInstance = kigoAccordion.instances.length] = this;
	this.items = [];
}


kigoAccordion.instances = [];

kigoAccordion.prototype.add = function(controler, controlee)
{
	if(
		!(controler = vkDom.el(controler)) ||
		!(controlee = vkDom.el(controlee))
	)
		return null;

	var	idx = this.items.length, self = this;

	vkDom.addClass(controler, 'accordion controler closed');
	vkDom.addClass(controlee, 'accordion controlee closed');

	if(vkDom.hasClass(document.body, 'ua-msie-6'))
	{
		vkDom.removeClass(controler, 'closed');
		vkDom.removeClass(controlee, 'closed');
		vkDom.addClass(controler, 'accordion_controler_closed');
		vkDom.addClass(controlee, 'accordion_controlee_closed');
	}



	// For html helpers...
	controlee.setAttribute('accordion_instance', this.myInstance);
	controlee.setAttribute('accordion_idx', idx);

	controler.onclick = function()
	{
		self.onClick(idx, self.onlyOneOpen);
	}

	this.items[idx] = {
		'controler'	:	controler,
		'controlee'	:	controlee,
		'open'		:	false
	};

	this.close(idx);

	return idx;
}


// There is usually no reason to overwrite this one!


// void
kigoAccordion.prototype.onClick = function(idx, onlyOneOpen)
{
	if(!kigo.is_int(idx) || idx < 0 || idx > this.items.length)
		return;

	if(this.items[idx].open)
		this.close(idx);
	else
	{
		if(onlyOneOpen)
			this.closeAll();
		this.open(idx);
	}
}

// void
kigoAccordion.prototype.close = function(idx)
{
	if(!kigo.is_int(idx) || idx < 0 || idx > this.items.length)
		return;

	vkDom.removeClass(this.items[idx].controler, 'open');
	vkDom.addClass(this.items[idx].controler, 'closed');

	vkDom.removeClass(this.items[idx].controlee, 'open');
	vkDom.addClass(this.items[idx].controlee, 'closed');


	if(vkDom.hasClass(document.body, 'ua-msie-6'))
	{
		vkDom.removeClass(this.items[idx].controler, 'closed');
		vkDom.removeClass(this.items[idx].controlee, 'closed');

		vkDom.removeClass(this.items[idx].controler, 'accordion_controler_open');
		vkDom.addClass(this.items[idx].controler, 'accordion_controler_closed');

		vkDom.removeClass(this.items[idx].controlee, 'accordion_controlee_open');
		vkDom.addClass(this.items[idx].controlee, 'accordion_controlee_closed');
	}

	this.items[idx].open = false;

	// 02/12/2009
	this.onClose(idx);
}

// void
kigoAccordion.prototype.closeAll = function()
{
	var	ret = true;

	for(var i = 0; i < this.items.length; i++)
	{
		if(this.items[i].open)
			this.close(i);
	}
}


// void
kigoAccordion.prototype.open = function(idx)
{
	if(!kigo.is_int(idx) || idx < 0 || idx > this.items.length)
		return;

	vkDom.removeClass(this.items[idx].controler, 'closed');
	vkDom.addClass(this.items[idx].controler, 'open');

	vkDom.removeClass(this.items[idx].controlee, 'closed');
	vkDom.addClass(this.items[idx].controlee, 'open');

	if(vkDom.hasClass(document.body, 'ua-msie-6'))
	{
		vkDom.removeClass(this.items[idx].controler, 'open');
		vkDom.removeClass(this.items[idx].controlee, 'open');

		vkDom.removeClass(this.items[idx].controler, 'accordion_controler_closed');
		vkDom.addClass(this.items[idx].controler, 'accordion_controler_open');

		vkDom.removeClass(this.items[idx].controlee, 'accordion_controlee_closed');
		vkDom.addClass(this.items[idx].controlee, 'accordion_controlee_open');
	}

	this.items[idx].open = true;

	// 02/12/2009
	this.onOpen(idx);
}

// void
kigoAccordion.prototype.openByChild = function(idx, onlyOneOpen)
{
	if(!kigo.is_int(idx) || idx < 0 || idx > this.items.length)
		return;

	if(!this.items[idx].open)
		this.onClick(idx, onlyOneOpen == null ? this.onlyOneOpen : onlyOneOpen ? true : false);	// If null, use default
}




// STATIC, bool
kigoAccordion.openByChild = function(element)	// [, onlyOneOpen=true ]
{
	var	instance, idx;

	if(!(element = vkDom.el(element)))
		return false;

	do
	{
		//kigoDebug.text('Testing element <'+element.tagName+'>');

		if(
			vkDom.hasClass(element, 'accordion') &&
			vkDom.hasClass(element, 'controlee')
		)
		{
			if(!kigo.is_object(instance = kigoAccordion.instances[kigo.intval(element.getAttribute('accordion_instance'))]))
				return false;

			instance.openByChild(kigo.intval(element.getAttribute('accordion_idx')), arguments.length < 2 ? null : arguments[1]);
			return true;
		}

	} while((element = element.parentNode) && element.nodeType == 1);

	return false;
}

// 02/12/2009
kigoAccordion.prototype.onOpen = function(idx)
{

}

kigoAccordion.prototype.onClose = function(idx)
{

}

/* /js/kigoDom.js */ /* UTF8 COOKIE: éà */

/*

02/12/2009		Release #0


Type	Returns						Method					Note

		kigoDom						kigoDom(element)		Creates a kigoDom instance from a DOM element.
															Throws exception if invalid/inexistant element was provided.

		kigoDom						kigoDom(id)				Creates a kigoDom instance from a unique element id (string).
															Throws exception if invalid/inexistant id was provided. Therefore, use getById() if
															unsure whether the element exists.

		kigoDom						kigoDom(domInstance)	Creates a kigoDom instance from an existing kigoDom instance. This is a reference to (rather than a clone of) the existing instance.

static	kigoDom | null				create(tagName [, attributes=null [, styles=null [, events=null ] ] ])

															Creates a kigoDom instance holding Element of tagName type.
															Throws exception on failure (ie. bad tagName)

static	kigoDom						getBody()				Returns the document body.

static	kigoDom | null				getById(id)				Returns dom Element by unique id.

static	Array(kigoDom) | null		getByTagName(tag)		Returns an array of element found by tag name.

static	kigoDom | null				getByTagName(tag, idx)	Returns an idx-th element found by tag name, null if not found.

		kigoDom | null				getById(id)				Returns child dom Element by unique id

		Array(kigoDom) | null		getByTagName(tag)		Returns an array of child elements found by tag name

		kigoDom | null				getByTagName(tag, idx)	Returns an idx-th element child of the current element found by tag name, null if not found.


		Element						domNode()				Returns the DOM Element

		kigoDom						empty()					Empties the node and returns the reference to itself

		kigoDom						append(child [, child [, ... ] ])
		kigoDom						appendArray(children)

															Appends one or more children.
															kigoDom instances, DOM Elements, strings (tranformed to text nodes), and arrays holding any of these are accepted.

		kigoDom | null				parentNode()			Returns the parent node kigoDom instance, or null if there is no parent

		kigoDom						orphanize()				Removes the element from its parent node, if any.

		kigoDom						clone([deep=true])		Clones the node

		kigoDom						swap(other)				Swaps the node with 'other' node. Nodes may be in different dom trees, and may even not be in a node tree at all.
															The method also swaps instances - this becomes other, and other becomes this.


Possible extensions (only if really required):

TODO:

	PRIVATE isDomNode() method. Currently we're treating every object that is neither kigoDom instance, nor null, nor array in some places, as being a DOM node.


*/

//////////////////////////////////////////////////////////////////
// CONSTRUCTOR

function	kigoDom(el)
{
	if(kigoDom.isNode(el))
		this.node = el;
	else if(kigoDom.isInstance(el))
		this.node = el.domNode();
	else
	{
		if(typeof(el) == 'string')
			this.node = document.getElementById(el);
		else
			this.node = null;

		if(!this.node)
			throw 'kigoDom::kigoDom(): Invalid DOM node'+(kigo.is_string(el) ? '('+el+')' : '');
	}
}

//////////////////////////////////////////////////////////////////
// PRIVATE PROPERTIES & METHODS

kigoDom.__embryon__ = {};

kigoDom.isInstance = function(i)
{
	return kigo.is_object(i) && i.constructor === kigoDom;
}

kigoDom.isNode = function(n)
{
	// Check whether this is an html node - this is a quick & dirty check - TODO: write a better one
	return kigo.is_object(n) && typeof(n['tagName']) == 'string'/* && !n.hasOwnProperty('tagName')*/;
}

kigoDom.mapAttrName = function(name)
{
	switch(name.toLowerCase())
	{
		case 'defaultchecked':
			return 'defaultChecked';

		case 'class':
		case 'classname':
			return 'className';
		
		case 'colspan':
			return 'colSpan';

		case 'maxlength':
			return 'maxLength';

		case 'readonly':
			return 'readOnly';

		case 'rowspan':
			return 'rowSpan';

		default:
			return name;	// Such as written by the developer
	}
}




//////////////////////////////////////////////////////////////////
// STATIC METHODS

kigoDom.getBody = function()
{
	return new kigoDom(document.body);
}

kigoDom.getById = function(id)
{
	var	el = document.getElementById(id);

	if(el)
		return new kigoDom(el);

	return null;
}

kigoDom.getByTagName = function(tagName)	// [, idx]
{
	var	els = document.getElementsByTagName(tagName);

	if(arguments.length > 1)
	{
		// Return single element
		if(typeof(els[arguments[1]]) != 'undefined')
			return new kigoDom(els[arguments[1]]);

		return null;
	}
	else
	{
		// Return array
		var	arr = [];

		for(var i = 0; i < els.length; i++)
			arr[i] = new kigoDom(els[i]);

		return arr;
	}
}




// Returns the created element
kigoDom.create = function(tagName)	// [, attributes [, styles [, events ] ] ]
{
	if(typeof(tagName) != 'string')
		throw 'kigoDom::create(): Bad argument';

	// TODO: Check whether cloning node is faster that creating nodes in all browsers - possibly do a browser cbeck and choose the faster method
	// Meanwhile, use cloning as it is significantly faster on very slow browsers

	if(typeof(kigoDom.__embryon__[tagName = tagName.toLowerCase()]) == 'undefined')
	{
		try
		{
			kigoDom.__embryon__[tagName] = document.createElement(tagName);
		}
		catch(e)
		{
			throw 'Failed creating element: '+e;
		}
	}

	var el = kigoDom.__embryon__[tagName].cloneNode(false);
	var	attrName;

	// SETUP ATTRIBUTES
	if(arguments.length > 1 && kigo.is_object(arguments[1]))
	{
		for(name in arguments[1])
		{
			if(arguments[1].hasOwnProperty(name))
			{
				// Intercept a couple of properties that some browsers dislike setting with setAttribute(), or that need special handling (ie. form elements)

				switch(attrName = kigoDom.mapAttrName(name))	// Also, 'repair' frequently misstypes attribute names with mapAttrName()
				{
					case 'id':
						el.id = arguments[1][name];
						break;

					case 'className':
						el.className = arguments[1][name];
						break;

					case 'checked':
					case 'defaultChecked':

						if(el.tagName == 'INPUT')
						{
							if(el.type.toLowerCase() == 'checkbox')
							{
								if(attrName == 'checked')
									el.checked = arguments[1][name] ? true : false;

								// 23/06/2010 - Why weren't we doing this for checkboxes?
//								else
//									el.defaultChecked = arguments[1][name] ? true : false;

								// Always set defaultChecked
								el.defaultChecked = arguments[1][name] ? true : false;
							}
							else if(el.type.toLowerCase() == 'radio')
							{
								if(attrName == 'checked')
									el.checked = arguments[1][name] ? true : false;

								// Always set defaultChecked
								el.defaultChecked = arguments[1][name] ? true : false;
							}
							else
								el.setAttribute(attrName, arguments[1][name]);
						}
						else
							el.setAttribute(attrName, arguments[1][name]);

						break;						

					case 'value':

						if(
							el.tagName == 'INPUT' ||
							el.tagName == 'TEXTAREA'
						)
							el.value = arguments[1][name];
						else
							el.setAttribute(attrName, arguments[1][name]);
						break;

					case 'readOnly':

						if(
							el.tagName == 'INPUT' ||
							el.tagName == 'TEXTAREA'
						)
							el.readOnly = arguments[1][name] ? true : false;
						else
							el.setAttribute(attrName, arguments[1][name]);
						break;

						/*
						else if(el.tagName == 'TEXTAREA')
							el.value = arguments[1][name];
							//el.appendChild(document.createTextNode(arguments[1][name]));
						else
							el.setAttribute(attrName, arguments[1][name]);
						break;
						*/
	
/* Ooops, IE doesn't like this! - I guess that the value becomes available only if the input's "type" attribute was set *before*...

					case 'value':
						// If 'value' is a prototype property, prefer using obj.value = 'xx' rather that setAttribute.
						// Otherwise, use setAttribute()
						if(
							!el.hasOwnProperty(name) &&		// is prototype or doesn't exist
							typeof(el[name]) != 'undefined'	// ... does exist
						)
							el.value = arguments[1][name];
						else
							el.setAttribute(name, arguments[1][name]);
						break;
*/
					default:
						el.setAttribute(attrName, arguments[1][name]);
				}
			}
		}
	}

	// SETUP STYLES
	if(arguments.length > 2 && kigo.is_object(arguments[2]))
	{
		for(name in arguments[2])
		{
			if(arguments[2].hasOwnProperty(name))
			{
				// Intercept a couple of styles that browsers will refuse to handle by their proper names
				switch(name)
				{
					case 'cssFloat':
					case 'float':		// This is a handy (but invalid) shortcut

						el.style['cssFloat'] = arguments[2][name];

						// Stupid IE, again and again
						if(vkDom.hasClass(document.body, 'ua-msie'))
							el.style['styleFloat'] = arguments[2][name];

						break;

					default:
						el.style[name] = arguments[2][name];
				}
			}
		}
	}

	// SETUP EVENTS
	if(arguments.length > 3 && kigo.is_object(arguments[3]))
	{
		for(name in arguments[3])
		{
			if(arguments[3].hasOwnProperty(name) && name == name.toLowerCase())
				el[kigo.substr(name, 0, 2) == 'on' ? name : 'on'+name] = arguments[3][name];
		}
	}


	return new kigoDom(el);
}


//////////////////////////////////////////////////////////////////
// NON-STATIC METHODS

kigoDom.prototype.domNode = function()
{
	return this.node;
}


kigoDom.prototype.getById = function(id)
{
	var el = this.node.getElementById(id);

	if(el)
		return new kigoDom(el);

	return null;
}

kigoDom.prototype.getByTagName = function(tagName)	// [, idx]
{
	var	els = this.node.getElementsByTagName(tagName);

	if(arguments.length > 1)
	{
		// Return single element
		if(typeof(els[arguments[1]]) != 'undefined')
			return new kigoDom(els[arguments[1]]);

		return null;
	}
	else
	{
		// Return array
		var	arr = [];

		for(var i = 0; i < els.length; i++)
			arr[i] = new kigoDom(els[i]);

		return arr;
	}
}


kigoDom.prototype.empty = function()
{
	while(this.node.firstChild)
		this.node.removeChild(this.node.firstChild);
	return this;
}




kigoDom.prototype.append = function()	// child [, child [, child [, ...] ] ]
{
	return this.appendArray(arguments);
}


kigoDom.prototype.appendArray = function(kids)
{
	for(var i = 0; i < kids.length; i++)
	{
		switch(typeof(kids[i]))
		{
			// kigoDom or Dom instance
			case 'object':

				// kigoDom instance (the most frequent usage case)
				if(kigoDom.isInstance(kids[i]))
					this.node.appendChild(kids[i].domNode());
				else if(kids[i] != null)
				{
					if(kigo.is_array(kids[i]))
						this.appendArray(kids[i]);
					else
						this.node.appendChild(kids[i]);
				}
				// Silently skip null values

				break;

			// Text nodes
			case 'string':
			case 'number':
				this.node.appendChild(
					document.createTextNode(kids[i])
				);
				break;

			default:
				// Silently skip others
		}
	}

	return this;
}


kigoDom.prototype.parentNode = function()
{

	/*
		We cannot use the parentNode attribute to determine whether the node has a parent, because of a "yet another" stupid IE bug:

		http://www.omacronides.com/notes/090424-parentnode-ie-et-les-autres/

		Use IE's parentElement!
	*/

	if(
		!this.node.parentNode ||
		kigo.is_null(this.node.parentElement)	// IE specific
	)
		return null;

	return new kigoDom(this.node.parentNode);
}


kigoDom.prototype.orphanize = function()
{
	var p;

	if(p = this.parentNode())
		p.node.removeChild(this.node);

	return this;
}


kigoDom.prototype.clone = function()	// [deep=true]
{
	return new kigoDom(this.node.cloneNode(!arguments.length || arguments[0] ? true : false));
}

kigoDom.prototype.swap = function(other)
{
	var	tmp;

	if(!kigoDom.isInstance(other))
		other = new kigoDom(other);

	// Use self::parentNode() to ensure there is a parent, regardless of IE's bugs
	// Use replaceChild() to ensure the element is inserted at the right place

	if(this.parentNode())
	{
		if(other.parentNode())
		{
			// Both have a parent..
			tmp = kigoDom.create('span');

			other.node.parentNode.replaceChild(tmp, other.node);
			this.node.parentNode.replaceChild(other.node, this.node);
			tmp.parentNode.replaceChild(this.node, tmp);
		}
		else
			this.node.parentNode.replaceChild(other.node, this.node);
	}
	else if(other.parentNode())
		other.node.parentNode.replaceChild(this.node, other.node);


/*
	if(this.node.parentNode)
	//if(kigo.is_object(this.node.parentNode) && kigo.is_function(this.node.parentNode['replaceChild']))
	{
		if(other.node.parentNode)
		//if(kigo.is_object(other.node.parentNode) && kigo.is_function(other.node.parentNode['replaceChild']))
		{
			alert('CASE 1.A');
			// Both have parent..
			tmp = kigoDom.create('span');

			other.node.parentNode.replaceChild(tmp, other.node);
			this.node.parentNode.replaceChild(other.node, this.node);
			tmp.parentNode.replaceChild(this.node, tmp);
		}
		else
		{
			alert('CASE 1.B');
			this.node.parentNode.replaceChild(other.node, this.node);
		}
	}
	else if(other.node.parentNode)
	//else if(kigo.is_object(other.node.parentNode) && kigo.is_function(other.node.parentNode['replaceChild']))
	{
		alert('CASE 2');
		other.node.parentNode.replaceChild(this.node, other.node);
	}
	else
		alert('CASE 3');
*/


	// Finally, "swap" instances
	tmp = this.node;
	this.node = other.node;
	other.node = tmp;

	return this;
}



/* /js/kigoAssoc.js */ /* UTF8 COOKIE: éà */

/*

16/12/2009		Release #0


Type	Returns				Method							Note

		kigoAssoc			kigoAssoc(obj)					Creates an instance from an object

		bool				isset(key)						Checks whether the key exists

		kigoAssoc			unset(key)						Unsets the key

		kigoAssoc			set(key, value)					Sets a value by key

		kigoAssoc | null	reindex(oldKey, newKey)			Reindexes oldKey by newKey. Returns NULL if oldKey did not exist.
															
		mixed				get(key [, default=null])		Returns the value for a key, default if not found.

		int					count()							Returns the number of values

		int					sizeof()						Alias to count()

		bool				find(value [, strict=false])	Looks up a value

		void				forEach(callback [, data])		Runs the callback for every element. 
															The callback recieves the following parameters:
															callback(key, value, <kigoAssoc instance> [, data])
															The loop is done on a list clone, therefore changes
															made to the instance are not visible during the loop.

		object				object()						Returns the object.

		array				array()							Returns object elements in an Array object. Keys are lost.


KNOWN ISSUES WITH CHROME:

http://code.google.com/p/chromium/issues/detail?id=883
http://code.google.com/p/chromium/issues/detail?id=20144


*/

function	kigoAssoc(assoc)
{
	if(kigoAssoc.isInstance(assoc))
		this.assoc = assoc.assoc;
	else if(kigo.is_object(assoc))
		this.assoc = assoc;
	else
		this.assoc = {};
}




// bool
kigoAssoc.prototype.isset = function(key)
{
	this.testKey(key);

	if(typeof(this.assoc[key]) != 'undefined')
		return this.assoc.hasOwnProperty(key);

	return false;
}

// kigoAssoc
kigoAssoc.prototype.unset = function(key)
{
	this.testKey(key);

	if(
		typeof(this.assoc[key]) != 'undefined' &&
		this.assoc.hasOwnProperty(key)
	)
		delete this.assoc[key];

	return this;
}

// kigoAssoc
kigoAssoc.prototype.set = function(key, value)
{
	this.testKey(key);

	if(typeof(this.assoc[key]) != 'undefined' && !this.assoc.hasOwnProperty(key))
		throw 'Attempt to use a key that matches prototype chain property or method';

	this.assoc[key] = value;

	return this;
}


// kigoAssoc | null
kigoAssoc.prototype.reindex = function(oldKey, newKey)
{
	this.testKey(oldKey);
	this.testKey(newKey);

	if(typeof(this.assoc[newKey]) != 'undefined' && !this.assoc.hasOwnProperty(newKey))
		throw 'Attempt to use a key that matches prototype chain property or method';

	if(
		typeof(this.assoc[oldKey]) != 'undefined' &&
		this.assoc.hasOwnProperty(oldKey)
	)
	{
		this.assoc[newKey] = this.assoc[oldKey];
		delete this.assoc[oldKey];
		return this;
	}
	else
		return null;
}


// mixed
kigoAssoc.prototype.get = function(key)	// [, default=null ]
{
	this.testKey(key);

	if(!this.assoc.hasOwnProperty(key))
		return arguments.length > 1 ? arguments[1] : null;

	return this.assoc[key];
}



// int
kigoAssoc.prototype.count = kigoAssoc.prototype.sizeof = function()
{
	var	s = 0;

	for(var key in this.assoc)
	{
		if(this.assoc.hasOwnProperty(key))
			++s;
	}

	return s;
}


kigoAssoc.prototype.forEach = function(callback)	// [, data]
{
	var	clone = this.object();

	if(arguments.length > 1)
		throw 'kigoAssoc::forEach() : oops, please dont use data argument, its reserved for future use!';	// -> use it for sorting

	for(var key in clone)
	{
		if(clone.hasOwnProperty(key))
			callback(key, clone[key], this, arguments[1]);
	}
}

kigoAssoc.prototype.find = function(value)	// [, strict=false]
{
	var	strict = arguments.length > 1 && arguments[1];

	if(arguments.length > 1 && arguments[1])
	{
		// strict
		for(var key in this.assoc)
		{
			if(this.assoc.hasOwnProperty(key) && this.assoc[key] === value)
				return true;
		}
	}
	else
	{
		// non-strict
		for(var key in this.assoc)
		{
			if(this.assoc.hasOwnProperty(key) && this.assoc[key] == value)
				return true;
		}
	}
	return false;
}


kigoAssoc.prototype.object = function()
{
	// Returns a *CLONED* object

	var	clone = {};

	for(var key in this.assoc)
	{
		if(this.assoc.hasOwnProperty(key))
			clone[key] = this.assoc[key];
	}

	return clone;
}


kigoAssoc.prototype.array = function()
{
	var	arr = [];

	this.forEach(
		function(key, value)
		{
			arr.push(value);
		}
	);

	return arr;
}


//////////////////////////////////////////////////////////////////
// PRIVATE PROPERTIES & METHODS


// void
kigoAssoc.prototype.testKey = function(key)
{
	//if(!kigo.is_string(key) || !key.length)	// Nb: empty strings are okay!
	// Accept strings and integer keys!
	if(!kigo.is_string(key) && !kigo.is_int(key))
		throw 'Bad key value';
}

// static bool
kigoAssoc.isInstance = function(i)
{
	return kigo.is_object(i) && i.constructor === kigoAssoc;
}

/*  Copyright Mihai Bazon, 2002-2005  |  www.bazon.net/mishoo
 * -----------------------------------------------------------
 *
 * The DHTML Calendar, version 1.0 "It is happening again"
 *
 * Details and latest version at:
 * www.dynarch.com/projects/calendar
 *
 * This script is developed by Dynarch.com.  Visit us at www.dynarch.com.
 *
 * This script is distributed under the GNU Lesser General Public License.
 * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
 */

/*
28/06/2010 - Patched yet another stupid Chrome bug (they're getting closer and closer to IE)
See http://www.dynarch.com/projects/calendar/old/, comment #124
*/

 Calendar=function(firstDayOfWeek,dateStr,onSelected,onClose){this.activeDiv=null;this.currentDateEl=null;this.getDateStatus=null;this.getDateToolTip=null;this.getDateText=null;this.timeout=null;this.onSelected=onSelected||null;this.onClose=onClose||null;this.dragging=false;this.hidden=false;this.minYear=1970;this.maxYear=2050;this.dateFormat=Calendar._TT["DEF_DATE_FORMAT"];this.ttDateFormat=Calendar._TT["TT_DATE_FORMAT"];this.isPopup=true;this.weekNumbers=true;this.firstDayOfWeek=typeof firstDayOfWeek=="number"?firstDayOfWeek:Calendar._FD;this.showsOtherMonths=false;this.dateStr=dateStr;this.ar_days=null;this.showsTime=false;this.time24=true;this.yearStep=2;this.hiliteToday=true;this.multiple=null;this.table=null;this.element=null;this.tbody=null;this.firstdayname=null;this.monthsCombo=null;this.yearsCombo=null;this.hilitedMonth=null;this.activeMonth=null;this.hilitedYear=null;this.activeYear=null;this.dateClicked=false;if(typeof Calendar._SDN=="undefined"){if(typeof Calendar._SDN_len=="undefined")Calendar._SDN_len=3;var ar=new Array();for(var i=8;i>0;){ar[--i]=Calendar._DN[i].substr(0,Calendar._SDN_len);}Calendar._SDN=ar;if(typeof Calendar._SMN_len=="undefined")Calendar._SMN_len=3;ar=new Array();for(var i=12;i>0;){ar[--i]=Calendar._MN[i].substr(0,Calendar._SMN_len);}Calendar._SMN=ar;}};Calendar._C=null;Calendar.is_ie=(/msie/i.test(navigator.userAgent)&&!/opera/i.test(navigator.userAgent));Calendar.is_ie5=(Calendar.is_ie&&/msie 5\.0/i.test(navigator.userAgent));Calendar.is_opera=/opera/i.test(navigator.userAgent);Calendar.is_khtml=/Konqueror|Safari|KHTML/i.test(navigator.userAgent);Calendar.getAbsolutePos=function(el){var SL=0,ST=0;var is_div=/^div$/i.test(el.tagName);if(is_div&&el.scrollLeft)SL=el.scrollLeft;if(is_div&&el.scrollTop)ST=el.scrollTop;var r={x:el.offsetLeft-SL,y:el.offsetTop-ST};if(el.offsetParent){var tmp=this.getAbsolutePos(el.offsetParent);r.x+=tmp.x;r.y+=tmp.y;}return r;};Calendar.isRelated=function(el,evt){var related=evt.relatedTarget;if(!related){var type=evt.type;if(type=="mouseover"){related=evt.fromElement;}else if(type=="mouseout"){related=evt.toElement;}}while(related){if(related==el){return true;}related=related.parentNode;}return false;};Calendar.removeClass=function(el,className){if(!(el&&el.className)){return;}var cls=el.className.split(" ");var ar=new Array();for(var i=cls.length;i>0;){if(cls[--i]!=className){ar[ar.length]=cls[i];}}el.className=ar.join(" ");};Calendar.addClass=function(el,className){Calendar.removeClass(el,className);el.className+=" "+className;};Calendar.getElement=function(ev){var f=Calendar.is_ie?window.event.srcElement:ev.currentTarget;while(f.nodeType!=1||/^div$/i.test(f.tagName))f=f.parentNode;return f;};Calendar.getTargetElement=function(ev){var f=Calendar.is_ie?window.event.srcElement:ev.target;while(f.nodeType!=1)f=f.parentNode;return f;};Calendar.stopEvent=function(ev){ev||(ev=window.event);if(Calendar.is_ie){ev.cancelBubble=true;ev.returnValue=false;}else{ev.preventDefault();ev.stopPropagation();}return false;};Calendar.addEvent=function(el,evname,func){if(el.attachEvent){el.attachEvent("on"+evname,func);}else if(el.addEventListener){el.addEventListener(evname,func,true);}else{el["on"+evname]=func;}};Calendar.removeEvent=function(el,evname,func){if(el.detachEvent){el.detachEvent("on"+evname,func);}else if(el.removeEventListener){el.removeEventListener(evname,func,true);}else{el["on"+evname]=null;}};Calendar.createElement=function(type,parent){var el=null;if(document.createElementNS){el=document.createElementNS("http://www.w3.org/1999/xhtml",type);}else{el=document.createElement(type);}if(typeof parent!="undefined"){parent.appendChild(el);}return el;};Calendar._add_evs=function(el){with(Calendar){addEvent(el,"mouseover",dayMouseOver);addEvent(el,"mousedown",dayMouseDown);addEvent(el,"mouseout",dayMouseOut);if(is_ie){addEvent(el,"dblclick",dayMouseDblClick);el.setAttribute("unselectable",true);}}};Calendar.findMonth=function(el){if(typeof el.month!="undefined"){return el;}else if(typeof el.parentNode.month!="undefined"){return el.parentNode;}return null;};Calendar.findYear=function(el){if(typeof el.year!="undefined"){return el;}else if(typeof el.parentNode.year!="undefined"){return el.parentNode;}return null;};Calendar.showMonthsCombo=function(){var cal=Calendar._C;if(!cal){return false;}var cal=cal;var cd=cal.activeDiv;var mc=cal.monthsCombo;if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}if(cal.activeMonth){Calendar.removeClass(cal.activeMonth,"active");}var mon=cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];Calendar.addClass(mon,"active");cal.activeMonth=mon;var s=mc.style;s.display="block";if(cd.navtype<0)s.left=cd.offsetLeft+"px";else{var mcw=mc.offsetWidth;if(typeof mcw=="undefined")mcw=50;s.left=(cd.offsetLeft+cd.offsetWidth-mcw)+"px";}s.top=(cd.offsetTop+cd.offsetHeight)+"px";};Calendar.showYearsCombo=function(fwd){var cal=Calendar._C;if(!cal){return false;}var cal=cal;var cd=cal.activeDiv;var yc=cal.yearsCombo;if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}if(cal.activeYear){Calendar.removeClass(cal.activeYear,"active");}cal.activeYear=null;var Y=cal.date.getFullYear()+(fwd?1:-1);var yr=yc.firstChild;var show=false;for(var i=12;i>0;--i){if(Y>=cal.minYear&&Y<=cal.maxYear){yr.innerHTML=Y;yr.year=Y;yr.style.display="block";show=true;}else{yr.style.display="none";}yr=yr.nextSibling;Y+=fwd?cal.yearStep:-cal.yearStep;}if(show){var s=yc.style;s.display="block";if(cd.navtype<0)s.left=cd.offsetLeft+"px";else{var ycw=yc.offsetWidth;if(typeof ycw=="undefined")ycw=50;s.left=(cd.offsetLeft+cd.offsetWidth-ycw)+"px";}s.top=(cd.offsetTop+cd.offsetHeight)+"px";}};Calendar.tableMouseUp=function(ev){var cal=Calendar._C;if(!cal){return false;}if(cal.timeout){clearTimeout(cal.timeout);}var el=cal.activeDiv;if(!el){return false;}var target=Calendar.getTargetElement(ev);ev||(ev=window.event);Calendar.removeClass(el,"active");if(target==el||target.parentNode==el){Calendar.cellClick(el,ev);}var mon=Calendar.findMonth(target);var date=null;if(mon){date=new Date(cal.date);if(mon.month!=date.getMonth()){date.setMonth(mon.month);cal.setDate(date);cal.dateClicked=false;cal.callHandler();}}else{var year=Calendar.findYear(target);if(year){date=new Date(cal.date);if(year.year!=date.getFullYear()){date.setFullYear(year.year);cal.setDate(date);cal.dateClicked=false;cal.callHandler();}}}with(Calendar){removeEvent(document,"mouseup",tableMouseUp);removeEvent(document,"mouseover",tableMouseOver);removeEvent(document,"mousemove",tableMouseOver);cal._hideCombos();_C=null;return stopEvent(ev);}};Calendar.tableMouseOver=function(ev){var cal=Calendar._C;if(!cal){return;}var el=cal.activeDiv;var target=Calendar.getTargetElement(ev);if(target==el||target.parentNode==el){Calendar.addClass(el,"hilite active");Calendar.addClass(el.parentNode,"rowhilite");}else{if(typeof el.navtype=="undefined"||(el.navtype!=50&&(el.navtype==0||Math.abs(el.navtype)>2)))Calendar.removeClass(el,"active");Calendar.removeClass(el,"hilite");Calendar.removeClass(el.parentNode,"rowhilite");}ev||(ev=window.event);if(el.navtype==50&&target!=el){var pos=Calendar.getAbsolutePos(el);var w=el.offsetWidth;var x=ev.clientX;var dx;var decrease=true;if(x>pos.x+w){dx=x-pos.x-w;decrease=false;}else dx=pos.x-x;if(dx<0)dx=0;var range=el._range;var current=el._current;var count=Math.floor(dx/10)%range.length;for(var i=range.length;--i>=0;)if(range[i]==current)break;while(count-->0)if(decrease){if(--i<0)i=range.length-1;}else if(++i>=range.length)i=0;var newval=range[i];el.innerHTML=newval;cal.onUpdateTime();}var mon=Calendar.findMonth(target);if(mon){if(mon.month!=cal.date.getMonth()){if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}Calendar.addClass(mon,"hilite");cal.hilitedMonth=mon;}else if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}}else{if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}var year=Calendar.findYear(target);if(year){if(year.year!=cal.date.getFullYear()){if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}Calendar.addClass(year,"hilite");cal.hilitedYear=year;}else if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}}else if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}}return Calendar.stopEvent(ev);};Calendar.tableMouseDown=function(ev){if(Calendar.getTargetElement(ev)==Calendar.getElement(ev)){return Calendar.stopEvent(ev);}};Calendar.calDragIt=function(ev){var cal=Calendar._C;if(!(cal&&cal.dragging)){return false;}var posX;var posY;if(Calendar.is_ie){posY=window.event.clientY+document.body.scrollTop;posX=window.event.clientX+document.body.scrollLeft;}else{posX=ev.pageX;posY=ev.pageY;}cal.hideShowCovered();var st=cal.element.style;st.left=(posX-cal.xOffs)+"px";st.top=(posY-cal.yOffs)+"px";return Calendar.stopEvent(ev);};Calendar.calDragEnd=function(ev){var cal=Calendar._C;if(!cal){return false;}cal.dragging=false;with(Calendar){removeEvent(document,"mousemove",calDragIt);removeEvent(document,"mouseup",calDragEnd);tableMouseUp(ev);}cal.hideShowCovered();};Calendar.dayMouseDown=function(ev){var el=Calendar.getElement(ev);if(el.disabled){return false;}var cal=el.calendar;cal.activeDiv=el;Calendar._C=cal;if(el.navtype!=300)with(Calendar){if(el.navtype==50){el._current=el.innerHTML;addEvent(document,"mousemove",tableMouseOver);}else addEvent(document,Calendar.is_ie5?"mousemove":"mouseover",tableMouseOver);addClass(el,"hilite active");addEvent(document,"mouseup",tableMouseUp);}else if(cal.isPopup){cal._dragStart(ev);}if(el.navtype==-1||el.navtype==1){if(cal.timeout)clearTimeout(cal.timeout);cal.timeout=setTimeout("Calendar.showMonthsCombo()",250);}else if(el.navtype==-2||el.navtype==2){if(cal.timeout)clearTimeout(cal.timeout);cal.timeout=setTimeout((el.navtype>0)?"Calendar.showYearsCombo(true)":"Calendar.showYearsCombo(false)",250);}else{cal.timeout=null;}return Calendar.stopEvent(ev);};Calendar.dayMouseDblClick=function(ev){Calendar.cellClick(Calendar.getElement(ev),ev||window.event);if(Calendar.is_ie){document.selection.empty();}};Calendar.dayMouseOver=function(ev){var el=Calendar.getElement(ev);if(Calendar.isRelated(el,ev)||Calendar._C||el.disabled){return false;}if(el.ttip){if(el.ttip.substr(0,1)=="_"){el.ttip=el.caldate.print(el.calendar.ttDateFormat)+el.ttip.substr(1);}el.calendar.tooltips.innerHTML=el.ttip;}if(el.navtype!=300){Calendar.addClass(el,"hilite");if(el.caldate){Calendar.addClass(el.parentNode,"rowhilite");var cal=el.calendar;if(cal&&cal.getDateToolTip){var d=el.caldate;window.status=d;el.title=cal.getDateToolTip(d,d.getFullYear(),d.getMonth(),d.getDate());}}}return Calendar.stopEvent(ev);};Calendar.dayMouseOut=function(ev){with(Calendar){var el=getElement(ev);if(isRelated(el,ev)||_C||el.disabled)return false;removeClass(el,"hilite");if(el.caldate)removeClass(el.parentNode,"rowhilite");if(el.calendar)el.calendar.tooltips.innerHTML=_TT["SEL_DATE"];}};Calendar.cellClick=function(el,ev){var cal=el.calendar;var closing=false;var newdate=false;var date=null;if(typeof el.navtype=="undefined"){if(cal.currentDateEl){Calendar.removeClass(cal.currentDateEl,"selected");Calendar.addClass(el,"selected");closing=(cal.currentDateEl==el);if(!closing){cal.currentDateEl=el;}}cal.date.setDateOnly(el.caldate);date=cal.date;var other_month=!(cal.dateClicked=!el.otherMonth);if(!other_month&&!cal.currentDateEl&&cal.multiple)cal._toggleMultipleDate(new Date(date));else newdate=!el.disabled;if(other_month)cal._init(cal.firstDayOfWeek,date);}else{if(el.navtype==200){Calendar.removeClass(el,"hilite");cal.callCloseHandler();return;}date=new Date(cal.date);if(el.navtype==0)date.setDateOnly(new Date());cal.dateClicked=false;var year=date.getFullYear();var mon=date.getMonth();function setMonth(m){var day=date.getDate();var max=date.getMonthDays(m);if(day>max){date.setDate(max);}date.setMonth(m);};switch(el.navtype){case 400:Calendar.removeClass(el,"hilite");var text=Calendar._TT["ABOUT"];if(typeof text!="undefined"){text+=cal.showsTime?Calendar._TT["ABOUT_TIME"]:"";}else{text="Help and about box text is not translated into this language.\n"+"If you know this language and you feel generous please update\n"+"the corresponding file in \"lang\" subdir to match calendar-en.js\n"+"and send it back to <mihai_bazon@yahoo.com> to get it into the distribution  ;-)\n\n"+"Thank you!\n"+"http://dynarch.com/mishoo/calendar.epl\n";}alert(text);return;case-2:if(year>cal.minYear){date.setFullYear(year-1);}break;case-1:if(mon>0){setMonth(mon-1);}else if(year-->cal.minYear){date.setFullYear(year);setMonth(11);}break;case 1:if(mon<11){setMonth(mon+1);}else if(year<cal.maxYear){date.setFullYear(year+1);setMonth(0);}break;case 2:if(year<cal.maxYear){date.setFullYear(year+1);}break;case 100:cal.setFirstDayOfWeek(el.fdow);return;case 50:var range=el._range;var current=el.innerHTML;for(var i=range.length;--i>=0;)if(range[i]==current)break;if(ev&&ev.shiftKey){if(--i<0)i=range.length-1;}else if(++i>=range.length)i=0;var newval=range[i];el.innerHTML=newval;cal.onUpdateTime();return;case 0:if((typeof cal.getDateStatus=="function")&&cal.getDateStatus(date,date.getFullYear(),date.getMonth(),date.getDate())){return false;}break;}if(!date.equalsTo(cal.date)){cal.setDate(date);newdate=true;}else if(el.navtype==0)newdate=closing=true;}if(newdate){ev&&cal.callHandler();}if(closing){Calendar.removeClass(el,"hilite");ev&&cal.callCloseHandler();}};Calendar.prototype.create=function(_par){var parent=null;if(!_par){parent=document.getElementsByTagName("body")[0];this.isPopup=true;}else{parent=_par;this.isPopup=false;}this.date=this.dateStr?new Date(this.dateStr):new Date();var table=Calendar.createElement("table");this.table=table;table.cellSpacing=0;table.cellPadding=0;table.calendar=this;Calendar.addEvent(table,"mousedown",Calendar.tableMouseDown);var div=Calendar.createElement("div");this.element=div;div.className="calendar";if(this.isPopup){div.style.position="absolute";div.style.display="none";}div.appendChild(table);var thead=Calendar.createElement("thead",table);var cell=null;var row=null;var cal=this;var hh=function(text,cs,navtype){cell=Calendar.createElement("td",row);cell.colSpan=cs;cell.className="button";if(navtype!=0&&Math.abs(navtype)<=2)cell.className+=" nav";Calendar._add_evs(cell);cell.calendar=cal;cell.navtype=navtype;cell.innerHTML="<div unselectable='on'>"+text+"</div>";return cell;};row=Calendar.createElement("tr",thead);var title_length=6;(this.isPopup)&&--title_length;(this.weekNumbers)&&++title_length;hh("?",1,400).ttip=Calendar._TT["INFO"];this.title=hh("",title_length,300);this.title.className="title";if(this.isPopup){this.title.ttip=Calendar._TT["DRAG_TO_MOVE"];this.title.style.cursor="move";hh("&#x00d7;",1,200).ttip=Calendar._TT["CLOSE"];}row=Calendar.createElement("tr",thead);row.className="headrow";this._nav_py=hh("&#x00ab;",1,-2);this._nav_py.ttip=Calendar._TT["PREV_YEAR"];this._nav_pm=hh("&#x2039;",1,-1);this._nav_pm.ttip=Calendar._TT["PREV_MONTH"];this._nav_now=hh(Calendar._TT["TODAY"],this.weekNumbers?4:3,0);this._nav_now.ttip=Calendar._TT["GO_TODAY"];this._nav_nm=hh("&#x203a;",1,1);this._nav_nm.ttip=Calendar._TT["NEXT_MONTH"];this._nav_ny=hh("&#x00bb;",1,2);this._nav_ny.ttip=Calendar._TT["NEXT_YEAR"];row=Calendar.createElement("tr",thead);row.className="daynames";if(this.weekNumbers){cell=Calendar.createElement("td",row);cell.className="name wn";cell.innerHTML=Calendar._TT["WK"];}for(var i=7;i>0;--i){cell=Calendar.createElement("td",row);if(!i){cell.navtype=100;cell.calendar=this;Calendar._add_evs(cell);}}this.firstdayname=(this.weekNumbers)?row.firstChild.nextSibling:row.firstChild;this._displayWeekdays();var tbody=Calendar.createElement("tbody",table);this.tbody=tbody;for(i=6;i>0;--i){row=Calendar.createElement("tr",tbody);if(this.weekNumbers){cell=Calendar.createElement("td",row);}for(var j=7;j>0;--j){cell=Calendar.createElement("td",row);cell.calendar=this;Calendar._add_evs(cell);}}if(this.showsTime){row=Calendar.createElement("tr",tbody);row.className="time";cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=2;cell.innerHTML=Calendar._TT["TIME"]||"&nbsp;";cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=this.weekNumbers?4:3;(function(){function makeTimePart(className,init,range_start,range_end){var part=Calendar.createElement("span",cell);part.className=className;part.innerHTML=init;part.calendar=cal;part.ttip=Calendar._TT["TIME_PART"];part.navtype=50;part._range=[];if(typeof range_start!="number")part._range=range_start;else{for(var i=range_start;i<=range_end;++i){var txt;if(i<10&&range_end>=10)txt='0'+i;else txt=''+i;part._range[part._range.length]=txt;}}Calendar._add_evs(part);return part;};var hrs=cal.date.getHours();var mins=cal.date.getMinutes();var t12=!cal.time24;var pm=(hrs>12);if(t12&&pm)hrs-=12;var H=makeTimePart("hour",hrs,t12?1:0,t12?12:23);var span=Calendar.createElement("span",cell);span.innerHTML=":";span.className="colon";var M=makeTimePart("minute",mins,0,59);var AP=null;cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=2;if(t12)AP=makeTimePart("ampm",pm?"pm":"am",["am","pm"]);else cell.innerHTML="&nbsp;";cal.onSetTime=function(){var pm,hrs=this.date.getHours(),mins=this.date.getMinutes();if(t12){pm=(hrs>=12);if(pm)hrs-=12;if(hrs==0)hrs=12;AP.innerHTML=pm?"pm":"am";}H.innerHTML=(hrs<10)?("0"+hrs):hrs;M.innerHTML=(mins<10)?("0"+mins):mins;};cal.onUpdateTime=function(){var date=this.date;var h=parseInt(H.innerHTML,10);if(t12){if(/pm/i.test(AP.innerHTML)&&h<12)h+=12;else if(/am/i.test(AP.innerHTML)&&h==12)h=0;}var d=date.getDate();var m=date.getMonth();var y=date.getFullYear();date.setHours(h);date.setMinutes(parseInt(M.innerHTML,10));date.setFullYear(y);date.setMonth(m);date.setDate(d);this.dateClicked=false;this.callHandler();};})();}else{this.onSetTime=this.onUpdateTime=function(){};}var tfoot=Calendar.createElement("tfoot",table);row=Calendar.createElement("tr",tfoot);row.className="footrow";cell=hh(Calendar._TT["SEL_DATE"],this.weekNumbers?8:7,300);cell.className="ttip";if(this.isPopup){cell.ttip=Calendar._TT["DRAG_TO_MOVE"];cell.style.cursor="move";}this.tooltips=cell;div=Calendar.createElement("div",this.element);this.monthsCombo=div;div.className="combo";for(i=0;i<Calendar._MN.length;++i){var mn=Calendar.createElement("div");mn.className=Calendar.is_ie?"label-IEfix":"label";mn.month=i;mn.innerHTML=Calendar._SMN[i];div.appendChild(mn);}div=Calendar.createElement("div",this.element);this.yearsCombo=div;div.className="combo";for(i=12;i>0;--i){var yr=Calendar.createElement("div");yr.className=Calendar.is_ie?"label-IEfix":"label";div.appendChild(yr);}this._init(this.firstDayOfWeek,this.date);parent.appendChild(this.element);};Calendar._keyEvent=function(ev){var cal=window._dynarch_popupCalendar;if(!cal||cal.multiple)return false;(Calendar.is_ie)&&(ev=window.event);var act=(Calendar.is_ie||ev.type=="keypress"),K=ev.keyCode;if(ev.ctrlKey){switch(K){case 37:act&&Calendar.cellClick(cal._nav_pm);break;case 38:act&&Calendar.cellClick(cal._nav_py);break;case 39:act&&Calendar.cellClick(cal._nav_nm);break;case 40:act&&Calendar.cellClick(cal._nav_ny);break;default:return false;}}else switch(K){case 32:Calendar.cellClick(cal._nav_now);break;case 27:act&&cal.callCloseHandler();break;case 37:case 38:case 39:case 40:if(act){var prev,x,y,ne,el,step;prev=K==37||K==38;step=(K==37||K==39)?1:7;function setVars(){el=cal.currentDateEl;var p=el.pos;x=p&15;y=p>>4;ne=cal.ar_days[y][x];};setVars();function prevMonth(){var date=new Date(cal.date);date.setDate(date.getDate()-step);cal.setDate(date);};function nextMonth(){var date=new Date(cal.date);date.setDate(date.getDate()+step);cal.setDate(date);};while(1){switch(K){case 37:if(--x>=0)ne=cal.ar_days[y][x];else{x=6;K=38;continue;}break;case 38:if(--y>=0)ne=cal.ar_days[y][x];else{prevMonth();setVars();}break;case 39:if(++x<7)ne=cal.ar_days[y][x];else{x=0;K=40;continue;}break;case 40:if(++y<cal.ar_days.length)ne=cal.ar_days[y][x];else{nextMonth();setVars();}break;}break;}if(ne){if(!ne.disabled)Calendar.cellClick(ne);else if(prev)prevMonth();else nextMonth();}}break;case 13:if(act)Calendar.cellClick(cal.currentDateEl,ev);break;default:return false;}return Calendar.stopEvent(ev);};Calendar.prototype._init=function(firstDayOfWeek,date){var today=new Date(),TY=today.getFullYear(),TM=today.getMonth(),TD=today.getDate();this.table.style.visibility="hidden";var year=date.getFullYear();if(year<this.minYear){year=this.minYear;date.setFullYear(year);}else if(year>this.maxYear){year=this.maxYear;date.setFullYear(year);}this.firstDayOfWeek=firstDayOfWeek;this.date=new Date(date);var month=date.getMonth();var mday=date.getDate();var no_days=date.getMonthDays();date.setDate(1);var day1=(date.getDay()-this.firstDayOfWeek)%7;if(day1<0)day1+=7;date.setDate(day1 == 0 ? 0 : (day1 < 0 ? Math.abs(day1) : 0 - day1));date.setDate(date.getDate()+1);var row=this.tbody.firstChild;var MN=Calendar._SMN[month];var ar_days=this.ar_days=new Array();var weekend=Calendar._TT["WEEKEND"];var dates=this.multiple?(this.datesCells={}):null;for(var i=0;i<6;++i,row=row.nextSibling){var cell=row.firstChild;if(this.weekNumbers){cell.className="day wn";cell.innerHTML=date.getWeekNumber();cell=cell.nextSibling;}row.className="daysrow";var hasdays=false,iday,dpos=ar_days[i]=[];for(var j=0;j<7;++j,cell=cell.nextSibling,date.setDate(iday+1)){iday=date.getDate();var wday=date.getDay();cell.className="day";cell.pos=i<<4|j;dpos[j]=cell;var current_month=(date.getMonth()==month);if(!current_month){if(this.showsOtherMonths){cell.className+=" othermonth";cell.otherMonth=true;}else{cell.className="emptycell";cell.innerHTML="&nbsp;";cell.disabled=true;continue;}}else{cell.otherMonth=false;hasdays=true;}cell.disabled=false;cell.innerHTML=this.getDateText?this.getDateText(date,iday):iday;if(dates)dates[date.print("%Y%m%d")]=cell;if(this.getDateStatus){var status=this.getDateStatus(date,year,month,iday);if(status===true){cell.className+=" disabled";cell.disabled=true;}else{if(/disabled/i.test(status))cell.disabled=true;cell.className+=" "+status;}}if(!cell.disabled){cell.caldate=new Date(date);cell.ttip="_";if(!this.multiple&&current_month&&iday==mday&&this.hiliteToday){cell.className+=" selected";this.currentDateEl=cell;}if(date.getFullYear()==TY&&date.getMonth()==TM&&iday==TD){cell.className+=" today";cell.ttip+=Calendar._TT["PART_TODAY"];}if(weekend.indexOf(wday.toString())!=-1)cell.className+=cell.otherMonth?" oweekend":" weekend";}}if(!(hasdays||this.showsOtherMonths))row.className="emptyrow";}this.title.innerHTML=Calendar._MN[month]+", "+year;this.onSetTime();this.table.style.visibility="visible";this._initMultipleDates();};Calendar.prototype._initMultipleDates=function(){if(this.multiple){for(var i in this.multiple){var cell=this.datesCells[i];var d=this.multiple[i];if(!d)continue;if(cell)cell.className+=" selected";}}};Calendar.prototype._toggleMultipleDate=function(date){if(this.multiple){var ds=date.print("%Y%m%d");var cell=this.datesCells[ds];if(cell){var d=this.multiple[ds];if(!d){Calendar.addClass(cell,"selected");this.multiple[ds]=date;}else{Calendar.removeClass(cell,"selected");delete this.multiple[ds];}}}};Calendar.prototype.setDateToolTipHandler=function(unaryFunction){this.getDateToolTip=unaryFunction;};Calendar.prototype.setDate=function(date){if(!date.equalsTo(this.date)){this._init(this.firstDayOfWeek,date);}};Calendar.prototype.refresh=function(){this._init(this.firstDayOfWeek,this.date);};Calendar.prototype.setFirstDayOfWeek=function(firstDayOfWeek){this._init(firstDayOfWeek,this.date);this._displayWeekdays();};Calendar.prototype.setDateStatusHandler=Calendar.prototype.setDisabledHandler=function(unaryFunction){this.getDateStatus=unaryFunction;};Calendar.prototype.setRange=function(a,z){this.minYear=a;this.maxYear=z;};Calendar.prototype.callHandler=function(){if(this.onSelected){this.onSelected(this,this.date.print(this.dateFormat));}};Calendar.prototype.callCloseHandler=function(){if(this.onClose){this.onClose(this);}this.hideShowCovered();};Calendar.prototype.destroy=function(){var el=this.element.parentNode;el.removeChild(this.element);Calendar._C=null;window._dynarch_popupCalendar=null;};Calendar.prototype.reparent=function(new_parent){var el=this.element;el.parentNode.removeChild(el);new_parent.appendChild(el);};Calendar._checkCalendar=function(ev){var calendar=window._dynarch_popupCalendar;if(!calendar){return false;}var el=Calendar.is_ie?Calendar.getElement(ev):Calendar.getTargetElement(ev);for(;el!=null&&el!=calendar.element;el=el.parentNode);if(el==null){window._dynarch_popupCalendar.callCloseHandler();return Calendar.stopEvent(ev);}};Calendar.prototype.show=function(){var rows=this.table.getElementsByTagName("tr");for(var i=rows.length;i>0;){var row=rows[--i];Calendar.removeClass(row,"rowhilite");var cells=row.getElementsByTagName("td");for(var j=cells.length;j>0;){var cell=cells[--j];Calendar.removeClass(cell,"hilite");Calendar.removeClass(cell,"active");}}this.element.style.display="block";this.hidden=false;if(this.isPopup){window._dynarch_popupCalendar=this;Calendar.addEvent(document,"keydown",Calendar._keyEvent);Calendar.addEvent(document,"keypress",Calendar._keyEvent);Calendar.addEvent(document,"mousedown",Calendar._checkCalendar);}this.hideShowCovered();};Calendar.prototype.hide=function(){if(this.isPopup){Calendar.removeEvent(document,"keydown",Calendar._keyEvent);Calendar.removeEvent(document,"keypress",Calendar._keyEvent);Calendar.removeEvent(document,"mousedown",Calendar._checkCalendar);}this.element.style.display="none";this.hidden=true;this.hideShowCovered();};Calendar.prototype.showAt=function(x,y){var s=this.element.style;s.left=x+"px";s.top=y+"px";this.show();};Calendar.prototype.showAtElement=function(el,opts){var self=this;var p=Calendar.getAbsolutePos(el);if(!opts||typeof opts!="string"){this.showAt(p.x,p.y+el.offsetHeight);return true;}function fixPosition(box){if(box.x<0)box.x=0;if(box.y<0)box.y=0;var cp=document.createElement("div");var s=cp.style;s.position="absolute";s.right=s.bottom=s.width=s.height="0px";document.body.appendChild(cp);var br=Calendar.getAbsolutePos(cp);document.body.removeChild(cp);if(Calendar.is_ie){br.y+=document.body.scrollTop;br.x+=document.body.scrollLeft;}else{br.y+=window.scrollY;br.x+=window.scrollX;}var tmp=box.x+box.width-br.x;if(tmp>0)box.x-=tmp;tmp=box.y+box.height-br.y;if(tmp>0)box.y-=tmp;};this.element.style.display="block";Calendar.continuation_for_the_fucking_khtml_browser=function(){var w=self.element.offsetWidth;var h=self.element.offsetHeight;self.element.style.display="none";var valign=opts.substr(0,1);var halign="l";if(opts.length>1){halign=opts.substr(1,1);}switch(valign){case "T":p.y-=h;break;case "B":p.y+=el.offsetHeight;break;case "C":p.y+=(el.offsetHeight-h)/2;break;case "t":p.y+=el.offsetHeight-h;break;case "b":break;}switch(halign){case "L":p.x-=w;break;case "R":p.x+=el.offsetWidth;break;case "C":p.x+=(el.offsetWidth-w)/2;break;case "l":p.x+=el.offsetWidth-w;break;case "r":break;}p.width=w;p.height=h+40;self.monthsCombo.style.display="none";fixPosition(p);self.showAt(p.x,p.y);};if(Calendar.is_khtml)setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()",10);else Calendar.continuation_for_the_fucking_khtml_browser();};Calendar.prototype.setDateFormat=function(str){this.dateFormat=str;};Calendar.prototype.setTtDateFormat=function(str){this.ttDateFormat=str;};Calendar.prototype.parseDate=function(str,fmt){if(!fmt)fmt=this.dateFormat;this.setDate(Date.parseDate(str,fmt));};Calendar.prototype.hideShowCovered=function(){if(!Calendar.is_ie&&!Calendar.is_opera)return;function getVisib(obj){var value=obj.style.visibility;if(!value){if(document.defaultView&&typeof(document.defaultView.getComputedStyle)=="function"){if(!Calendar.is_khtml)value=document.defaultView. getComputedStyle(obj,"").getPropertyValue("visibility");else value='';}else if(obj.currentStyle){value=obj.currentStyle.visibility;}else value='';}return value;};var tags=new Array("applet","iframe","select");var el=this.element;var p=Calendar.getAbsolutePos(el);var EX1=p.x;var EX2=el.offsetWidth+EX1;var EY1=p.y;var EY2=el.offsetHeight+EY1;for(var k=tags.length;k>0;){var ar=document.getElementsByTagName(tags[--k]);var cc=null;for(var i=ar.length;i>0;){cc=ar[--i];p=Calendar.getAbsolutePos(cc);var CX1=p.x;var CX2=cc.offsetWidth+CX1;var CY1=p.y;var CY2=cc.offsetHeight+CY1;if(this.hidden||(CX1>EX2)||(CX2<EX1)||(CY1>EY2)||(CY2<EY1)){if(!cc.__msh_save_visibility){cc.__msh_save_visibility=getVisib(cc);}cc.style.visibility=cc.__msh_save_visibility;}else{if(!cc.__msh_save_visibility){cc.__msh_save_visibility=getVisib(cc);}cc.style.visibility="hidden";}}}};Calendar.prototype._displayWeekdays=function(){var fdow=this.firstDayOfWeek;var cell=this.firstdayname;var weekend=Calendar._TT["WEEKEND"];for(var i=0;i<7;++i){cell.className="day name";var realday=(i+fdow)%7;if(i){cell.ttip=Calendar._TT["DAY_FIRST"].replace("%s",Calendar._DN[realday]);cell.navtype=100;cell.calendar=this;cell.fdow=realday;Calendar._add_evs(cell);}if(weekend.indexOf(realday.toString())!=-1){Calendar.addClass(cell,"weekend");}cell.innerHTML=Calendar._SDN[(i+fdow)%7];cell=cell.nextSibling;}};Calendar.prototype._hideCombos=function(){this.monthsCombo.style.display="none";this.yearsCombo.style.display="none";};Calendar.prototype._dragStart=function(ev){if(this.dragging){return;}this.dragging=true;var posX;var posY;if(Calendar.is_ie){posY=window.event.clientY+document.body.scrollTop;posX=window.event.clientX+document.body.scrollLeft;}else{posY=ev.clientY+window.scrollY;posX=ev.clientX+window.scrollX;}var st=this.element.style;this.xOffs=posX-parseInt(st.left);this.yOffs=posY-parseInt(st.top);with(Calendar){addEvent(document,"mousemove",calDragIt);addEvent(document,"mouseup",calDragEnd);}};Date._MD=new Array(31,28,31,30,31,30,31,31,30,31,30,31);Date.SECOND=1000;Date.MINUTE=60*Date.SECOND;Date.HOUR=60*Date.MINUTE;Date.DAY=24*Date.HOUR;Date.WEEK=7*Date.DAY;Date.parseDate=function(str,fmt){var today=new Date();var y=0;var m=-1;var d=0;var a=str.split(/\W+/);var b=fmt.match(/%./g);var i=0,j=0;var hr=0;var min=0;for(i=0;i<a.length;++i){if(!a[i])continue;switch(b[i]){case "%d":case "%e":d=parseInt(a[i],10);break;case "%m":m=parseInt(a[i],10)-1;break;case "%Y":case "%y":y=parseInt(a[i],10);(y<100)&&(y+=(y>29)?1900:2000);break;case "%b":case "%B":for(j=0;j<12;++j){if(Calendar._MN[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){m=j;break;}}break;case "%H":case "%I":case "%k":case "%l":hr=parseInt(a[i],10);break;case "%P":case "%p":if(/pm/i.test(a[i])&&hr<12)hr+=12;else if(/am/i.test(a[i])&&hr>=12)hr-=12;break;case "%M":min=parseInt(a[i],10);break;}}if(isNaN(y))y=today.getFullYear();if(isNaN(m))m=today.getMonth();if(isNaN(d))d=today.getDate();if(isNaN(hr))hr=today.getHours();if(isNaN(min))min=today.getMinutes();if(y!=0&&m!=-1&&d!=0)return new Date(y,m,d,hr,min,0);y=0;m=-1;d=0;for(i=0;i<a.length;++i){if(a[i].search(/[a-zA-Z]+/)!=-1){var t=-1;for(j=0;j<12;++j){if(Calendar._MN[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){t=j;break;}}if(t!=-1){if(m!=-1){d=m+1;}m=t;}}else if(parseInt(a[i],10)<=12&&m==-1){m=a[i]-1;}else if(parseInt(a[i],10)>31&&y==0){y=parseInt(a[i],10);(y<100)&&(y+=(y>29)?1900:2000);}else if(d==0){d=a[i];}}if(y==0)y=today.getFullYear();if(m!=-1&&d!=0)return new Date(y,m,d,hr,min,0);return today;};Date.prototype.getMonthDays=function(month){var year=this.getFullYear();if(typeof month=="undefined"){month=this.getMonth();}if(((0==(year%4))&&((0!=(year%100))||(0==(year%400))))&&month==1){return 29;}else{return Date._MD[month];}};Date.prototype.getDayOfYear=function(){var now=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var then=new Date(this.getFullYear(),0,0,0,0,0);var time=now-then;return Math.floor(time/Date.DAY);};Date.prototype.getWeekNumber=function(){var d=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var DoW=d.getDay();d.setDate(d.getDate()-(DoW+6)%7+3);var ms=d.valueOf();d.setMonth(0);d.setDate(4);return Math.round((ms-d.valueOf())/(7*864e5))+1;};Date.prototype.equalsTo=function(date){return((this.getFullYear()==date.getFullYear())&&(this.getMonth()==date.getMonth())&&(this.getDate()==date.getDate())&&(this.getHours()==date.getHours())&&(this.getMinutes()==date.getMinutes()));};Date.prototype.setDateOnly=function(date){var tmp=new Date(date);this.setDate(1);this.setFullYear(tmp.getFullYear());this.setMonth(tmp.getMonth());this.setDate(tmp.getDate());};Date.prototype.print=function(str){var m=this.getMonth();var d=this.getDate();var y=this.getFullYear();var wn=this.getWeekNumber();var w=this.getDay();var s={};var hr=this.getHours();var pm=(hr>=12);var ir=(pm)?(hr-12):hr;var dy=this.getDayOfYear();if(ir==0)ir=12;var min=this.getMinutes();var sec=this.getSeconds();s["%a"]=Calendar._SDN[w];s["%A"]=Calendar._DN[w];s["%b"]=Calendar._SMN[m];s["%B"]=Calendar._MN[m];s["%C"]=1+Math.floor(y/100);s["%d"]=(d<10)?("0"+d):d;s["%e"]=d;s["%H"]=(hr<10)?("0"+hr):hr;s["%I"]=(ir<10)?("0"+ir):ir;s["%j"]=(dy<100)?((dy<10)?("00"+dy):("0"+dy)):dy;s["%k"]=hr;s["%l"]=ir;s["%m"]=(m<9)?("0"+(1+m)):(1+m);s["%M"]=(min<10)?("0"+min):min;s["%n"]="\n";s["%p"]=pm?"PM":"AM";s["%P"]=pm?"pm":"am";s["%s"]=Math.floor(this.getTime()/1000);s["%S"]=(sec<10)?("0"+sec):sec;s["%t"]="\t";s["%U"]=s["%W"]=s["%V"]=(wn<10)?("0"+wn):wn;s["%u"]=w+1;s["%w"]=w;s["%y"]=(''+y).substr(2,2);s["%Y"]=y;s["%%"]="%";var re=/%./g;if(!Calendar.is_ie5&&!Calendar.is_khtml)return str.replace(re,function(par){return s[par]||par;});var a=str.match(re);for(var i=0;i<a.length;i++){var tmp=s[a[i]];if(tmp){re=new RegExp(a[i],'g');str=str.replace(re,tmp);}}return str;};Date.prototype.__msh_oldSetFullYear=Date.prototype.setFullYear;Date.prototype.setFullYear=function(y){var d=new Date(this);d.__msh_oldSetFullYear(y);if(d.getMonth()!=this.getMonth())this.setDate(28);this.__msh_oldSetFullYear(y);};window._dynarch_popupCalendar=null;
// ** I18N

// Calendar EN language
// Author: Mihai Bazon, <mihai_bazon@yahoo.com>
// Encoding: any
// Distributed under the same terms as the calendar itself.

// For translators: please use UTF-8 if possible.  We strongly believe that
// Unicode is the answer to a real internationalized world.  Also please
// include your contact information in the header, as can be seen above.

// full day names
Calendar._DN = new Array
("Sunday",
 "Monday",
 "Tuesday",
 "Wednesday",
 "Thursday",
 "Friday",
 "Saturday",
 "Sunday");

// Please note that the following array of short day names (and the same goes
// for short month names, _SMN) isn't absolutely necessary.  We give it here
// for exemplification on how one can customize the short day names, but if
// they are simply the first N letters of the full name you can simply say:
//
//   Calendar._SDN_len = N; // short day name length
//   Calendar._SMN_len = N; // short month name length
//
// If N = 3 then this is not needed either since we assume a value of 3 if not
// present, to be compatible with translation files that were written before
// this feature.

// short day names
Calendar._SDN = new Array
("Sun",
 "Mon",
 "Tue",
 "Wed",
 "Thu",
 "Fri",
 "Sat",
 "Sun");

// First day of the week. "0" means display Sunday first, "1" means display
// Monday first, etc.
Calendar._FD = 0;

// full month names
Calendar._MN = new Array
("January",
 "February",
 "March",
 "April",
 "May",
 "June",
 "July",
 "August",
 "September",
 "October",
 "November",
 "December");

// short month names
Calendar._SMN = new Array
("Jan",
 "Feb",
 "Mar",
 "Apr",
 "May",
 "Jun",
 "Jul",
 "Aug",
 "Sep",
 "Oct",
 "Nov",
 "Dec");

// tooltips
Calendar._TT = {};
Calendar._TT["INFO"] = "About the calendar";

Calendar._TT["ABOUT"] =
"DHTML Date/Time Selector\n" +
"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-)
"For latest version visit: http://www.dynarch.com/projects/calendar/\n" +
"Distributed under GNU LGPL.  See http://gnu.org/licenses/lgpl.html for details." +
"\n\n" +
"Date selection:\n" +
"- Use the \xab, \xbb buttons to select year\n" +
"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" +
"- Hold mouse button on any of the above buttons for faster selection.";
Calendar._TT["ABOUT_TIME"] = "\n\n" +
"Time selection:\n" +
"- Click on any of the time parts to increase it\n" +
"- or Shift-click to decrease it\n" +
"- or click and drag for faster selection.";

Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)";
Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)";
Calendar._TT["GO_TODAY"] = "Go Today";
Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)";
Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)";
Calendar._TT["SEL_DATE"] = "Select date";
Calendar._TT["DRAG_TO_MOVE"] = "Drag to move";
Calendar._TT["PART_TODAY"] = " (today)";

// the following is to inform that "%s" is to be the first day of week
// %s will be replaced with the day name.
Calendar._TT["DAY_FIRST"] = "Display %s first";

// This may be locale-dependent.  It specifies the week-end days, as an array
// of comma-separated numbers.  The numbers are from 0 to 6: 0 means Sunday, 1
// means Monday, etc.
Calendar._TT["WEEKEND"] = "0,6";

Calendar._TT["CLOSE"] = "Close";
Calendar._TT["TODAY"] = "Today";
Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value";

// date formats
Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d";
Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e";

Calendar._TT["WK"] = "wk";
Calendar._TT["TIME"] = "Time:";

/*  Copyright Mihai Bazon, 2002, 2003  |  http://dynarch.com/mishoo/
 * ---------------------------------------------------------------------------
 *
 * The DHTML Calendar
 *
 * Details and latest version at:
 * http://dynarch.com/mishoo/calendar.epl
 *
 * This script is distributed under the GNU Lesser General Public License.
 * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
 *
 * This file defines helper functions for setting up the calendar.  They are
 * intended to help non-programmers get a working calendar on their site
 * quickly.  This script should not be seen as part of the calendar.  It just
 * shows you what one can do with the calendar, while in the same time
 * providing a quick and simple method for setting it up.  If you need
 * exhaustive customization of the calendar creation process feel free to
 * modify this code to suit your needs (this is recommended and much better
 * than modifying calendar.js itself).
 */
 Calendar.setup=function(params){function param_default(pname,def){if(typeof params[pname]=="undefined"){params[pname]=def;}};param_default("inputField",null);param_default("displayArea",null);param_default("button",null);param_default("eventName","click");param_default("ifFormat","%Y/%m/%d");param_default("daFormat","%Y/%m/%d");param_default("singleClick",true);param_default("disableFunc",null);param_default("dateStatusFunc",params["disableFunc"]);param_default("dateTooltipFunc",null);param_default("dateText",null);param_default("firstDay",null);param_default("align","Br");param_default("range",[1900,2999]);param_default("weekNumbers",true);param_default("flat",null);param_default("flatCallback",null);param_default("onSelect",null);param_default("onClose",null);param_default("onUpdate",null);param_default("date",null);param_default("showsTime",false);param_default("timeFormat","24");param_default("electric",true);param_default("step",2);param_default("position",null);param_default("cache",false);param_default("showOthers",false);param_default("multiple",null);var tmp=["inputField","displayArea","button"];for(var i in tmp){if(typeof params[tmp[i]]=="string"){params[tmp[i]]=document.getElementById(params[tmp[i]]);}}if(!(params.flat||params.multiple||params.inputField||params.displayArea||params.button)){alert("Calendar.setup:\n  Nothing to setup (no fields found).  Please check your code");return false;}function onSelect(cal){var p=cal.params;var update=(cal.dateClicked||p.electric);if(update&&p.inputField){p.inputField.value=cal.date.print(p.ifFormat);if(typeof p.inputField.onchange=="function")p.inputField.onchange();}if(update&&p.displayArea)p.displayArea.innerHTML=cal.date.print(p.daFormat);if(update&&typeof p.onUpdate=="function")p.onUpdate(cal);if(update&&p.flat){if(typeof p.flatCallback=="function")p.flatCallback(cal);}if(update&&p.singleClick&&cal.dateClicked)cal.callCloseHandler();};if(params.flat!=null){if(typeof params.flat=="string")params.flat=document.getElementById(params.flat);if(!params.flat){alert("Calendar.setup:\n  Flat specified but can't find parent.");return false;}var cal=new Calendar(params.firstDay,params.date,params.onSelect||onSelect);cal.setDateToolTipHandler(params.dateTooltipFunc);cal.showsOtherMonths=params.showOthers;cal.showsTime=params.showsTime;cal.time24=(params.timeFormat=="24");cal.params=params;cal.weekNumbers=params.weekNumbers;cal.setRange(params.range[0],params.range[1]);cal.setDateStatusHandler(params.dateStatusFunc);cal.getDateText=params.dateText;if(params.ifFormat){cal.setDateFormat(params.ifFormat);}if(params.inputField&&typeof params.inputField.value=="string"){cal.parseDate(params.inputField.value);}cal.create(params.flat);cal.show();return false;}var triggerEl=params.button||params.displayArea||params.inputField;triggerEl["on"+params.eventName]=function(){var dateEl=params.inputField||params.displayArea;var dateFmt=params.inputField?params.ifFormat:params.daFormat;var mustCreate=false;var cal=window.calendar;if(dateEl)params.date=Date.parseDate(dateEl.value||dateEl.innerHTML,dateFmt);if(!(cal&&params.cache)){window.calendar=cal=new Calendar(params.firstDay,params.date,params.onSelect||onSelect,params.onClose||function(cal){cal.hide();});cal.setDateToolTipHandler(params.dateTooltipFunc);cal.showsTime=params.showsTime;cal.time24=(params.timeFormat=="24");cal.weekNumbers=params.weekNumbers;mustCreate=true;}else{if(params.date)cal.setDate(params.date);cal.hide();}if(params.multiple){cal.multiple={};for(var i=params.multiple.length;--i>=0;){var d=params.multiple[i];var ds=d.print("%Y%m%d");cal.multiple[ds]=d;}}cal.showsOtherMonths=params.showOthers;cal.yearStep=params.step;cal.setRange(params.range[0],params.range[1]);cal.params=params;cal.setDateStatusHandler(params.dateStatusFunc);cal.getDateText=params.dateText;cal.setDateFormat(dateFmt);if(mustCreate)cal.create();cal.refresh();if(!params.position)cal.showAtElement(params.button||params.displayArea||params.inputField,params.align);else cal.showAt(params.position[0],params.position[1]);return false;};return cal;};
/*	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
*/
var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
/* /main.js */ /* UTF8 COOKIE: éà */

/* API RETURN CODES */

var	E_OK = 0;
var	E_INPUT = 1;
var	E_SYS = 2;
var	E_CRED = 3;
var	E_NOTACTIVATED = 4;
var	E_DEACTIVATED = 5;
var	E_AUTH = 6;
var	E_NOSUCH = 7;
var	E_CONFLICT = 8;
var	E_LIMIT = 9;
var	E_ALREADY = 10;
var	E_CANCELED = 11;
var	E_EMPTY = 12;
var	E_SYNC = 13;
var	E_TIMEOUT = 14;
var	E_SIZE = 15;

/* Common methods */

function	goTo(url)	// [, forceLoading ]
{
	if(
		!vkDom.hasClass(document.body, 'corp') ||
		(
			arguments.length > 1 &&
			arguments[1] == true
		)
	)
		loading();

	window.location = url;
	return false;
}

function	_popupObject()
{
	if(vkDom.hasClass(document.body, 'modal'))
		return vkModal;
	else
		return vkPopup;
}

function	loading()
{
	_popupObject().status('Loading...', 'Please wait while loading', 'wait', null);
}

function	errorRetry(retry)
{
	_popupObject().yesno('Unexpected problem', 'An unexpected problem occured.\nWould you like to retry?', 'question', retry, true);
}

function	errorFatal()
{
	_popupObject().message('An error occured', 'An error occured.\nPlease contact us if the problem persists.', 'error', goLogin);
}

function	timeoutRetry(retry)
{
	_popupObject().yesno('Operation timed out', 'The operation timed out.\nDo you want to retry?', 'question', retry);
}

function	timeoutNoRetry()
{
	_popupObject().message('Operation timed out', 'The operation timed out.', 'warn', null);
}

function	timeoutFatal()
{
	_popupObject().message('Operation timed out', 'The operation timed out.\nPlease contact us if the problem persists..', 'error', goLogin);
}

function	authError()
{
	_popupObject().message('Please authenticate', 'This feature requires user authentication.\nPlease log-in and try again.', 'warn', goLogin);
}

function	askExitWithoutSaving(callback)
{
	if(typeof(callback) != 'function')
		return;

	_popupObject().yesno(
		'Exit without saving?', 
			'Not saved.\n'+
			'\n'+
			'Do you want to exit anyway?',
		'ask',
		function(yn)
		{
			if(yn)
				callback();
		},
		false
	);
}

function	goLogin()	// [, dontLogout ]
{
	/*
	if(vkDom.hasClass(document.body, 'corp'))
		goTo('/');
	else
	{
		if(arguments.length && arguments[0])
			goTo('/login.php');
		else
			goTo('logout.php');
	}
	*/

	if(arguments.length && arguments[0])
		goTo('/login.php');
	else
		goTo('logout.php');

	return false;
}

// Corp
function	watchTour()
{
	window.open('http://video.kigo.net/20080813/guided_tour.html', 'watchTour', 'resizable=0,scrollbars=0,width=933,height=710');
	return false;
}


/**************************************************************************************************/
/* Forms handling */

function	resetMissingFields(arr)
{
	var	i;

	if(arr == null || typeof(arr) != 'object' || !arr['length'])
	{
		kigoDebug.text('resetMissingFields(): bad argument.');
		return;
	}

	for(i = 0; i < arr.length; i++)
	{
		if(!vkDom.el(arr[i]))
		{
			kigoDebug.text('resetMissingFields(): skipping unknown field: '+arr[i]);
			continue;
		}

		vkDom.removeClass(arr[i], 'missing');
	}
}

// string
function	setMissingField(field, first)
{
	if(!vkDom.el(field))
	{
		kigoDebug.text('setMissingField(): no such field: '+field);
		return first;
	}

	vkDom.addClass(field, 'missing');
	return (first == null ? field : first);
}

// object
function	buildPostObject(fields)
{
	var	i, el, obj = {};

	for(i = 0; i < fields.length; i++)
	{
		if(!(el = vkDom.el(fields[i])))
		{
			kigoDebug.text('buildPostObject(): skipping unknown field: '+fields[i]);
			continue;
		}

		obj[fields[i]] = vkDom.getFormValue(el);
		
		// Convert checkboxes true/false values into 1/0
		if(el.tagName == 'INPUT' && el.getAttribute('type').toUpperCase() == 'CHECKBOX')
			obj[fields[i]] = obj[fields[i]] ? 1 : 0;
	}
	
	return obj;
}

// void
function	resetFormFields(fields)
{
	var	i, el;

	for(i = 0; i < fields.length; i++)
	{
		if(!(el = vkDom.el(fields[i])))
		{
			kigoDebug.text('resetFormFields(): skipping unknown field: '+fields[i]);
			continue;
		}

		vkDom.setFormValue(el, null);
	}
}


// void
function	fillFormFields(fields, data)
{
	var	i, el;

	for(i = 0; i < fields.length; i++)
	{
		if(!(el = vkDom.el(fields[i])))
		{
			kigoDebug.text('resetFormFields(): skipping unknown field: '+fields[i]);
			continue;
		}

		vkDom.setFormValue(el, data[fields[i]]);
	}
}

/**************************************************************************************************/
/* Ajax specific */
var	ajax;

function	getAjax()
{
	/*
	if(ajax != null)
		ajax.abort();
	*/
	// 03/04/2009
	if(ajax != null)
		ajax.free();

	ajax = new vkJSONRemote();
}


/**************************************************************************************************/
/* Menu handler */

var	onAppClick = function(url)
{
	function	goToUrl()
	{
		goTo(url);
	}

	if(	
		typeof(window['changesDetected']) != 'undefined' &&
		window['changesDetected']
	)
		askExitWithoutSaving(goToUrl);		// Ask the user...
	else
		goToUrl();

	return false;
}


/**************************************************************************************************/
/* Configure vkDebug */

var	kigoDebug = new vkDebug();
kigoDebug.enable(_enableDebug);


/**************************************************************************************************/
/* Setup Form & UA monitors */

var	kigoFormMonitor = new vkFormMonitor();


/**************************************************************************************************/
/* onLoad()'s are now grouped */

vkDom.onLoad(
	function()
	{
		/*****************************************/
		/* Per-browser single login verification */


		if(
			vkDom.hasClass(document.body, 'app') &&
			vkDom.hasClass(document.body, 'page')
		)
		{
			if(_owner != null)
			{
				// Setup verification
				var interval = setInterval(
					function()
					{
						if(vkDom.getCookie('_CURRENT_OWNER') != _owner)
						{
							vkPopup.message(
								'Account logged out',
									'This owner account is logged out.\n'+
									'\n'+
									'Only one owner account can be open at a time.',
								'warn',
								function() { goLogin(true /* don't logout */); }
							);

							clearInterval(interval);
						}
					},
					950
				);
			}
			else if(_ra != null)
			{
				// Setup verification
				var interval = setInterval(
					function()
					{
						if(vkDom.getCookie('_CURRENT_RA') != _ra)
						{
							vkPopup.message(
								'Account logged out',
									'This rental agency account is logged out.\n'+
									'\n'+
									'Only one rental agency account can be open at a time.',
								'warn',
								function() { goLogin(true /* don't logout */); }
							);

							clearInterval(interval);
						}
					},
					950
				);
			}
		}

		
		/***************************/
		/* Display content on load */
	
		vkDom.display('container', 'block');


		/*********************/
		/* vk* setup & fixes */


		// 02/11/2009 - Reintroducing focus interception, in order to take account of the accordion control

		vkDom._kigo_focus = vkDom.focus;

		vkDom.focus = function(el)
		{
			if(!(el = this.el(el)))
				return;
		
			if(el.tagName == 'INPUT')
				kigoAccordion.openByChild(el);

			this._kigo_focus(el);
		}

		vkDom._kigo_select = vkDom.select;

		vkDom.select = function(el)
		{
			if(!(el = this.el(el)))
				return;
		
			if(el.tagName == 'INPUT')
				kigoAccordion.openByChild(el);

			this._kigo_select(el);
		}




		// 14/05/2009 - Also patch vkPopup.localModal
		vkPopup._kigo_localModal = vkPopup.localModal;

		vkPopup.localModal = function(title, width, height, content)
		{
			var	modal;

			if(!(content = vkDom.el(content)))
				return;

			// Remove content before opening modal...
			this._kigo_localModal_content = content;
			this._kigo_localModal_content_parent = content.parentNode;
			content.parentNode.removeChild(content);

			// Open modal
			modal = this._kigo_localModal(title, width, height);
			vkDom.addClass(modal, 'localmodalcontainer');
			vkDom.addClass(content, 'localmodal');
			modal.appendChild(content);

			return modal;
		}

		vkPopup._kigo_closeModal = vkPopup.closeModal;

		vkPopup.closeModal = function()
		{
			if(this._kigo_localModal_content != undefined && this._kigo_localModal_content != null)
			{
				this._kigo_localModal_content.parentNode.removeChild(this._kigo_localModal_content);

				this._kigo_localModal_content_parent.appendChild(this._kigo_localModal_content);

				this._kigo_localModal_content = null;
				this._kigo_localModal_content_parent = null;
			}

			this._kigo_closeModal();
		}


		/***********************/
		/* ToolTips processing */
		kigoToolTips.autoSetup(document.body);
		



		/**********************/
		/* Input filter setup */

		function	inputFilter(ev)
		{
			var	obj;
			var	charCode;

			if(ev.target)
				obj = ev.target;
			else if(ev.srcElement)
				obj = ev.srcElement;
			else
				return true;


			if(!obj.getAttribute('filter'))
				return true;

			// Okay, first of all, whatever the filter is, allow any selection keys (backspace, arrows, home, end...)
			
			if(ev['charCode'])
				charCode = ev.charCode;
			else if(ev['keyCode'])
				charCode = ev.keyCode;
			else
				return true;

			// vkDebug.text('charCode='+charCode);

			switch(charCode)
			{
				case 8:		// Backspace
				case 9:		// Tab
				case 13:	// Enter
				// case 16:	// Shift
				// case 17:	// Ctrl
				// case 18:	// Alt
				case 35:	// End
				case 36:	// Home
				case 37:	// Left
				case 38:	// Up
				case 39:	// Right
				case 40:	// Down
				// case 45:	// Insert
				// case 46:	// Delete
					return true;
			}


			function	_if_cancel(ev)
			{
				if(ev.preventDefault)
					ev.preventDefault();
				return false;
			}

			switch(obj.getAttribute('filter'))
			{
				case 'money':			// Positive or negative monetary amounts

					// Rules:
					// - Digits and . (dot) only
					// - at least one digit prior to the dot
					// - at most two digits after the dot

					// 15/01/2008 - As for now, only filter characters (digits, dot and one )

					if(
						(charCode >= 48 && charCode <= 57) ||		// 0 - 9
						charCode == 44 ||							// ,
						charCode == 45 ||							// -
						charCode == 46								// .
					)
						return true;
					else
						return _if_cancel(ev);


				case 'money_positive':	// Positive amounts only

					// vkDebug.text('positive');
			
					if(
						(charCode >= 48 && charCode <= 57) ||		// 0 - 9
						charCode == 44 ||							// ,
						charCode == 46								// .
					)
						return true;
					else
						return _if_cancel(ev);
				

				case 'digits':			// Digits only

					if(charCode == 46)	// . and <del>
					{
						// Find a solution to filter <del> only.. until then, allow!
						return true;
					}

					if((charCode >= 48 && charCode <= 57))			// 0 - 9
						return true;
					else
						return _if_cancel(ev);
					
					break;

				case 'hour':
					break;

				case 'minute':
					break;

				case 'custom':
					// obj.getAttribute('customfilter')
					break;

				// 07/01/2009
				case 'url':

					switch(charCode)
					{
						case 32:	// [space]
						//case 46:	// .			-	Both . and <del> => Find a solution to filter <del> only.. until then, allow!
						case 47:	// /
						case 92:	// \
						case 91:	// [
						case 93:	// ]
							return _if_cancel(ev);

						default:
							return true;
					}
					break;
			}

			return true;
		}

		// Note that on IE the event is attached to the body...
		// When attached to the window, it's not called

		if(window.addEventListener)
			window.addEventListener('keypress', inputFilter, false);
		else if(window.attachEvent)
			document.body.attachEvent('onkeypress', inputFilter);



		/*********************/
		/* Configure vkPopup */

		vkPopup.setZIndexBase(500);
		vkPopup.setLang('EN');
	}
);

/* /js/kigoAppMenu.js */ /* UTF8 COOKIE: éà */

var	kigoAppMenu = function()
{
	var	self = this;

	this.items = [
		'RES_UNRD_REQ_COUNT',
		'RES_UNRD_HOLD_COUNT',
		'RES_UNRD_CONFIRMED_COUNT',
		'RES_UNRD_CANCELED_COUNT',
		'ORC_INCOMING_COUNT'
	];

	this.minTimer = 10000;	
	this.maxTimer = 60000;
	this.idleStep = 30;	// seconds
	this.idleTimerIncreaseStep = 1000;

	this.currentTimer = this.minTimer;


	function	reschedule()
	{
		//kigoDebug.text('Next update in '+self.currentTimer+'ms');

		setTimeout(
			function()
			{
				self.ajaxRequest.run();
			},
			self.currentTimer
		);
	}


	this.ajaxRequest = new kigoAjaxRequest2(
		function(data)
		{
			var	i, item, element;

			for(i = 0; i < self.items.length; i++)
			{
				item = self.items[i];
				element = 'KAM_'+item;

				if(vkDom.el(element))
				{
					if(
						typeof(data[item]) != 'undefined' &&
						data[item] > 0	
					)
						vkDom.setText(element, ' ('+data[item]+')');
					else
						vkDom.setText(element, '');
				}
			}

			reschedule();
			
			return true;
		},
		'ajax/app_menu/notifications.php'
	);

	this.ajaxRequest.silent(true);
	//this.ajaxRequest.enableDebug(kigoDebug);

	this.ajaxRequest.onTimeout = this.ajaxRequest.onError = function()
	{
		self.currentTimer = self.minTimer;
		reschedule();
	}

	this.uaMonitor = new vkUserIdleMonitor(
		function(mon)
		{
			// User has been idle for a minute > increase timer...
			if( (self.currentTimer += self.idleTimerIncreaseStep) > self.maxTimer)
				self.currentTimer = self.maxTimer;

			mon.resume();
		},
		30
	);

	this.uaMonitor.activityCallback = function() { self.currentTimer = self.minTimer; };

	vkDom.onLoad(
		function()
		{
			// At least one of the items must exist...
			for(var i = 0; i < self.items.length; i++)
			{
				if(vkDom.el('KAM_'+self.items[i]))
				{
					self.uaMonitor.monitor();
					self.ajaxRequest.run();
					return;
				}
			}
		}
	);
}



var	kam = new kigoAppMenu();



kigo.CFG = {"MISC":{"MAXLEN_EMAIL":200,"MAXLEN_URL":200},"ACCOUNT":{"USERNAME_MIN_CHARS":4,"USERNAME_MAX_CHARS":20,"PASSWORD_MIN_CHARS":5},"PROP":{"MAX_BEDROOMS":20,"MAX_BEDS":20,"MAXLEN_PROP_NAME":100,"MAXLEN_STREETNO":8,"MAXLEN_ADDR1":60,"MAXLEN_ADDR2":35,"MAXLEN_ADDR3":35,"MAXLEN_APTNO":10,"MAXLEN_POSTCODE":8,"MAXLEN_CITY":26,"MAXLEN_REGION":35,"MAXLEN_PHONE":30,"MAXLEN_FLOOR":4,"MAXLEN_PHOTO_NAME":50,"PRICING":{"MAX_DOWN_DAYS":99,"MAX_REMAINING_DAYS":99,"MAX_RANGES":10,"MAXLEN_PERIOD":50}},"RES_GE":{"MAX_FILES":3,"MAX_RCPT":5,"MAX_FILE_SIZE_MB":1},"WS":{"KIGODOMAIN_SUFFIX":"kigo.net","KIGODOMAIN_MINLEN":3,"KIGODOMAIN_MAXLEN":30,"CUSTOMDOMAIN_MINLEN":4,"CUSTOMDOMAIN_MAXLEN":80,"GOOGLE_SITE_VERIFY_MIN_LENGTH":1,"GOOGLE_SITE_VERIFY_MAX_LENGTH":100,"GOOGLE_ANALYTICS_MIN_LENGTH":6,"GOOGLE_ANALYTICS_MAX_LENGTH":32,"WSL_FS":{"IMGTYPE_JPEG":1,"IMGTYPE_SWF":2,"IMGTYPE_GIF":8,"IMGTYPE_PNG":4}}}
kigo.CONST = {"RES":{"ORIGIN_OWNER":"OWNER","ORIGIN_ORC_SHARE":"ORC_SHARE","ORIGIN_KIGO":"KIGO","FOR_OWNER":"OWNER","FOR_ORC_SHARE":"ORC_SHARE","FOR_OWNER_RA":"OWNER_RA"},"PROP":{"PRICING":{"N_MON":1,"N_TUE":2,"N_WEN":4,"N_THU":8,"N_FRI":16,"N_SAT":32,"N_SUN":64,"N_NONE":0,"N_ALL":127,"RU_NIGHT":"NIGHT","RU_MONTH":"MONTH","RU_YEAR":"YEAR","RU_NIGHT_MAX":27,"RU_MONTH_MAX":11,"RU_YEAR_MAX":5}}}