/**
 *
 * 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() {
    /**
     * 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
     * @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) {
	var onReady=function(req) {return typeof callBackAsync == 'function' && callBackAsync(sysCommon.deserialize(req.responseXML.documentElement));}

	var req=sysCommon.sendXMLHTTPRequest('POST', '/system/js/gate.php?'+event, callBackAsync, sysCommon.serialize(params), onReady);

	if (!callBackAsync) {
	    if (!req.responseXML) {
		throw new Exception('GATE: Response is not valid XML, check the mime-type of the response, it must be text/xml!');
	    }	 
	    if (!req.responseXML.documentElement) {
		throw new Exception('GATE: The response is not XML? Ready State: '+req.readyState+', Response Text: '+req.responseText);
	    }
	    return sysCommon.deserialize(req.responseXML && req.responseXML.documentElement);
	} else {
	    return false;
	}
    }

    /**
     * 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 new Exception("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 new Exception('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 new Exception("XML.serialize is not supported or can't serialize "+node);
	}
    }

    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;'
	 */
	return ('' + text).replace('&', '&amp;', 'g').replace('"', '&quot;', 'g').replace('\'', '&#039;', 'g').replace('<', '&lt;', 'g').replace('>', '&gt;', 'g');
        // 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) {
	var commonAttrs='name="'+sysCommon.htmlEscape(typeof name == 'undefined' ? '' : name)+'" xmlns="http://www.1stomni.com/interchange"';
	var string='';

	switch (typeof obj) {
	case 'object':
	    if (obj.nodeType || (typeof Node == 'object' && obj instanceof Node)) {
		string+='<document '+commonAttrs+'>'+sysCommon.htmlEscape(sysCommon.xmlSerialize(obj))+'</document>';
	    } else {
		string+='<array '+commonAttrs+'>';
		for (var key in obj) {
		    string+=sysCommon.serialize(obj[key], key);
		}
		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 '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 'number':
	    value=node.textContent || node.text;
	    break;
	case 'string':
	    value=node.textContent || node.text;
	    break;
	case 'boolean':
	    value=((node.textContent || node.text) == '1');	    
	    break;
	case 'document':
	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.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) {
	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];
    }
    
    this.centerElement=function(el) {
	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;

	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.
     * @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();
	}
    } 
}
var sysCommon=new SysCommon;
var sys=new Sys;

window.ieHackSelectBoxesLock=0;

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