/**
 * cmPrototype javascript library
 * @copyright 2005 Innovatif
 * @package Libs
 * @author Denis Arh
 */


// Some key-codes, that can be used on onkey* events
var keyCodes = {
	a			: 97,
	d    	: 100,
	i    	: 105,
	left  : 37,
	right : 39,
	up   	: 38,
	down 	: 40,
	home 	: 36,
	end  	: 35,
	del  	: 46,
	enter	: 13
}


/**
 * Checks if (mixed)needle exists in the (array)haystack
 *
 * @param  mixed Needle
 * @param  array Haystack
 * @return bool
 */
function inArray(needle, haystack){
	return null != searchArray(needle, haystack);
}

Array.prototype.inArray = function(needle) {
	return null != this.searchArray(needle);
}


/**
 * Returns position of needle in the haystack or null if not found
 *
 * @param  mixed Needle
 * @param  array Haystack
 * @return mixed (integer from 0 to haystack.length -1 or NULL)
 */
function searchArray(needle, haystack){
	for(i in haystack){
		if(haystack[i] == needle){
			return i;
		}
	}
	return null;
}

Array.prototype.searchArray = function(needle) {
	return searchArray(needle, this);
}

function Dump(input, maxDepth) {
	this.__baseDump = function(level, input, maxDepth) {
		if (level < maxDepth && 'object' == typeof(input)) {
			return "(object):<br />" + this.__readObject(level, input, maxDepth);
		} else if ('function' == typeof(input)) {
			return "("+typeof(input)+")";
		} else {
			return "("+typeof(input)+") " + input;
		}
	}

	this.__readObject = function(level, obj, maxDepth) {
		var out = '';
		for (var n in obj) {
			out += this.strRepeat('+ &nbsp; ', level)
			    + "[" + n + "] "
			    + this.__baseDump(level + 1, obj[n], maxDepth) + "<br />";
		}
		return out;
	}

	this.strRepeat = function(string, multiplier) {
		var out = '';
		for (var i = 0; i < multiplier; i++) {
			out += string;
		}
		return out;
	}

	if (maxDepth == undefined) {
		maxDepth = 20;
	}
	var dumpWindow = cmPopup('', 'about:blank', 1, 640, 480, 1, 1, 1, 0);
	if (dumpWindow.document) {
		dumpWindow.document.writeln('<h1>JS Dump</h1>');
		dumpWindow.document.writeln(this.__baseDump(0, input, maxDepth));
	}
}

var noticeWindow
function raiseNotice(notice) {
	if (!noticeWindow || !noticeWindow.document) {
		noticeWindow = cmPopup('', 'about:blank', 1, 640, 480, 1, 1, 0);
		noticeWindow.document.writeln('<h1>Notices</h1>');
	}

	noticeWindow.document.writeln('<pre>' + notice + '</pre>');
}

/**
 * void cmXmlHttp(string URL, function handler, bool async)
 *
 */
var cmXmlHttpReq = null;
function cmXmlHttp(xmlUrl, handler, adtValues) {
	//var adtValues[xmlUrl] =

	// fix for IE caching
	var aChar = (xmlUrl.indexOf('?') >=0) ? '&' : '?';
	xmlUrl+=aChar+"ms=" + new Date().getTime();
  if (window.XMLHttpRequest) {
  	// native XMLHttpRequest object
    cmXmlHttpReq = new XMLHttpRequest();
    cmXmlHttpReq.adtValues = adtValues;
    cmXmlHttpReq.onreadystatechange = function() {
			handler(adtValues);
		}
    cmXmlHttpReq.open("GET", xmlUrl, true);
    cmXmlHttpReq.send(null);

    return true;
  }
	else if (window.ActiveXObject) {
		// ActiveX
		cmXmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");

		if (cmXmlHttpReq) {
			cmXmlHttpReq.onreadystatechange = function() {
				handler(adtValues);
			}

			cmXmlHttpReq.open("GET", xmlUrl, true);
			cmXmlHttpReq.send();
    }

    return true;
  }

  return false;
}

