Index: trunk/phpgwapi/inc/class.common.inc.php
===================================================================
--- trunk/phpgwapi/inc/class.common.inc.php	(revision 2419)
+++ trunk/phpgwapi/inc/class.common.inc.php	(revision 2420)
@@ -1224,7 +1224,7 @@
 			}
 
-			$GLOBALS['phpgw']->js->validate_file( 'tools','event_config' );
+			$GLOBALS['phpgw']->js->validate_file( 'tools','xevent' );
 			$GLOBALS['phpgw']->js->validate_file( 'tools','xconnector' );
-			//$GLOBALS['phpgw']->js->validate_file( 'tools','xlink' );
+			$GLOBALS['phpgw']->js->validate_file( 'tools','xlink' );
 
 			$GLOBALS['phpgw']->js->validate_file('expressoAjax','expressoAjax');
Index: trunk/phpgwapi/js/tools/xlink.js
===================================================================
--- trunk/phpgwapi/js/tools/xlink.js	(revision 2420)
+++ trunk/phpgwapi/js/tools/xlink.js	(revision 2420)
@@ -0,0 +1,47 @@
+( function( )
+{
+	var _connector = new XConnector;
+	function handler( pLink )
+	{
+		var action = pLink.href;
+		/*
+		var a = ( action.indexOf( 'javascript:' ) === 0 );
+		a = ( a || ( action.indexOf( '#' ) === 0 ) );
+		a = ( a || ( action.indexOf( window.location + '#' ) === 0 ) );
+		*/
+		var a = ( action.indexOf( 'admin/' ) > 0 || action.indexOf( '=admin.' ) > 0 );
+		a = ! ( a && ! ( action.indexOf( '#' ) === 0 || action.indexOf( window.location + '#' ) === 0 || action.indexOf( 'javascript:' ) === 0 ) );
+
+		if ( ! a )
+			pLink.onclick = function( )
+			{
+				var request = _connector.go( {
+					'access' : action,
+					'handler' : function( data )
+					{
+						//alert( action );
+						//alert( data );
+
+						var el = document.createElement( 'div' );
+						el.innerHTML = data;
+
+						var links = el.getElementsByTagName( 'a' );
+						for ( var i = 0; i < links.length; i++ )
+							handler( links.item( i ) );
+
+						document.getElementById( 'divAppbox' ).innerHTML = '';
+						while ( el.hasChildNodes( ) )
+							document.getElementById( 'divAppbox' ).appendChild( el.firstChild );
+					}
+				} );
+				return false;
+			};
+	}
+
+	XEvents.add( window, 'onload', function( )
+	{
+		var links = document.getElementsByTagName( 'a' );
+		for ( var i = 0; i < links.length; i++ )
+			handler( links.item( i ) );
+	} );
+} )( );
Index: trunk/phpgwapi/js/tools/xevent.js
===================================================================
--- trunk/phpgwapi/js/tools/xevent.js	(revision 2420)
+++ trunk/phpgwapi/js/tools/xevent.js	(revision 2420)
@@ -0,0 +1,45 @@
+( function( )
+{
+	function config( pObj, pEvent, pHandler )
+	{
+		if ( typeof pObj == 'object' )
+		{
+			if ( pEvent.substring(0, 2) == 'on' )
+				pEvent = pEvent.substring(2, pEvent.length );
+
+			if ( arguments.length == 3 )
+			{
+				if ( pObj.addEventListener )
+					pObj.addEventListener(pEvent, pHandler, false );
+				else if ( pObj.attachEvent )
+					pObj.attachEvent( 'on' + pEvent, pHandler );
+			}
+			else if ( arguments.length == 4 )
+			{
+				if ( pObj.removeEventListener )
+					pObj.removeEventListener( pEvent, pHandler, false );
+				else if ( pObj.detachEvent )
+					pObj.detachEvent( 'on' + pEvent, pHandler );
+			}
+		}
+	}
+
+	function add( pObj, pEvent, pHandler )
+	{
+		config( pObj, pEvent, pHandler );
+	}
+
+	function remove( pObj, pEvent, pHandler )
+	{
+		config( pObj, pEvent, pHandler, true );
+	}
+
+	function Events( )
+	{
+	}
+
+	Events.prototype.add = add;
+	Events.prototype.remove = remove;
+
+	window.XEvents = new Events;
+} )( );
Index: trunk/phpgwapi/js/tools/xconnector.js
===================================================================
--- trunk/phpgwapi/js/tools/xconnector.js	(revision 2420)
+++ trunk/phpgwapi/js/tools/xconnector.js	(revision 2420)
@@ -0,0 +1,321 @@
+(function()
+{
+	var _THREADS = [];
+	var _ie;
+
+	function _config(pObj, pEvent, pHandler)
+	{
+		if ( typeof pObj == 'object' )
+		{
+			if ( pEvent.substring(0, 2) == 'on' )
+				pEvent = pEvent.substring(2, pEvent.length);
+
+			if ( pObj.addEventListener )
+				pObj.addEventListener(pEvent, pHandler, false);
+			else if ( pObj.attachEvent )
+				pObj.attachEvent('on' + pEvent, pHandler);
+		}
+	}
+
+	// xhr = XMLHttpRequest
+	function _xhr()
+	{
+		try
+		{
+			return new XMLHttpRequest();
+		}
+		catch ( _e )
+		{
+			_ie = true;
+			try
+			{
+				return new ActiveXObject('Msxml2.XMLHTTP');
+			}
+			catch ( _e1 )
+			{
+				try
+				{
+					return new ActiveXObject('Microsoft.XMLHTTP');
+				}
+				catch ( _e2 )
+				{
+					return false;
+				}
+			}
+		}
+	}
+
+	function _HANDLER()
+	{
+		var _ID = arguments[0];
+
+		if  ( _THREADS[_ID] )
+		{
+			if ( _ie && _THREADS[_ID]._XHR.readyState != 4 )
+				return false;
+
+			switch ( _THREADS[_ID]._XHR.readyState )
+			{
+				case 3 :
+					if ( _THREADS[_ID]._HANDLER.stream )
+					{
+						var _data = _THREADS[_ID]._XHR.responseText.substr(_THREADS[_ID]._index).replace(/^ +| +$/g, '');
+						//alert(_data);
+						_THREADS[_ID]._rtlen = _THREADS[_ID]._XHR.responseText.length;
+
+						if ( _THREADS[_ID]._index < _THREADS[_ID]._rtlen && _data.length )
+							try
+							{
+								_THREADS[_ID]._HANDLER.stream(_data);
+							}
+							catch(_e)
+							{
+								//alert("#stream\n\n" + _e + "\n\n" + _e.description);
+							}
+
+						if ( _THREADS[ _ID ] )
+							_THREADS[ _ID ]._index = _THREADS[ _ID ]._rtlen;
+					}
+				break;
+				case 4 :
+					try
+					{
+						switch ( _THREADS[ _ID ]._XHR.status )
+						{
+							case 200:
+								var _data = ( _THREADS[ _ID ]._MODE == 'XML' ) ?
+									_THREADS[ _ID ]._XHR.responseXML :
+									_THREADS[ _ID ]._XHR.responseText;
+
+								if ( _ie && _THREADS[ _ID ]._HANDLER.stream )
+									_THREADS[ _ID ]._HANDLER.stream(_data);
+
+								var _request = ( _THREADS[ _ID ]._HANDLER.request ) ?
+									_THREADS[ _ID ]._HANDLER.request : false;
+
+								if ( _THREADS[ _ID ]._MODE == 'XML' && _data.documentElement && _data.documentElement.hasAttribute( 'ControllerKey' ) )
+								{
+									this._CONTROLLER_KEY = _data.documentElement.getAttribute( 'ControllerKey' );
+									_data.documentElement.removeAttribute( 'ControllerKey' );
+								}
+
+								delete _THREADS[ _ID ];
+
+								if ( _request )
+									try
+									{
+										_request( _data );
+									}
+									catch( e )
+									{
+										//alert("#request\n\n" + _e + "\n\n" + _e.description);
+									}
+
+							break; // [case : status 200]
+							case 404:
+								delete _THREADS[ _ID ];
+								alert( 'Page Not Found!' );
+							break; // [case : status 404]
+							default:
+								delete _THREADS[ _ID ];
+						}
+					}
+					catch( e )
+					{
+					}
+				break;
+				default :
+			}
+		}
+	}
+
+	function _execute( )
+	{
+		var _ID = arguments[0];
+
+		var _ACTION = 'act=' + _ID;
+		//var _TARGET = this._PATH + _THREADS[ _ID ]._ACCESS;
+		var _TARGET = _THREADS[ _ID ]._ACCESS;
+
+		var _SEND = null;
+
+		//_TARGET += ( this._CONTROLLER ) ?
+		//	this._CONTROLLER  : 'controller.php';
+
+		//if ( _THREADS[_ID]._METHOD == 'GET' )
+		//	_TARGET += '?' + _ACTION;
+
+		//_TARGET += _ID;
+
+		_THREADS[ _ID ]._XHR.open( ( ( _THREADS[ _ID ]._POST ) ? 'POST' : 'GET' ), _TARGET, true);
+
+		if ( _THREADS[ _ID ]._POST )
+		{
+			_THREADS[ _ID ]._XHR.setRequestHeader( 'Content-Type','application/x-www-form-urlencoded' );
+			_THREADS[ _ID ]._XHR.setRequestHeader( 'Cache-Control',  'no-store, no-cache, must-revalidate' );
+			_THREADS[ _ID ]._XHR.setRequestHeader( 'Cache-Control', 'post-check=0, pre-check=0' );
+			_THREADS[ _ID ]._XHR.setRequestHeader( 'Pragma', 'no-cache' );
+
+			_SEND = _THREADS[ _ID ]._POST;
+
+			if ( this._CONTROLLER_KEY && typeof _SEND.documentElement == 'object' )
+				_SEND.documentElement.setAttribute( 'ControllerKey', this._CONTROLLER_KEY );
+
+		}
+
+		_THREADS[ _ID ]._XHR.setRequestHeader( 'BackgroundRequest', Date.parse( new Date ) );
+
+		var _this = this;
+		_THREADS[ _ID ]._XHR.onreadystatechange = function( ){ _HANDLER.call( _this, _ID ); };
+		_THREADS[ _ID ]._XHR.send( _SEND );
+	}
+
+	function usage( )
+	{
+		return ""+
+			"Description:\n"+
+			"\t<obj>.go({string access, [mixed handler[, XMLObject post]]})\n\n"+
+			"Parameters:\n"+
+			"\taccess : assinatura de acesso Ã  camada de controle.\n"+
+			"\thandler : uma funÃ§Ã£o a ser executada no fim da requisiÃ§Ã£o\n"+
+			"\t\tou um objeto que poderÃ¡ conter dois Ã­ndices sendo\n"+
+			"\t\tque ambos deverÃ£o ser uma funÃ§Ã£o que serÃ¡ executada\n"+
+			"\t\tconforme o status do objeto xhr, sendo que na posiÃ§Ã£o\n"+
+			"\t\t'stream' serÃ¡ a funÃ§Ã£o a ser executada a cada iteraÃ§Ã£o\n"+
+			"\t\tdo objeto xhr e na posiÃ§Ã£o 'request' serÃ¡ a funÃ§Ã£o\n"+
+			"\t\ta ser executada no fim da requisiÃ§Ã£o.\n"+
+			"\tpost : se especificado deverÃ¡ ser uma query string ou um\n"+
+			"\tXML bem formatado.\n\n";
+	}
+
+	// @PARAM <string> access :
+	//		assinatura de acesso Ã  camada de controle
+	//
+	// @PARAM <object> handler :
+	//		OBS : neste caso a conexÃ£o assumirÃ¡ que se trata de uma stream
+	//		objeto contendo dois duas funÃ§Ãµes, sendo,
+	//		no Ã­ndice stream deverÃ¡ conter uma funÃ§Ã£o que serÃ¡ execultada
+	//		a cada mudanÃ§a de status do objeto xhr
+	//
+	// @PARAM <function> handler :
+	// 		funcÃ£o a ser executada no retorno da requisiÃ§Ã£o
+	//		OBS : neste caso a conexÃ£o assumirÃ¡ que se trata de uma
+	//		requisiÃ§Ã£o funÃ§Ã£o que serÃ¡ execultada no final da requisiÃ§Ã£o
+	//
+	// @PARAM <XMLObject> post:
+	//		este parÃ¢metro define se a conexÃ£o Ã© via GET ou POST
+	//		caso o parÃ¢metro nÃ£o esteja presente a conexÃ£o serÃ¡ execultada
+	//		via GET, por outro lado, caso ele exista deverÃ¡ ser uma query
+	//		string vÃ¡lida ou um xml bem formatado
+	//
+	function go( )
+	{
+		if ( arguments.length != 1 )
+			return { 'error' : "#0\n\n" + usage( ) };
+
+		var _argv = arguments[ 0 ];
+
+		if ( ( ! _argv.access ) || ( typeof _argv.access != 'string' ) || ( ! _argv.access.length ) )
+			return { 'error' : "#1\n\n" + usage( ) };
+
+		var _ID = _argv.access;
+
+		_THREADS[ _ID ] = {
+			'_ACCESS'	: _argv.access,
+			'_HANDLER'	: { },
+			'_POST'		: ( ( _argv.post ) ? _argv.post : false ),  // [GET | POST]
+			'_MODE'		: 'TEXT', // [XML | TEXT]
+			'_TYPE'		: null,	// [4 for request | 3 for stream]
+			'_XHR'		: null	// [4 for request | 3 for stream]
+		};
+
+		if ( _argv.mode && ( _argv.mode == 'XML' || _argv.mode == 'TEXT' ) )
+			_THREADS[ _ID ] = _argv.mode;
+
+		if ( _argv.handler )
+			switch ( typeof _argv.handler )
+			{
+				case 'function' :
+					_THREADS[ _ID ]._HANDLER = { 'request' : _argv.handler };
+					break;
+				case 'object' :
+					for ( var i in _argv.handler )
+						if ( i != 'stream' && i != 'request' )
+						{
+							delete _THREADS[ _ID ];
+							return { 'error' : "#2\n\n" + usage( ) };
+						}
+						else if ( i == 'stream' )
+						{
+							_THREADS[ _ID ]._index = 0;
+							_THREADS[ _ID ]._rtlen = 0;
+						}
+						_THREADS[ _ID ]._HANDLER = _argv.handler;
+					break;
+			}
+
+		if ( ! ( _THREADS[ _ID ]._XHR = _xhr( ) ) )
+		{
+			delete _THREADS[_ID];
+			return { 'error' : "#3 it cannot make a xhr object" };
+		}
+
+		_execute.call(this, _ID);
+
+		return { 'success' : "your thread is running and the response will be manipulated by the handler" };
+	}
+
+	function _abort()
+	{
+		for ( var _ID in _THREADS )
+		{
+			// @TODO
+			// try/catch for unknown error of IE.
+			// Check, store and retrieve the try/catch.
+			try
+			{
+				if ( _THREADS[_ID] && _THREADS[_ID]._XHR && _THREADS[_ID]._XHR.abort )
+					_THREADS[_ID]._XHR.abort();
+
+				delete _THREADS[_ID];
+			}
+			catch(e){}
+		}
+	}
+
+	function Connector( )
+	{
+		var _argv = arguments;
+
+		if ( ( _argv.length == 1 ) && ( typeof _argv[0] == 'object' ) )
+		{
+			_argv = _argv[ 0 ];
+
+			this._PATH = '';
+
+			if ( _argv.path && ( typeof _argv.path == 'string' ) && this._PATH.length )
+			{
+				if ( _argv.path.indexOf('/') != 0 )
+					_argv.path = '/' + _argv.path;
+
+				if ( _argv.path.lastIndexOf('/') != ( _argv.path.length - 1) )
+					_argv.path += '/';
+
+				this._PATH = _argv.path;
+			}
+
+			this._CONTROLLER = ( _argv.controller ) ?
+				_argv.controller : false;
+
+			this._CONTROLLER_KEY = ( _argv.key ) ?
+				_argv.key : false;
+		}
+	}
+
+	Connector.prototype.go = go;
+	Connector.prototype.abort = _abort;
+	window.XConnector = Connector;
+
+	_config( window, 'onbeforeunload', _abort );
+}
+)();
