/**
 *
 * The global object `sys' simulates some features of our global $sys.
 *
 * @package    
 * @subpackage 
 * @author     Daniel Sevcik <daniel.sevcik@dev.webdevelopers.cz>
 * @version    $Revision: 1.0 $
 * @copyright  2008 Daniel Sevcik
 * @since      2008-04-30
 * @access     public
 */
function Sys() {
    this.delayedEvents=[]; // List of events to send
    this.locks={_pooling: 0}; // Locking to prevent multiple dispatching of the same set
    this.delayedEventsCounter=0; // To have unique numbers
    this.delayedEventsDispatchTime=0; // Date object of the next dispatch
    this.delayedEventsDispatchTimeout=0; // setTimeout pointer to the dispatcher
    this.mode = 'SYSTEM-REDUCED';

    /**
     * Generic function for acquiring locks.
     *
     * Example:
     * if (sys.acquireLock('cms:DOMModify', true)) {
     *   ...[my DOM modifications]...
     *   sys.releaseLock('cms:DOMModify');
     * } else {
     *   alert('DOM is locked by other process!');
     * }
     * 
     * @access public
     * @param string type Any identifier, prefix the identifier with your module id (eg. 'cms:myLock')
     * @param bool nonBlocking FALSE to wait until the lock is successfully acquired, TRUE to not wait for lock release and return ASAP even if lock is not acquired 
     * @return bool TRUE - lock is acquired (always returned for blocking acquiring), FALSE lock is alredy used/locked
     */
    this.acquireLock=function(type, nonBlocking) {
        // Init
        if (typeof this.locks[type] == 'undefined') this.locks[type]=0;

        // Acquire
        if (nonBlocking) {
            return (!this.locks[type] && ++this.locks[type] == 1);
        } else {
            while (this.locks[type] || ++this.locks[type] !== 1); // Locked
            return true;
        }
    }

    /**
     * Release the lock you previously acquired using the sys.acquireLock()
     *
     * Example:
     * if (sys.acquireLock('cms:DOMModify')) {
     *   ...[my DOM modifications]...
     *   sys.releaseLock('cms:DOMModify');
     * }
     *
     * @access public
     * @param string type The type used when calling sys.acquireLock()
     * @return void
     */
    this.releaseLock=function(type) {
        this.locks[type]=0; // Unlock        
    }


	/**
	 * Sets the system mode in which events are executed
	 * Addition authorization is applied on the server
	 *
	 * @param string
	 * @default SYSTEM-REDUCED
	 */
	this.setMode=function(mode) {
		this.mode = mode;
	}

	/**
	 * Returns current mode
	 *
	 * @return string
	 */
	this.getMode=function() {
		return this.mode;
	}

	/**
	 * Fetch the info about the user using the AJAX.
	 *
	 * The method fires the client:get-user-info to fetch the data.
	 * This event is handled by rms.toolkit by default and delivers
	 * common meta data. Other handlers may be added as needed.
	 *
	 * Example: document.getElementById('name').innerHTML=sys.getUserName('userName');
	 *
	 * @access private
	 * @param string key The key name. May be extended by handlers. By default any of: userName, userTZ, firstName, middleName, lastName, mail, phone
	 * @param string defaultValue if the key is not defined use this as default value.
	 * @return string
	 */
	this.getUserInfo=function(key, defaultValue) {		
		// Fetch info
		if (this.userInfo === false) {
			this.acquireLock('sys:getUserInfo', false);
			// Wasn't somebody faster?
			if (this.userInfo === false) {
				var results=sys.fireEvent('get-user-info');
				// Merge
				this.userInfo={};
				for (var handlerId in results) {
					for (var handlerKey in results[handlerId]) {
						this.userInfo[handlerKey]=results[handlerId][handlerKey];
					}
				}
			}
			this.releaseLock('sys:getUserInfo');
		}
		return this.userInfo[key] || defaultValue || '';
	}

    /**
     * This type of events is always asynchronous. The event will be
     * fired sometime between now and maxDelay seconds.
     *
     * The reason for this method is the speed optimization. If there is
     * more pending delayed events we can pool them and dispatch at once
     * with single request.
     *
     * USE WHENEVER POSSIBLE WITH LONGEST TIME POSSIBLE!
     * 
     * @access public
     * @param string event
     * @param mixed params mixed value or function that returns the params
     * @param mixed callBack FALSE or callback function.
     * @param int maxDelay seconds of maximum delay before firing this event.
     * @return int id Unique id of the delayed event that may be used in sys.removeDelayedEvent(id)
     */
    this.fireEventDelayed=function(event, params, callBack, maxDelay) {
        // Add to the list and generate unique id
        var id=++this.delayedEventsCounter;
        this.acquireLock('_pooling');
        this.delayedEvents.push([id, event, params, callBack, this.mode]);
        this.releaseLock('_pooling');
        
        // Calculate and schedule next dispatching
        var dispatchTime=new Date((new Date).getTime() + (maxDelay || 0) * 1000);
        if (!this.delayedEventsDispatchTime || this.delayedEventsDispatchTime > dispatchTime) {
            this.delayedEventsDispatchTime=dispatchTime;
            clearTimeout(this.delayedEventsDispatchTimeout);
            this.delayedEventsDispatchTimeout=setTimeout(function() {sys.dispatchEvents();}, maxDelay * 1000);
        }
        
        return id;
    }

    /**
     * Same as sys.fireEventDelayed() but return only the first object from the array.
     */
    this.fireEventDelayedShift=function(event, params, callBack, maxDelay) {
        var realCallBack=callBack;
        if (typeof callBack == 'function') {
            realCallBack=function(response) {callBack(sys.responseShift(response));};
        }
        return this.fireEventDelayed(event, params, realCallBack, maxDelay);
    }

    /**
     * If there is a pending delayed event remove it.
     *
     * @access public
     * @param int id obtained from sys.fireEventDelayed*() method
     * @return bool true if event was removed, false if event was already dispatched or not found
     */
    this.removeDelayedEvent=function(id) {
        this.acquireLock('_pooling');
        for(var i=0; i < this.delayedEvents.length; i++) {
            if (this.delayedEvents[i][0] == id) {
                this.delayedEvents.splice(i, 1);
                this.releaseLock('_pooling');
                return true;
            }
        }
        this.releaseLock('_pooling');
        return false;
    }

    /**
     * @access private
     */
    this.dispatchEvents=function(syncEvent) {
        // Get the pool content and reset it
        this.acquireLock('_pooling');
        var events=this.delayedEvents;
        this.delayedEvents=[]; // Remove the list asap so no other dispatcher can screw it up.
        this.delayedEventsDispatchTime=0;
        this.releaseLock('_pooling');

        // Append synchronous event
        if (syncEvent) {
            syncEvent.unshift(0);
            events.push(syncEvent);
        }

        // Nothing to send
        if (events.length == 0) return null; 

        // Prepare the body
        var params=[];
        var callBacks={};
        var debugName=[];
        for(var i=0; i < events.length; i++) {
            debugName.push(events[i][1]);
            params.push({id: events[i][0], event: events[i][1], mode: events[i][4], params: typeof events[i][2] == 'function' ? events[i][2]() : events[i][2]});
            callBacks[events[i][0]]=events[i][3];
        }

        // Send
	var req=sysCommon.sendXMLHTTPRequest('POST', '/system/js/gate.php?'+debugName.join(',')+'#'+debugName.length, false, sysCommon.serialize(params), false);

        // Process response
        if (!req.responseXML) throw 'GATE: Response is not valid XML, check the mime-type of the response, it must be text/xml!';
        if (!req.responseXML.documentElement) throw 'GATE: The response is not XML? Ready State: '+req.readyState+', Response Text: '+req.responseText;
        var response=sysCommon.deserialize(req.responseXML.documentElement);

        // Call callbacks
        var callAsync=function(method, answer) {
            setTimeout(function() {method(answer);}, 0);
        }
        for(var id in response) {
            if (typeof callBacks[id] == 'function') {
                callAsync(callBacks[id], response[id]);
            }
        }
        var ret=response[id];

        // Return the last answer (for the synchronous caller)
        return ret;
    }
    
    /**
     * Fire the Spire event.
     *
     * Example:
     * alert(sys.fireEvent('cms:update', {name: name, params: [1, 33, 52]}));
     * sys.fireEvent('cms:update', {name: name, params: [1, 33, 52]}, function() {alert('done');});     
     *
     * @access public
     * @param string event
     * @param mixed params mixed value or function that returns the params
     * @param mixed callBackAsync BOOL or callback FUNCTION. When TRUE or FUNCTION is given then the call is asynchronous.
     * @return mixed FALSE if asynchronous call otherwise return the value from the event as returned by the $sys->fireEvent()
     */
    this.fireEvent=function(event, params, callBackAsync) {
        if (callBackAsync) {
            this.fireEventDelayed(event, params, callBackAsync);
            return false;
        } else {
            return this.dispatchEvents([event, params, false, this.mode]);
        }
    }

    /**
     * Same as sys.fireEvent but returns only the first result from the response array.
     *
     * @access public
     * @param string event
     * @param mixed params
     * @param mixed callBackAsync BOOL or callback FUNCTION. When TRUE or FUNCTION is given then the call is asynchronous.
     * @return mixed FALSE if asynchronous call otherwise return the FIRST value from the event as returned by the $sys->fireEvent()
     */
    this.fireEventShift=function(event, params, callBackAsync) {
	var callBackShift=null;
	if (callBackAsync) {
	    var callBackShift=function (response) {
		return callBackAsync(sys.responseShift(response));
	    }
	}
	var response=this.fireEvent(event, params, callBackShift);
	if (!callBackShift) {
	    return this.responseShift(response);
	}
	return null;
    }

    this.responseShift=function(response) {
		for(var key in response) {
			return response[key];
		}
		return null;
    }
	
}