/**
 * Advanced interface for xmlHttpRequests.
 *
 * Handlers object sample:
 * {
 *  start: 		function(adt) {}
 *  complete: function(responseXML, responseText, adt) {}
 *  error:		function(status, statusText, adt) {}
 *  adt:			*anything*
 * }
 *
 * complete() arguments: (executed only when status == 200!)
 *	- responseXML: 		parsed xml document
 *  - reposonseText:	plain text
 *
 * error() arguments:
 *	- status:					HTTP code ( 404 not found...)
 *	- statusText:     Status text
 *
 * @param string id
 * @param string url
 * @param object handlers
 * @return bool
 */
var httpRequests = new Array();
var httpRequest = {
	get: function(id, mUrl, handlers, postVars) {
		// fix for IE caching
		var aChar = (mUrl.indexOf('?') >=0) ? '&' : '?';
		mUrl+=aChar+'ms=' + new Date().getTime();
	  if (window.XMLHttpRequest) {
	  	// native XMLHttpRequest object
	    httpRequests[id] = new XMLHttpRequest();
	  } else if (window.ActiveXObject) {
			// ActiveX
			httpRequests[id] = new ActiveXObject("Microsoft.XMLHTTP");
		}

		if (httpRequests[id]) {
	    httpRequests[id].onreadystatechange = function() {
	    	if (0 == httpRequests[id].readyState && handlers.start) {
	    		//handlers.start(handlers.adt);
	    		cmCallback(handlers.start, [handlers.adt]);
	    	}

	    	if (4 == httpRequests[id].readyState) {
	    		if (handlers.complete) {
	    			cmCallback(handlers.complete, [httpRequests[id].responseXML, httpRequests[id].responseText, handlers.adt]);
	    		} else if (handlers.error) {
	    			cmCallback(handlers.error, [httpRequests[id].status, httpRequests[id].statusText]);
	    		}
	    	}
	    }

	    if (undefined == postVars) {
	    	httpRequests[id].open("GET", mUrl, true);
	    } else {
	    	httpRequests[id].open("POST", mUrl, true);
	    	httpRequests[id].setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	    }
		} else {
			return false;
		}

		if (undefined == postVars) {
			if (window.XMLHttpRequest) {
	    	httpRequests[id].send(null);
	    } else if (window.ActiveXObject) {
	    	httpRequests[id].send();
	    }
	  } else {
	  	httpRequests[id].send(postVars);
	  }

    return true;
  }
}


/**
 * Performs a callback
 *
 * @param  Mixed  Callback function or array(context, function)
 * @param  Arguments...
 */
function cmCallback(cFunction, cArguments) {
	var callback, context;

	if (0 == arguments.length) {
		return;
	}
	if ('function' == typeof(cFunction)) {
		context  = window;
		callback = cFunction;
	} else if (('array' == typeof(cFunction) || 'object' == typeof(cFunction)) && 2 == cFunction.length) {
		context  = cFunction[0];
		callback = cFunction[1];
	} else {
		return;
	}

	var execCode = 'callback.call(context';
	for (var a = 0; undefined != cArguments && a < cArguments.length; a++) {
		execCode += ',cArguments['+a+']';
	}
	execCode += ')';

	// Remember kids: Eval is evil!
	eval(execCode);

	return;
}




var _v     = navigator.userAgent.toLowerCase();
var _DOM   = document.getElementById;
var _IE    = (_v.indexOf("msie 6") > -1 && _DOM);
var _Gecko = (_v.indexOf("gecko") > -1 && _DOM);
var _Moz   = ((_Gecko)?parseInt(navigator.productSub):0 > 20020512);

var cmAgent = {
	v: 					_v,
	DOM: 				_DOM,
	IE: 				_IE,
	cssCompat:	(_IE && document.compatMode == "CSS1Compat"),
	Gecko:			_Gecko,
	Safari:			(_v.indexOf("safari") > -1 && _DOM),
	Moz:				_Moz,
	DHTML:			(_IE || _Moz),

/*
	detectVersion()

	returns browser version
*/

	detectVersion: function(){
		return navigator.appVersion;
	},


/*
	hasCookieEnabled()

	checks whether browser has cookies enabled and returns boolean
*/
	hasCookieEnabled : function(){
		/*
		var tc = 'testCookie';
		document.cookie = "testCookie=" + tc + "; path=/";
		if (-1 == document.cookie.indexOf(tc,0)) {
			return false;
		}
		*/
		return true;
   },
/*
	hasFlash()

	checks whether browser has flash player
*/
	hasFlash: function(){
		latestVersion = 10;
		if (navigator.plugins && navigator.plugins.length) {
			for(i = 0; i < navigator.plugins.length; i++) {
				if(-1 != navigator.plugins[i].name.indexOf('Shockwave Flash')){
					return navigator.plugins[i].description.split('Shockwave Flash ')[1];
				}
			}
		}

		if(window.ActiveXObject){
			for(i = latestVersion; i > 0; i--){
				try{
					var flash = new ActiveXObject("ShockwaveFlash.ShockwaveFlash." + i);
					return i;
				}
				catch(err){
				}
			}
		}
		return false;
	},

	/**
	 * detectOs()
	 * detects the operating system where the browser is running
	 */
	detectOs: function(){
		os = navigator.platform;
		if(-1 != os.indexOf('Win')){
			return 'windows';
		} else if(-1 != os.indexOf('Mac')){
			return 'mac';
		} else if(-1 != os.indexOf('BSD')){
			return 'BSD';
		} else if(-1 != os.indexOf('Linux')){
			return 'linux';
		} else {
			return 'unknown';
		}
	},

	/**
	 * detectResolution()
	 * detects window resolution and returns as array [ 0 => widht, 1 => height]
   */
	detectResolution: function(){
		return Array(
			(cmAgent.Moz)	?	innerWidth	:	document.body.clientWidth,
			(cmAgent.Moz)	?	innerHeight	:	document.body.clientHeight
		)
	},

	/**
	 * detectScreen()
	 * detects screen resolution and returns it as array [ 0 => widht, 1 => height]
	 */
	detectScreen: function(){
		return Array(screen.width, screen.height);
	},

	/**
	 * getColorDepth()
	 *
	 * detects color depth of the screen
	 */
	getColorDepth: function() {
		return screen.colorDepth;
	}


}

// Why functions? becouse this must be called AFTER page is loaded...
// IE will complain if you call them before.
var cmPage = {
	getWidth: function() {
		return (
			(!cmAgent.Moz && document.documentElement && document.documentElement.clientWidth)
			?
			// IE + DOCTYPE width
			document.documentElement.clientWidth
			:
			// IE - DOCTYPE width
			document.body.clientWidth
		);
	},
	getHeight: function() {
		return (
			(!cmAgent.Moz && document.documentElement && document.documentElement.clientHeight)
			?
			// IE + DOCTYPE height
			document.documentElement.clientHeight
			:
			// IE - DOCTYPE height
			document.body.clientHeight
		);
	}
};


Date._SEC   =  1;
Date._MIN   =  2;
Date._HOUR  =  4;
Date._DAY   =  8;
Date._MONTH = 16;
Date._YEAR  = 32;

Date._TIME      = Date._SEC  | Date._MIN   | Date._HOUR;
Date._DATE      = Date._DAY  | Date._MONTH | Date._YEAR;
Date._DATETIME  = Date._TIME | Date._DATE;


/*
	Date.parseIso(isoFormatedDate)

	Parses iso formated date-time (YYYY-MM-DD HH:MM:SS) and sets
	values.
*/
Date.prototype.parseIso = function(isoFormatedDate) {
	var datetime = isoFormatedDate.split(' ');
	var time     = datetime[1].split(':');

	this.setSeconds(parseInt(time[2], 10));	 // SS
	this.setMinutes(parseInt(time[1], 10));	 // MM
	this.setHours(  parseInt(time[0], 10));	 // HH

	var date     = datetime[0].split('-');

	this.setFullYear(
		parseInt(date[0], 10),     // YYYY
		parseInt(date[1], 10) - 1, // MM
		parseInt(date[2], 10)      // DD
	);
}


/*
	Date.compare(date, compareFields)

	compares two date objects and returns true if they are the same

	date            = date to compare to
	compareFields		= fields to compare (Date._SEC ... Date._YEAR)
*/
Date.prototype.compare = function(date, compareFields){
	if (undefined == compareFields) {
		compareFields = Date._DATE;
	}

	if ((0 < compareFields & Date._SEC) && (this.getMinutes() != date.getMinutes())) {
		return false;
	}

	if ((0 < compareFields & Date._MIN) && (this.getMinutes() != date.getMinutes())) {
		return false;
	}

	if ((0 < compareFields & Date._HOUR) && (this.getHours() != date.getHours())) {
		return false;
	}

	if ((0 < compareFields & Date._DAY) && (this.getDate() != date.getDate())) {
		return false;
	}

	if ((0 < compareFields & Date._MONTH) && (this.getMonth() != date.getMonth())) {
		return false;
	}

	if ((0 < compareFields & Date._YEAR) && (this.getFullYear() != date.getFullYear())) {
		return false;
	}

	return true;
};