/**
 * These are the functions that support the `sys' object.
 * 
 * @package    
 * @subpackage 
 * @author     Daniel Sevcik <daniel.sevcik@dev.webdevelopers.cz>
 * @version    $Revision: 1.0 $
 * @copyright  2008 Daniel Sevcik
 * @since      2008-04-30
 * @access     public
 */
function SysCommon() {
    this.sendXMLHTTPRequest=function(method, url, async, value, onReady) {
	//window.console.log('method: '+method+', url: '+url+', async: '+async+', value: '+value+', onReady: '+onReady);

	var req=sysCommon.createXMLHttpRequest();
	try { // Client experienced strange 0x80004005 error 
	    req.open(method, url, async ? true : false);
	    req.setRequestHeader("Content-Type","text/xml; charset=UTF-8"); // HTTP_RAW_POST_DATA
	    sysCommon.ajaxFlag(true);
	    req.send(value || ''); //MSIE must have string not FALSE
	} catch(e) {
	    // http://devel.1stomni.com/.pm/requests/tasks/269-form.edit_269-taskId.16781768.html
	    throw "GATE: Your browser failed to contact the server.\n\nEXCEPTION:SysCommon.sendXMLHTTPRequest\n\nLocation: "+location+"\n\nAction: "+method+" "+url+"\n\nreadyState/status: "+req.readyState+"/"+req.status+"\n\nMessage:\n"+e;
	}

	var doOnReady=function() {onReady && onReady(req);}
	if (async) {
	    req.onreadystatechange=function() {
		if (sysCommon.isRequestReady(req)) {
		    sysCommon.ajaxFlag(false);
		    doOnReady();
		}
	    }
	} else {
	    sysCommon.ajaxFlag(false);	    
	    doOnReady();
	    return req;
	}
	return false;
    }

    /**
     * Get the XMLHTTPRequest Object
     *
     * @access public
     * @return Object
     */
    this.createXMLHttpRequest=function() {
	if (window.XMLHttpRequest){
	    // If IE7, Mozilla, Safari, etc: Use native object
	    var xmlHttp = new XMLHttpRequest();
	} else {
	    if (window.ActiveXObject){
		// ...otherwise, use the ActiveX control for IE5.x and IE6
		var xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
	    }				
	}
	return xmlHttp;
    }

    this.isRequestReady=function(req) {
	//0	The object has been created, but not initialized (the open method has not been called).
	//1	The object has been created, but the send method has not been called.
	//2	The send method has been called, but the status and headers are not yet available.
	//3	Some data has been received. Calling the responseBody and responseText properties at this state to obtain partial results will return an error, because status and response headers are not fully available.
	//4	All the data has been received, and the complete data is available.
	if (req.readyState != 4) return false;
	if (req.status != 200) {
	    throw 'Communication error: Server responded with: '+req.status+' '+req.statusText;
	}
	return true;
    }

    this.xmlSerialize=function(node) {
	if (typeof XMLSerializer != "undefined") {
	    return (new XMLSerializer()).serializeToString(node);
	} else if (node.xml) {
	    return node.xml;
	} else {
	    throw "XML.serialize is not supported or can't serialize "+node;
	}
    }

    this.parseToDOM=function(data) {
	var dom;

        // Common mistakes
	// @ie must use regexp to replace it globally 
        data=data.replace(/&nbsp;/g, '&#160;');

	try {
		if (document.implementation.createDocument) { 
			var parser = new DOMParser();

			dom = parser.parseFromString(data, 'text/xml');

			if (dom.documentElement.tagName == 'parsererror') {
				throw ["TextEditor: Cannot parse XML to DOM!", dom];
			}

		} else if (window.ActiveXObject) { 
			// Internet Explorer, create a new XML document using ActiveX 
			// and use loadXML as a DOM parser. 
			dom = new ActiveXObject("Microsoft.XMLDOM");
			dom.async = "false"; 
			if (!dom.loadXML(data)) {
				throw "TextEditor: Cannot parse XML to DOM!";
			}
		}
	} catch(e) {
		return;
	}

	return dom;
    }

    this.htmlEscape=function(text) {
	/**
	 *  The translations performed are:
	 * '&' (ampersand) becomes '&amp;'
	 * '"' (double quote) becomes '&quot;' when ENT_NOQUOTES is not set.
	 * ''' (single quote) becomes '&#039;' only when ENT_QUOTES is set.
	 * '<' (less than) becomes '&lt;'
	 * '>' (greater than) becomes '&gt;'
	 */
        // @ie does not support String.replace(SEARCH, REPLACE, 'g') - flag 'g' - one must use regexp!
	return ('' + text).replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#039;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
        // Unreliable - does not work in MSIE - it looses some of the new lines
	//var div=document.createElement('div');
	//div.appendChild(document.createTextNode(text));
	//return div.innerHTML;
    }

    this.serialize=function(obj, name, noNS) {
	var commonAttrs='name="'+sysCommon.htmlEscape(typeof name == 'undefined' ? '' : name)+'" '+(noNS?'':'xmlns="http://www.1stomni.com/interchange"');
	var string='';

	switch (typeof obj) {
	case 'object':
        if (obj === null) {
			string+='<null/>';
	    } else if (obj.nodeType || (typeof Node == 'object' && obj instanceof Node)) {
			string+='<document '+commonAttrs+'>'+sysCommon.htmlEscape(sysCommon.xmlSerialize(obj))+'</document>';
	    } else if (obj instanceof Array) {
			string+='<list '+commonAttrs+'>';
			for (var i=0; i < obj.length; i++) {
				string+=sysCommon.serialize(obj[i], i, 1);
			}
			string+='</list>';
	    } else {
			string+='<array '+commonAttrs+'>';
			for (var key in obj) {
				string+=sysCommon.serialize(obj[key], key, 1);
			}
			string+='</array>';
	    }
	    break;
	case 'function':
	    string+='<function '+commonAttrs+' xml:space="preserve">'+sysCommon.htmlEscape(obj.__toString())+'</function>';
	case 'number':
	    string+='<number '+commonAttrs+' xml:space="preserve">'+sysCommon.htmlEscape(obj)+'</number>';
	    break;
	case 'string':
	    string+='<string '+commonAttrs+' xml:space="preserve">'+sysCommon.htmlEscape(obj)+'</string>';
	    break;
	case 'boolean':
	    string+='<boolean '+commonAttrs+' xml:space="preserve">'+(obj ? 1 : 0)+'</boolean>';	
	    break;
	}

	return string;
    }

    this.deserialize=function(node) {		
	var value;

	switch(node.tagName) {
		case 'null':
			value=null;
		break;
		case 'array':
			value={};
			for(var i=0; i < node.childNodes.length; i++) {
				if (node.childNodes[i].nodeType == 1) {
					value[node.childNodes[i].getAttribute('name')]=sysCommon.deserialize(node.childNodes[i]);
				}
			}
		break;
		case 'list':
			value=[];
			for(var i=0; i < node.childNodes.length; i++) {
				if (node.childNodes[i].nodeType == 1) {
					value.push(sysCommon.deserialize(node.childNodes[i]));
				}
			}
			break;
		case 'number':
			value=node.textContent || node.text;
		break;
		case 'string':
			value=node.textContent || node.text;
			if (typeof value == 'undefined') value = '';
		break;
		case 'boolean':
			value=((node.textContent || node.text) == '1');	    
		break;
		case 'document':
			value=sysCommon.parseToDOM(node.textContent || node.text);
		break;
		case 'exception':
			throw (node.textContent || node.text);
		break;
		case 'function':
		default:
			//window.console.log(node);
			value='***Javascript:sysCommon.deserialize():Not supported structure ('+node+') "'+node.tagName+'" ***';
		}
		return value;
    }

    this.ajaxFlag=function(state) {
	sysCommon.findAjaxFlag(false);
	var doIt=function() {sys.ajaxSemaphor.className=(sys.ajaxSemaphor.pending > 0 ? 'pending simultaneous'+sys.ajaxSemaphor.pending : 'finished');};

	if (!sys.ajaxSemaphor) return;
	if (state) {
	    sys.ajaxSemaphor.pending++;
	    doIt();
	} else {
	    sys.ajaxSemaphor.pending--;
	    if (sys.ajaxSemaphor.pending < 0) sys.ajaxSemaphor.pending=0;
	    setTimeout(doIt, 100); // Timeout to avoid flickering for many requests in a row
	}
    }

    this.findAjaxFlag=function(create) {
	if (sys.ajaxSemaphor) return;
	sys.ajaxSemaphor=document.getElementById('ajaxSemaphor');

	// Create artificial
	if (create && !sys.ajaxSemaphor) {
	    sys.ajaxSemaphor=document.body.appendChild(document.createElement('div', 'semaphor'));
	    sys.ajaxSemaphor.id='ajaxSemaphor';
	}
	
	if (sys.ajaxSemaphor) sys.ajaxSemaphor.pending=0;
    }

    this.addListener=function(eventName, fce, obj) {
	if (obj.attachEvent) {
	    return obj.attachEvent('on'+eventName, fce);
	} else {
	    return obj.addEventListener(eventName, fce, false);
	}
    }

    this.addReflowListener=function(fce, obj) {
	obj.sysCommonLastBounds=[];
	var checkReflow=function() {
	    if (obj.sysCommonLastBounds != sysCommon.getPosition(obj).join(',')) {
		obj.sysCommonLastBounds=sysCommon.getPosition(obj).join(',');
		fce(obj);
	    }
	    setTimeout(checkReflow, 100); // Using timeout instead of interval to avoid simultaneous slow checks
	}	
	//setInterval(checkReflow, 100); // -- may result in simultaneous checks for slow getPosition() method variant (safari)
	setTimeout(checkReflow, 0);	
    }

    this.cancelEvent=function(event) {
	if (!event) {
	    event=window.event;
	}
	event.preventDefault && event.preventDefault(); 
	event.returnValue=false;
    }

    this.ieHackSelectBoxes=function(hide) {
	if (!window.external || typeof window.XMLHttpRequest != "undefined") return; // Not IE6
	var lock=++window.ieHackSelectBoxesLock;
	
	for (formIdx=0; formIdx < document.forms.length; formIdx++) {
	    var theForm = document.forms[formIdx];
	    for (elementIdx=0; elementIdx < theForm.elements.length; elementIdx++) {
		window.status += theForm[elementIdx].type;
		if(theForm[elementIdx].type == "select-one") {
		    if (lock != window.ieHackSelectBoxesLock) return;
	    
		    if (hide && typeof theForm[elementIdx].visibilityBak == 'undefined') theForm[elementIdx].visibilityBak=theForm[elementIdx].style.visibility;
		    theForm[elementIdx].style.visibility=(!hide ? theForm[elementIdx].visibilityBak : "hidden");
		    if (!hide) theForm[elementIdx].visibilityBak=undefined;
		}
	    }
	}
    }

    this.getPosition=function (node) {
		if (typeof node.getBoundingClientRect == 'function') {
			var r1=node.getBoundingClientRect();
			var r2=document.body.getBoundingClientRect();	    
			return [r1.top - r2.top, r1.left - r2.left, r1.bottom - r2.top, r1.right - r2.left];
		}
	
		var leftPos=topPos=0;
		if (node.offsetParent) {
			leftPos=node.offsetLeft;
			topPos=node.offsetTop;
			while (node.offsetParent) {
				node=node.offsetParent;
				leftPos+=node.offsetLeft;
				topPos+=node.offsetTop;
			}
		}
		return [topPos, leftPos, topPos + node.clientHeight, leftPos + node.clientWidth];
    }

    this.pullCurtain=function(show, html) {
        if (typeof sysCommon.curtain == 'undefined') {
            sysCommon.curtainBlock=document.body.insertBefore(document.createElement('div'), document.body.firstChild);
            sysCommon.curtainBlock.setAttribute('class', 'systemCurtainBlock');
            sysCommon.curtainBlock.setAttribute('style', 'text-align: center; display: none; z-index: 1000; position: absolute; top: 0px; left: 0px;');
            sysCommon.curtain=document.body.insertBefore(document.createElement('div'), document.body.firstChild);
            sysCommon.curtain.setAttribute('class', 'systemCurtain');
            sysCommon.curtain.setAttribute('style', 'display: none; background-color: black; filter:alpha(opacity=70); opacity: 0.7; z-index: 1000; position: absolute; top: 0px; left: 0px; width: 100%; height: 100%;');
        }
        sysCommon.curtain.style.display=show ? 'block' : 'none';
        sysCommon.curtainBlock.style.display=show ? 'block' : 'none';
        
        if (show) {
            sysCommon.ieHackSelectBoxes(true);
            sysCommon.curtainBlock.innerHTML=html;
            sysCommon.centerElement(sysCommon.curtainBlock);
        } else {
            sysCommon.ieHackSelectBoxes(false);            
            sysCommon.curtainBlock.innserHTML='';
        }
    }
    
	// sysCommon.centerElement(myDiv, [0, 0, 1000, 10000]);
    this.centerElement=function(el, constraints) {
		el.style.display = 'block';                                                                                             
		el.style.position = "absolute";
			
		if (window.innerWidth) {
			var screenMaxW = window.innerWidth;
			var screenMaxH = window.innerHeight;
		} else if (document.documentElement && document.documentElement.clientWidth) {
			var screenMaxH = document.documentElement.clientHeight;
			var screenMaxW = document.documentElement.clientWidth;
		} else if (document.body) {                                                                                 	
			var screenMaxW = document.body.clientWidth;
			var screenMaxH = document.body.clientHeight;
		}
		var x = (screenMaxW - el.clientWidth)/2;
		var y = (screenMaxH - el.clientHeight)/2;

		// Check the constraints
		if (typeof constraints != 'object') {
			constraints=[-10000, -10000, 10000, 10000];
		}			
		if (x + el.clientWidth > constraints[3]) x=constraints[3] - el.clientWidth;
		if (x < constraints[1]) x=constraints[1];		
		if (y + el.clientHeight > constraints[2]) y=constraints[2] - el.clientHeight;
		if (y < constraints[0]) y=constraints[0];		

		if (document.all) {	
			iebody=(document.compatMode=="CSS1Compat")? document.documentElement : document.body;
			y = y + iebody.scrollTop;
		}

		el.style.left = x + 'px';
		el.style.top = y + 'px'; /*y + 'px';*/			
    }

    /**
     * 
     *
     * @access public
     * @param string name Cookie name
     * @param string value Cookie value
     * @param string expiration Optional. In DAYS(!) relative to the current time. FALSE for expiration at the end of user session (browser closed).
     * @param bool secure Optional. Default: false . If true then cookie can be transmitted only over secure connections.
     * @return string value
     */
    this.setCookie=function(name, value, expiration, secure) {
	var path='/';
	var domain=document.location.host.replace(/^www.?\./, '');

	var expirationDate;
	if (expiration) {
	    expirationDate=new Date();
	    expirationDate.setTime (expirationDate.getTime() + Math.round(expiration * 3600 * 24));
	} 
	document.cookie=name + "=" + escape(value) + ((expirationDate) ? "; expires=" + expirationDate.toGMTString() : "") + (path ? "; path=" + path : "") + ((domain) ? "; domain=" + domain : "") + ((secure) ? "; secure" : "");
    }

    /**
     * 
     *
     * @access public
     * @param string name 
     * @return mixed
     */
    this.getCookie=function(name) {
	var arg = name + "=";
	var alen = arg.length;
	var clen = document.cookie.length;
	var i = 0;
	while (i < clen) {
	    var j = i + alen;
	    if (document.cookie.substring(i, j) == arg) {
		var endstr = document.cookie.indexOf (";", j);
		if (endstr == -1)
		    endstr = document.cookie.length;
		return unescape(document.cookie.substring(j, endstr));
	    }
	    i = document.cookie.indexOf(" ", i) + 1;
	    if (i == 0) break;
	}
	return null;
    }

    /**
     * 
     *
     * @access public
     * @param string name of hte cookie
     * @return void
     */
    this.removeCookie=function(name) { 
	var path='/';
	var domain=document.location.host.replace(/^www.?\./, '');
	if (this.getCookie(name)) {
	    document.cookie='_' + name + "=" + ((path) ? "; path=" + path : "") + ((domain) ? "; domain=" + domain : "") + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
	}
    }


    /**
     * This method is workaround for form.submit() method that does not fire "onsubmit" event.
     * It simulates the normal "submit" event.
     *
     * Use it like this: sysCommon.submit(form);
     *
     * @access public
     * @param DOMElement form
     * @return void
     */
    this.submit=function(form) {
	if (document.createEvent) { // Standard
	    event = document.createEvent("HTMLEvents");
	    event.initEvent("submit", false, true);
	    form.dispatchEvent(event);
	} else { // IE
	    form.fireEvent("onsubmit") && form.submit();
	}
    };

    /**
     * This method is workaround for element.click() method.
     * It simulates the normal "click" event.
     *
     * Use it like this: sysCommon.click(element);
     *
     * @access public
     * @param DOMElement el
     * @return void
     */
    this.click=function(el) {
        if (document.createEvent) {
            var evt=document.createEvent('MouseEvents');
            var pos=sysCommon.getPosition(el);
            // event.initMouseEvent(type, canBubble, cancelable, view, 
            //          detail, screenX, screenY, clientX, clientY, 
            //          ctrlKey, altKey, shiftKey, metaKey, 
            //          button, relatedTarget);
            evt.initMouseEvent('click', true, true, document.defaultView, 1, pos[0], pos[1], pos[0], pos[1], false, false, false, false, 0, null);
            el.dispatchEvent(evt);
        } else {
            el.click();
        }
    }


    var b = navigator.userAgent.toLowerCase();

    /*
     * Figure out what browser is being used
     */
    this.browser = {
        safari: /webkit/.test(b),
        opera: /opera|presto/.test(b),
        msie: /msie/.test(b) && !/opera/.test(b),
        mozilla: /mozilla/.test(b) && !/(compatible|webkit)/.test(b)
    }



    /**
     * This method creates an INPUT element. It adds a workaround to the IE weird behaviour
     * when it provides @name and @type attributes as read-only
     *
     * @access public
     * @param DOMElement Parent element
     * @param string Input type (text,hidden,submit etc.)
     * @param string Input field name
     * @return DOMElement
     */
    this.createInputElement = function(parent, type, name) {

        if (this.browser.msie) {
            var e = document.createElement("<INPUT type="+type+" name='"+name+"'>");
        } else {
            var e = document.createElement('INPUT');
            e.type = type;
            e.name = name;
        }

        parent.appendChild(e);

        return e;
    }

}

var sysCommon=new SysCommon;
var sys=new Sys;

window.ieHackSelectBoxesLock=0;

/* Find Or Create Ajax Semaphor */
sysCommon.addListener('load', function() {sysCommon.findAjaxFlag(true);}, window);