/*
 * Returns days in current (or specified) month
 *
 * @param   int Current month (0 - jan, 11 - dec) - optional
 * @returns int -1 if out of range, 28-31 if valid.
 */
Date.prototype.getDaysInMonth = function(cmon){
	var dim  = [31,(this.isLeapYear?29:28),31,30,31,30,31,31,30,31,30,31];

	if (undefined == cmon) {
		cmon = this.getMonth();
	}

	if (0 <= cmon || 11 >= cmon) {
		return dim[cmon];
	}
	return -1;
}

/**
 * Returns true if given or current (object's) year is leap
 *
 *
 */
Date.prototype.isLeapYear = function(year) {
	if (undefined == year) {
		year = this.getFullYear();
	}

	return (0 == year % 4) || (0 == year % 100 && 0 != year % 400);
}


/**
 * Object helpers
 *
 *
 */
function objectClone(input) {
	for (var p in input) {
		if (undefined == input[p]) {
			this[p] = undefined;
		} else if (null == input[p]) {
			this[p] = null;
		} else if ('object' == typeof(input[p])) {
			this[p] = new objectClone(input[p]);
		} else {
			this[p] = input[p];
		}
	}
}

Object.prototype.clone = function() {
	return new objectClone(this);
}


var cmDOM = {

	/**
	 * void toggleVisibility(string id[, bool state])
	 *
	 * Toggles object's visiblity. Visibility will be forced by second
	 * parameter (if it is set) otherwise we use negated current state.
	 */
	toggleVisibility: function(ref, state) {
	  var el = ('object' == typeof(ref) ? ref : $(ref));

	  if (undefined == el) {
	  	return;
	  }

		if (undefined == state) {
		  state = el.style.display != 'none';
		}
		el.style.display = ( state ? '' : 'none' );
	},

	/**
	 * void removeChildren(string id)
	 *
	 * Removes all sub-nodes from given object.
	 */
	clean: function(ref) {
		var el = ('object' == typeof(ref) ? ref : $(ref));

		if (el && el.childNodes) {
			while (0 < el.childNodes.length) {
				el.removeChild(el.childNodes[0]);
			}
		}
	},

	/**
	 * Appends text node with <text> content to <ref> DOM object
	 *
	 */
	addText: function(ref, text) {
		var el = ('object' == typeof(ref) ? ref : $(ref));

		el.appendChild(document.createTextNode(text));
	},

	/**
	 * document.createElement wrapper
	 *
	 * If parent node is specified, created element is
	 * auto-appended to it.
	 */

	crEl: function(el, parentNode) {
		var newEl = document.createElement(el);

		if (undefined != parentNode) {
			parentNode.appendChild(newEl);
		}

		return newEl;
	},

	divText: function(text) {
		var div = document.createElement('div');
		div.appendChild(document.createTextNode(text));
		return div;
	}
}


/*****************************************************************************
 ** Images
 **/

function cmImage() {};

/**
 * Preloads images. Path to images can be given as an array in the first
 * argument or as a series of arguments.
 */
cmImage.preload = function() {
	var input;

	if (!document.images){
		return false;
	}

	if (1 == arguments.length && 'Array' == typeof(arguments[0])) {
		input = arguments[0];
	} else if (0 < arguments.length) {
		input = arguments;
	} else {
		return false;
	}

	var ar = new Array(input.length);
	for (var i = 0; i < input.length; i++) {
		if ('string' == typeof(input[i]) && 0 < input[i].length) {
			ar[i] = new Image();
			ar[i].src = input[i];
		}
	}

	return true;
}


cmImage.tempSrc = new Array();

/**
 * Swaps image source.
 * Original source is saved for swapBack() function
 */
cmImage.swapTo = function(imgId, newSrc, tempSrcId) {
	var imgObj = ('string' == typeof(imgId) ? $(imgId) : imgId);

	tempSrcId = (undefined == tempSrcId ? 0 : tempSrcId);

	this.tempSrc[tempSrcId] = imgObj.src;
	imgObj.src              = newSrc;
}

/**
 * Swaps image source back to original
 */
cmImage.swapBack = function(imgId, tempSrcId) {
	var imgObj = ('string' == typeof(imgId) ? $(imgId) : imgId);

	tempSrcId = (undefined == tempSrcId ? 0 : tempSrcId);

	imgObj.src  = this.tempSrc[tempSrcId];
}


function cmNavigator(){

/*
	cookies2object()

	object storing cookies in its properties
	exposes the method getCookie(n)

	n = cookie name

*/

	this.cookies2object = function(){
		document.write('<pre>'+document.cookie+'</pre>');
		cookiesArray = document.cookie.split('; ');
		for(i in cookiesArray){
			pair = cookiesArray[i].split('=');
			this[pair[0]] = pair[1];
		}

		this.getCookie = function(n){
			return this[n];
		};

		return this;
	};

/*
	object2cookie(obj)

	converts each property of the given object to a cookie

	obj = the object whose properties are going to be stored as cookies

*/

	this.object2cookie = function(obj){
		for(i in obj){
			document.cookie = i + ' = ' + obj[i];
		}
	};

/*
	get object
	stores get string and exposes the method retrieve
		getVar(varName)

		returns the value of one property

		prop = the property to return

*/
	this.get = function(){
		getString	= document.location.search.substr(1);
		if(getString != ''){
			getPairs		= getString.split('&');
			for(i in getPairs){
				pair = getPairs[i].split('=');
				this[unescape(pair[0])] = unescape(pair[1]);
			}
		}

		this.retrive = function(prop){
			if(this[prop] == undefined){
				return undefined;
			}
			return this[prop];
		};

		return this;
	};

/*
	redirectOnCondition(cond, url, cond, url ....)

	for each arguments checks the condition and redirect user to another specified page

	cond 	= conition to check
	url 	= url where to redirect

*/
	this.redirectOnCondition = function(){
		for(i = 0; i < arguments.length; i += 2){
			if(eval(arguments[i])){
				window.location.href = arguments[i + 1];
			}
		}
	};
}

function cmPopup(name, path, focus, width, height, resizable, scrollbars, status, dependant) {
	var left_offset, top_offset, winhandler;
	var settings = '';

  if (1 == dependant) {
  	if (window.showModalDialog) {
    	return window.showModalDialog(path,'dialogWidth:'+width+'px; dialogHeight:'+height+'px');
  	}
  	else {
  		settings = 'modal=yes,dialog=no,minimizable=yes,dependent=yes,';
  	}
  }

  settings = settings + 'resizable='  + (resizable  ? 'yes' : 'no') + ',';
  settings = settings + 'scrollbars=' + (scrollbars ? 'yes' : 'no') + ',';
  settings = settings + 'status='     + (status     ? 'yes' : 'no') + ',';

  if ( width ) {
    if ( height == null ) {
      height = width;
    }

		if ( navigator.appName == "Microsoft Internet Explorer" && parseInt(navigator.appVersion) >= 4 ) {
			left_offset = ( screen.Availwidth  ) ? ( screen.Availwidth  - width  ) / 2 : 100;
			top_offset  = ( screen.Availheight ) ? ( screen.Availheight - height ) / 2 : 100;
		}
		else {
			left_offset = ( screen.width       ) ? ( screen.width  - width  )     / 2 : 100;
			top_offset  = ( screen.height      ) ? ( screen.height - height )     / 2 : 100;
		}

    settings = settings + 'width='+width+',height='+height+',top='+top_offset+',left='+left_offset;
  }

  try {
		winhandler = window.open(path,name,settings);

		if (focus && winhandler && winhandler.focus) {
			winhandler.focus();
		}
  } catch (e) {};

  return winhandler;
};

/******************************************************************************
 ** Some magick
 **/

function $(elementId) {
  return document.getElementById(elementId);
}

function setSelectionRange(input, selS, selE) {
  if (input.setSelectionRange) {
    input.focus();
    input.setSelectionRange(selS, selE);
  }
  else if (input.createTextRange) {
    var range = input.createTextRange();
    range.collapse(true);
    range.moveEnd('character', selE);
    range.moveStart('character', selS);
    range.select();
  }
}

function replaceSelection (input, replacement) {
	if (input.setSelectionRange) {
		var selS = input.selectionStart;
		var selE = input.selectionEnd;

		input.value = input.value.substring(0, selS) + replacement + input.value.substring(selE);

		if (selS == selE){
			setSelectionRange(input, replacement.length + selS, replacement.length + selS);
		} else {
			setSelectionRange(input, selS, selS + 	replacement.length);
		}

	} else if (document.selection) {
		var range = document.selection.createRange();

		if (range.parentElement() == input) {
			var isCollapsed = range.text == '';
			range.text = replacement;

			 if (!isCollapsed)  {
				range.moveStart('character', replacement.length * -1);
				range.select();
			}
		}
	}
}


/**
 * Stack for asyn. objects (for internal use only)
 */
var asyncObjectStack = {
	_idSeq: 0,
	_calls: [],

	getCall: function(callId) {
		return this._calls[callId];
	}
}

/**
 * Prepears an asyn. object that will be called when needed.
 *
 * @param callback Function to call
 * @param mixed callback argument #1
 * @param mixed callback argument #2
 * @param mixed callback argument #3
 * @param mixed callback argument #4
 * @param mixed callback argument #5
 */

function asyncObject() {
	this.callback    = arguments[0];
	this.callbackArg = new Array();

	for (var a = 1; a < arguments.length; a++) {
		this.callbackArg[a - 1] = arguments[a];
	}

	this.id = asyncObjectStack._idSeq++;
	asyncObjectStack._calls[this.id] = this;

	this.exec = function () {
		cmCallback(this.callback, this.callbackArg);
	}
}

/**
 * Schedules future call
 * If repeat is omited or set to 0 (or 1) only one call will be executed
 * Any positive number will couse to repeat the call that many times
 * If negative number is set as repeat call will be repeated untill cleared
 *
 * Values returned from setTimeout or setInterval methods will be returned
 * so the scheduled event can be cleared later.
 *
 * Usage:
 * asyncSchedule(new asyncObject(callback, arguments...))
 *
 * Callback can be array:
 *   [(object)context, (function)callback] - calback-method is called in the context-object's context)
 * or
 *   (function)calback
 *
 * or plain old function reference
 *
 */
function asyncSchedule(aObj, delay, repeat) {
	if (undefined == repeat || 0 == repeat || null == repeat) {
		repeat = 1;
	}

	var asyncObjExec;

	if (undefined != aObj.callback) {
		// Using async object
		asyncObjExec = 'asyncObjectStack.getCall('+aObj.id+').exec()';
	} else {
		// function reference
		asyncObjExec = aObj;
	}

	if (repeat > 0) {
		for (var r = 0; r < repeat; r++) {
			return window.setTimeout(asyncObjExec, delay * (r + 1));
		}
	} else if (repeat < 0) {
		return window.setInterval(asyncObjExec, delay);
	}

	return null;
}

/**
 * Universal event2function binder
 *
 */
function addEvent(ref, evType, fn, useCapture) {
	var el = ('object' == typeof(ref) ? ref : $(ref));

	if (el.addEventListener) {
		el.addEventListener(evType, fn, useCapture);
		return true;
	/**
	 * IE (and maybe some other browsers also) can use attachEvent function.
	 * Function does not attach the copy a handler but the reference - and this is
	 * usualy not what we want it to do - especialy becouse most event handlers refer
	 * to the object that triggered the event.
	} else if (elm.attachEvent) {
		return elm.attachEvent('on' + evType, fn);
	*/
	} else {
		el['on' + evType] = fn;
		return null;
	}
}



var cssMagick = {
	addClass: function(el, name) {
		if (-1 == el.className.search(name)) {
			el.className = el.className + ' ' + name;
		}
	},

	removeClass: function(el, name) {
		el.className = el.className.replace(name, '');
	}
}

function findPos(ref) {
	var el = ('object' == typeof(ref) ? ref : $(ref));

	var pos = {left: 0, top: 0};

	if (el.offsetParent) {
		pos.left = el.offsetLeft;
		pos.top  = el.offsetTop;
		el = el.offsetParent;
		while (el) {
			pos.left += el.offsetLeft;
			pos.top  += el.offsetTop;
			el = el.offsetParent;
		}
	}
	return pos;
}