diff --git a/docs/source/simplesamlphp-modules.xml b/docs/source/simplesamlphp-modules.xml
index 2cf4b527ed94a2951fce8f7b40a7ee4bad990a25..2b08d8d9bb4f925c32eed8ea2d0c2a898147be10 100644
--- a/docs/source/simplesamlphp-modules.xml
+++ b/docs/source/simplesamlphp-modules.xml
@@ -7,7 +7,7 @@
   <articleinfo>
     <date>2008-08-21</date>
 
-    <pubdate>Thu Aug 21 10:32:42 2008</pubdate>
+    <pubdate>Thu Aug 21 10:45:26 2008</pubdate>
 
     <author>
       <firstname>Olav</firstname>
diff --git a/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php b/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php
index 06065913da1fd5f67d4c11dafbf4430816b7b41d..009b55d192092fb2c046973f7d106f3cf9e37af4 100644
--- a/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php
+++ b/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php
@@ -177,7 +177,7 @@ class SimpleSAML_Bindings_SAML20_HTTPRedirect {
 		return $redirectURL;
 	
 	}
-	
+	# $request, $localentityid, $remoteentityid, $relayState = null, $endpoint = 'SingleSignOnService', $direction = 'SAMLRequest', $mode = 'SP'
 	public function sendMessage($request, $localentityid, $remoteentityid, $relayState = null, $endpoint = 'SingleSignOnService', $direction = 'SAMLRequest', $mode = 'SP') {
 		
 		SimpleSAML_Utilities::validateXMLDocument($request, 'saml20');
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
index a866801084bd0fc231e7256e22e2dfc4942d6104..be7d1be84cd3157c317dd01464e38c4bac4a277b 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
@@ -87,7 +87,7 @@ class SimpleSAML_Metadata_MetaDataStorageHandler {
 	 * @param $set  The set we the property comes from.
 	 * @return The autogenerated metadata property.
 	 */
-	public function getGenerated($property, $set = 'saml20-sp-hosted') {
+	public function getGenerated($property, $set = 'saml20-sp-hosted', $options = array() ) {
 
 		/* First we check if the user has overridden this property in the metadata. */
 		try {
@@ -115,12 +115,38 @@ class SimpleSAML_Metadata_MetaDataStorageHandler {
 					return $baseurl . 'saml2/sp/SingleLogoutService.php';					
 			}
 		} elseif($set == 'saml20-idp-hosted') {
+			
+			$logouttype = 'traditional';
+			if (array_key_exists('logouttype', $options)) $logouttype = $options['logouttype'];
+			if (!in_array($logouttype, array('traditional', 'iframe'))) 
+				throw new Exception('Invalid logout type [' . $logouttype . '] in IdP Hosted Metadata');
+
 			switch ($property) {				
 				case 'SingleSignOnService' : 
 					return $baseurl . 'saml2/idp/SSOService.php';
 
 				case 'SingleLogoutService' : 
-					return $baseurl . 'saml2/idp/SingleLogoutService.php';					
+					
+					switch ($logouttype) {
+						case 'iframe' : 
+							return $baseurl . 'saml2/idp/SingleLogoutServiceiFrame.php';
+						
+						case 'traditional' :
+						default :
+							return $baseurl . 'saml2/idp/SingleLogoutService.php';			
+					}
+				
+				case 'SingleLogoutServiceResponse' : 
+
+					switch ($logouttype) {
+						case 'iframe' : 
+							return $baseurl . 'saml2/idp/SingleLogoutServiceiFrameResponse.php';
+						
+						case 'traditional' :
+						default :
+							return $baseurl . 'saml2/idp/SingleLogoutService.php';			
+					}
+				
 			}
 		} elseif($set == 'shib13-sp-hosted') {
 			switch ($property) {				
diff --git a/libextinc/xajax/copyright.inc.php b/libextinc/xajax/copyright.inc.php
new file mode 100755
index 0000000000000000000000000000000000000000..ce24adff6bc34f564f6afb03b41aeedd723f5e8c
--- /dev/null
+++ b/libextinc/xajax/copyright.inc.php
@@ -0,0 +1,54 @@
+<?php
+/*
+	File: copyright.inc.php
+	
+	This file contains detailed information regarding the xajax project, 
+	current version, copyrights, licnese and documentation.
+	
+	You do not need to include this file in your project for xajax to 
+	function properly.
+*/
+
+/*
+	Section: Current Version
+	
+	xajax version 0.5 (beta 4)
+*/
+
+/*
+	Section: Copyright
+	
+	- copyright (c) 2005-2007 by Jared White & J. Max Wilson
+	- portions copyright (c) 2006-2007 by Joseph Woolley
+*/
+
+/*
+	Section: Description
+	
+	xajax is an open source PHP class library for easily creating powerful
+	PHP-driven, web-based Ajax Applications. Using xajax, you can asynchronously
+	call PHP functions and update the content of your your webpage without
+	reloading the page.
+*/
+
+/*
+	Section: License
+	
+	xajax is released under the terms of the BSD license
+	http://www.xajaxproject.org/bsd_license.txt
+*/
+
+/*
+	@package xajax
+	@version $Id: copyright.inc.php 327 2007-02-28 16:55:26Z calltoconstruct $
+	@copyright Copyright (c) 2005-2006 by Jared White & J. Max Wilson
+	@license http://www.xajaxproject.org/bsd_license.txt BSD License
+*/
+
+/*
+	Section: Online documentation
+	
+	Online documentation for this class is available on the xajax wiki at:
+	http://wiki.xajaxproject.org/Documentation:xajax.inc.php
+*/
+	
diff --git a/libextinc/xajax/xajax.inc.php b/libextinc/xajax/xajax.inc.php
new file mode 100644
index 0000000000000000000000000000000000000000..602a049302b4921f5fee17a7898a8d4e620eaeeb
--- /dev/null
+++ b/libextinc/xajax/xajax.inc.php
@@ -0,0 +1,1239 @@
+<?php
+/**
+ * xajax.inc.php :: Main xajax class and setup file
+ *
+ * xajax version 0.2.4
+ * copyright (c) 2005 by Jared White & J. Max Wilson
+ * http://www.xajaxproject.org
+ *
+ * xajax is an open source PHP class library for easily creating powerful
+ * PHP-driven, web-based Ajax Applications. Using xajax, you can asynchronously
+ * call PHP functions and update the content of your your webpage without
+ * reloading the page.
+ *
+ * xajax is released under the terms of the LGPL license
+ * http://www.gnu.org/copyleft/lesser.html#SEC3
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * @package xajax
+ * @version $Id$
+ * @copyright Copyright (c) 2005-2006  by Jared White & J. Max Wilson
+ * @license http://www.gnu.org/copyleft/lesser.html#SEC3 LGPL License
+ */
+
+/*
+   ----------------------------------------------------------------------------
+   | Online documentation for this class is available on the xajax wiki at:   |
+   | http://wiki.xajaxproject.org/Documentation:xajax.inc.php                 |
+   ----------------------------------------------------------------------------
+*/
+
+/**
+ * Define XAJAX_DEFAULT_CHAR_ENCODING that is used by both
+ * the xajax and xajaxResponse classes
+ */
+if (!defined ('XAJAX_DEFAULT_CHAR_ENCODING'))
+{
+	define ('XAJAX_DEFAULT_CHAR_ENCODING', 'utf-8' );
+}
+
+require_once(dirname(__FILE__)."/xajaxResponse.inc.php");
+
+/**
+ * Communication Method Defines
+ */
+if (!defined ('XAJAX_GET'))
+{
+	define ('XAJAX_GET', 0);
+}
+if (!defined ('XAJAX_POST'))
+{
+	define ('XAJAX_POST', 1);
+}
+
+/**
+ * The xajax class generates the xajax javascript for your page including the 
+ * Javascript wrappers for the PHP functions that you want to call from your page.
+ * It also handles processing and executing the command messages in the XML responses
+ * sent back to your page from your PHP functions.
+ * 
+ * @package xajax
+ */ 
+class xajax
+{
+	/**#@+
+	 * @access protected
+	 */
+	/**
+	 * @var array Array of PHP functions that will be callable through javascript wrappers
+	 */
+	var $aFunctions;
+	/**
+	 * @var array Array of object callbacks that will allow Javascript to call PHP methods (key=function name)
+	 */
+	var $aObjects;
+	/**
+	 * @var array Array of RequestTypes to be used with each function (key=function name)
+	 */
+	var $aFunctionRequestTypes;
+	/**
+	 * @var array Array of Include Files for any external functions (key=function name)
+	 */
+	var $aFunctionIncludeFiles;
+	/**
+	 * @var string Name of the PHP function to call if no callable function was found
+	 */
+	var $sCatchAllFunction;
+	/**
+	 * @var string Name of the PHP function to call before any other function
+	 */
+	var $sPreFunction;
+	/**
+	 * @var string The URI for making requests to the xajax object
+	 */
+	var $sRequestURI;
+	/**
+	 * @var string The prefix to prepend to the javascript wraper function name
+	 */
+	var $sWrapperPrefix;
+	/**
+	 * @var boolean Show debug messages (default false)
+	 */
+	var $bDebug;
+	/**
+	 * @var boolean Show messages in the client browser's status bar (default false)
+	 */
+	var $bStatusMessages;	
+	/**
+	 * @var boolean Allow xajax to exit after processing a request (default true)
+	 */
+	var $bExitAllowed;
+	/**
+	 * @var boolean Use wait cursor in browser (default true)
+	 */
+	var $bWaitCursor;
+	/**
+	 * @var boolean Use an special xajax error handler so the errors are sent to the browser properly (default false)
+	 */
+	var $bErrorHandler;
+	/**
+	 * @var string Specify what, if any, file xajax should log errors to (and more information in a future release)
+	 */
+	var $sLogFile;
+	/**
+	 * @var boolean Clean all output buffers before outputting response (default false)
+	 */
+	var $bCleanBuffer;
+	/**
+	 * @var string String containing the character encoding used
+	 */
+	var $sEncoding;
+	/**
+	 * @var boolean Decode input request args from UTF-8 (default false)
+	 */
+	var $bDecodeUTF8Input;
+	/**
+	 * @var boolean Convert special characters to HTML entities (default false)
+	 */
+	var $bOutputEntities;
+	/**
+	 * @var array Array for parsing complex objects
+	 */
+	var $aObjArray;
+	/**
+	 * @var integer Position in $aObjArray
+	 */
+	var $iPos;
+	
+	/**#@-*/
+	
+	/**
+	 * Constructor. You can set some extra xajax options right away or use
+	 * individual methods later to set options.
+	 * 
+	 * @param string  defaults to the current browser URI
+	 * @param string  defaults to "xajax_";
+	 * @param string  defaults to XAJAX_DEFAULT_CHAR_ENCODING defined above
+	 * @param boolean defaults to false
+	 */
+	function xajax($sRequestURI="",$sWrapperPrefix="xajax_",$sEncoding=XAJAX_DEFAULT_CHAR_ENCODING,$bDebug=false)
+	{
+		$this->aFunctions = array();
+		$this->aObjects = array();
+		$this->aFunctionIncludeFiles = array();
+		$this->sRequestURI = $sRequestURI;
+		if ($this->sRequestURI == "")
+			$this->sRequestURI = $this->_detectURI();
+		$this->sWrapperPrefix = $sWrapperPrefix;
+		$this->bDebug = $bDebug;
+		$this->bStatusMessages = false;
+		$this->bWaitCursor = true;
+		$this->bExitAllowed = true;
+		$this->bErrorHandler = false;
+		$this->sLogFile = "";
+		$this->bCleanBuffer = false;
+		$this->setCharEncoding($sEncoding);
+		$this->bDecodeUTF8Input = false;
+		$this->bOutputEntities = false;
+	}
+		
+	/**
+	 * Sets the URI to which requests will be made.
+	 * <i>Usage:</i> <kbd>$xajax->setRequestURI("http://www.xajaxproject.org");</kbd>
+	 * 
+	 * @param string the URI (can be absolute or relative) of the PHP script
+	 *               that will be accessed when an xajax request occurs
+	 */
+	function setRequestURI($sRequestURI)
+	{
+		$this->sRequestURI = $sRequestURI;
+	}
+
+	/**
+	 * Sets the prefix that will be appended to the Javascript wrapper
+	 * functions (default is "xajax_").
+	 * 
+	 * @param string
+	 */ 
+	// 
+	function setWrapperPrefix($sPrefix)
+	{
+		$this->sWrapperPrefix = $sPrefix;
+	}
+	
+	/**
+	 * Enables debug messages for xajax.
+	 * */
+	function debugOn()
+	{
+		$this->bDebug = true;
+	}
+	
+	/**
+	 * Disables debug messages for xajax (default behavior).
+	 */
+	function debugOff()
+	{
+		$this->bDebug = false;
+	}
+		
+	/**
+	 * Enables messages in the browser's status bar for xajax.
+	 */
+	function statusMessagesOn()
+	{
+		$this->bStatusMessages = true;
+	}
+	
+	/**
+	 * Disables messages in the browser's status bar for xajax (default behavior).
+	 */
+	function statusMessagesOff()
+	{
+		$this->bStatusMessages = false;
+	}
+	
+	/**
+	 * Enables the wait cursor to be displayed in the browser (default behavior).
+	 */
+	function waitCursorOn()
+	{
+		$this->bWaitCursor = true;
+	}
+	
+	/**
+	 * Disables the wait cursor to be displayed in the browser.
+	 */
+	function waitCursorOff()
+	{
+		$this->bWaitCursor = false;
+	}	
+	
+	/**
+	 * Enables xajax to exit immediately after processing a request and
+	 * sending the response back to the browser (default behavior).
+	 */
+	function exitAllowedOn()
+	{
+		$this->bExitAllowed = true;
+	}
+	
+	/**
+	 * Disables xajax's default behavior of exiting immediately after
+	 * processing a request and sending the response back to the browser.
+	 */
+	function exitAllowedOff()
+	{
+		$this->bExitAllowed = false;
+	}
+	
+	/**
+	 * Turns on xajax's error handling system so that PHP errors that occur
+	 * during a request are trapped and pushed to the browser in the form of
+	 * a Javascript alert.
+	 */
+	function errorHandlerOn()
+	{
+		$this->bErrorHandler = true;
+	}
+
+	/**
+	 * Turns off xajax's error handling system (default behavior).
+	 */
+	function errorHandlerOff()
+	{
+		$this->bErrorHandler = false;
+	}
+	
+	/**
+	 * Specifies a log file that will be written to by xajax during a request
+	 * (used only by the error handling system at present). If you don't invoke
+	 * this method, or you pass in "", then no log file will be written to.
+	 * <i>Usage:</i> <kbd>$xajax->setLogFile("/xajax_logs/errors.log");</kbd>
+	 */
+	function setLogFile($sFilename)
+	{
+		$this->sLogFile = $sFilename;
+	}
+
+	/**
+	 * Causes xajax to clean out all output buffers before outputting a
+	 * response (default behavior).
+	 */
+	function cleanBufferOn()
+	{
+		$this->bCleanBuffer = true;
+	}
+	/**
+	 * Turns off xajax's output buffer cleaning.
+	 */
+	function cleanBufferOff()
+	{
+		$this->bCleanBuffer = false;
+	}
+	
+	/**
+	 * Sets the character encoding for the HTTP output based on
+	 * <kbd>$sEncoding</kbd>, which is a string containing the character
+	 * encoding to use. You don't need to use this method normally, since the
+	 * character encoding for the response gets set automatically based on the
+	 * <kbd>XAJAX_DEFAULT_CHAR_ENCODING</kbd> constant.
+	 * <i>Usage:</i> <kbd>$xajax->setCharEncoding("utf-8");</kbd>
+	 *
+	 * @param string the encoding type to use (utf-8, iso-8859-1, etc.)
+	 */
+	function setCharEncoding($sEncoding)
+	{
+		$this->sEncoding = $sEncoding;
+	}
+
+	/**
+	 * Causes xajax to decode the input request args from UTF-8 to the current
+	 * encoding if possible. Either the iconv or mb_string extension must be
+	 * present for optimal functionality.
+	 */
+	function decodeUTF8InputOn()
+	{
+		$this->bDecodeUTF8Input = true;
+	}
+
+	/**
+	 * Turns off decoding the input request args from UTF-8 (default behavior).
+	 */
+	function decodeUTF8InputOff()
+	{
+		$this->bDecodeUTF8Input = false;
+	}
+	
+	/**
+	 * Tells the response object to convert special characters to HTML entities
+	 * automatically (only works if the mb_string extension is available).
+	 */
+	function outputEntitiesOn()
+	{
+		$this->bOutputEntities = true;
+	}
+	
+	/**
+	 * Tells the response object to output special characters intact. (default
+	 * behavior).
+	 */
+	function outputEntitiesOff()
+	{
+		$this->bOutputEntities = false;
+	}
+				
+	/**
+	 * Registers a PHP function or method to be callable through xajax in your
+	 * Javascript. If you want to register a function, pass in the name of that
+	 * function. If you want to register a static class method, pass in an
+	 * array like so:
+	 * <kbd>array("myFunctionName", "myClass", "myMethod")</kbd>
+	 * For an object instance method, use an object variable for the second
+	 * array element (and in PHP 4 make sure you put an & before the variable
+	 * to pass the object by reference). Note: the function name is what you
+	 * call via Javascript, so it can be anything as long as it doesn't
+	 * conflict with any other registered function name.
+	 * 
+	 * <i>Usage:</i> <kbd>$xajax->registerFunction("myFunction");</kbd>
+	 * or: <kbd>$xajax->registerFunction(array("myFunctionName", &$myObject, "myMethod"));</kbd>
+	 * 
+	 * @param mixed  contains the function name or an object callback array
+	 * @param mixed  request type (XAJAX_GET/XAJAX_POST) that should be used 
+	 *               for this function.  Defaults to XAJAX_POST.
+	 */
+	function registerFunction($mFunction,$sRequestType=XAJAX_POST)
+	{
+		if (is_array($mFunction)) {
+			$this->aFunctions[$mFunction[0]] = 1;
+			$this->aFunctionRequestTypes[$mFunction[0]] = $sRequestType;
+			$this->aObjects[$mFunction[0]] = array_slice($mFunction, 1);
+		}	
+		else {
+			$this->aFunctions[$mFunction] = 1;
+			$this->aFunctionRequestTypes[$mFunction] = $sRequestType;
+		}
+	}
+	
+	/**
+	 * Registers a PHP function to be callable through xajax which is located
+	 * in some other file.  If the function is requested the external file will
+	 * be included to define the function before the function is called.
+	 * 
+	 * <i>Usage:</i> <kbd>$xajax->registerExternalFunction("myFunction","myFunction.inc.php",XAJAX_POST);</kbd>
+	 * 
+	 * @param string contains the function name or an object callback array
+	 *               ({@link xajax::registerFunction() see registerFunction} for
+	 *               more info on object callback arrays)
+	 * @param string contains the path and filename of the include file
+	 * @param mixed  the RequestType (XAJAX_GET/XAJAX_POST) that should be used 
+	 *		          for this function. Defaults to XAJAX_POST.
+	 */
+	function registerExternalFunction($mFunction,$sIncludeFile,$sRequestType=XAJAX_POST)
+	{
+		$this->registerFunction($mFunction, $sRequestType);
+		
+		if (is_array($mFunction)) {
+			$this->aFunctionIncludeFiles[$mFunction[0]] = $sIncludeFile;
+		}
+		else {
+			$this->aFunctionIncludeFiles[$mFunction] = $sIncludeFile;
+		}
+	}
+	
+	/**
+	 * Registers a PHP function to be called when xajax cannot find the
+	 * function being called via Javascript. Because this is technically
+	 * impossible when using "wrapped" functions, the catch-all feature is
+	 * only useful when you're directly using the xajax.call() Javascript
+	 * method. Use the catch-all feature when you want more dynamic ability to
+	 * intercept unknown calls and handle them in a custom way.
+	 * 
+	 * <i>Usage:</i> <kbd>$xajax->registerCatchAllFunction("myCatchAllFunction");</kbd>
+	 * 
+	 * @param string contains the function name or an object callback array
+	 *               ({@link xajax::registerFunction() see registerFunction} for
+	 *               more info on object callback arrays)
+	 */
+	function registerCatchAllFunction($mFunction)
+	{
+		if (is_array($mFunction)) {
+			$this->sCatchAllFunction = $mFunction[0];
+			$this->aObjects[$mFunction[0]] = array_slice($mFunction, 1);
+		}
+		else {
+			$this->sCatchAllFunction = $mFunction;
+		}
+	}
+	
+	/**
+	 * Registers a PHP function to be called before xajax calls the requested
+	 * function. xajax will automatically add the request function's response
+	 * to the pre-function's response to create a single response. Another
+	 * feature is the ability to return not just a response, but an array with
+	 * the first element being false (a boolean) and the second being the
+	 * response. In this case, the pre-function's response will be returned to
+	 * the browser without xajax calling the requested function.
+	 * 
+	 * <i>Usage:</i> <kbd>$xajax->registerPreFunction("myPreFunction");</kbd>
+	 * 
+	 * @param string contains the function name or an object callback array
+	 *               ({@link xajax::registerFunction() see registerFunction} for
+	 *               more info on object callback arrays)
+	 */
+	function registerPreFunction($mFunction)
+	{
+		if (is_array($mFunction)) {
+			$this->sPreFunction = $mFunction[0];
+			$this->aObjects[$mFunction[0]] = array_slice($mFunction, 1);
+		}
+		else {
+			$this->sPreFunction = $mFunction;
+		}
+	}
+	
+	/**
+	 * Returns true if xajax can process the request, false if otherwise.
+	 * You can use this to determine if xajax needs to process the request or
+	 * not.
+	 * 
+	 * @return boolean
+	 */ 
+	function canProcessRequests()
+	{
+		if ($this->getRequestMode() != -1) return true;
+		return false;
+	}
+	
+	/**
+	 * Returns the current request mode (XAJAX_GET or XAJAX_POST), or -1 if
+	 * there is none.
+	 * 
+	 * @return mixed
+	 */
+	function getRequestMode()
+	{
+		if (!empty($_GET["xajax"]))
+			return XAJAX_GET;
+		
+		if (!empty($_POST["xajax"]))
+			return XAJAX_POST;
+			
+		return -1;
+	}
+	
+	/**
+	 * This is the main communications engine of xajax. The engine handles all
+	 * incoming xajax requests, calls the apporiate PHP functions (or
+	 * class/object methods) and passes the XML responses back to the
+	 * Javascript response handler. If your RequestURI is the same as your Web
+	 * page then this function should be called before any headers or HTML has
+	 * been sent.
+	 */
+	function processRequests()
+	{	
+		
+		$requestMode = -1;
+		$sFunctionName = "";
+		$bFoundFunction = true;
+		$bFunctionIsCatchAll = false;
+		$sFunctionNameForSpecial = "";
+		$aArgs = array();
+		$sPreResponse = "";
+		$bEndRequest = false;
+		$sResponse = "";
+		
+		$requestMode = $this->getRequestMode();
+		if ($requestMode == -1) return;
+	
+		if ($requestMode == XAJAX_POST)
+		{
+			$sFunctionName = $_POST["xajax"];
+			
+			if (!empty($_POST["xajaxargs"])) 
+				$aArgs = $_POST["xajaxargs"];
+		}
+		else
+		{	
+			header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+			header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
+			header ("Cache-Control: no-cache, must-revalidate");
+			header ("Pragma: no-cache");
+			
+			$sFunctionName = $_GET["xajax"];
+			
+			if (!empty($_GET["xajaxargs"])) 
+				$aArgs = $_GET["xajaxargs"];
+		}
+		
+		// Use xajax error handler if necessary
+		if ($this->bErrorHandler) {
+			$GLOBALS['xajaxErrorHandlerText'] = "";
+			set_error_handler("xajaxErrorHandler");
+		}
+		
+		if ($this->sPreFunction) {
+			if (!$this->_isFunctionCallable($this->sPreFunction)) {
+				$bFoundFunction = false;
+				$objResponse = new xajaxResponse();
+				$objResponse->addAlert("Unknown Pre-Function ". $this->sPreFunction);
+				$sResponse = $objResponse->getXML();
+			}
+		}
+		//include any external dependencies associated with this function name
+		if (array_key_exists($sFunctionName,$this->aFunctionIncludeFiles))
+		{
+			ob_start();
+			include_once($this->aFunctionIncludeFiles[$sFunctionName]);
+			ob_end_clean();
+		}
+		
+		if ($bFoundFunction) {
+			$sFunctionNameForSpecial = $sFunctionName;
+			if (!array_key_exists($sFunctionName, $this->aFunctions))
+			{
+				if ($this->sCatchAllFunction) {
+					$sFunctionName = $this->sCatchAllFunction;
+					$bFunctionIsCatchAll = true;
+				}
+				else {
+					$bFoundFunction = false;
+					$objResponse = new xajaxResponse();
+					$objResponse->addAlert("Unknown Function $sFunctionName.");
+					$sResponse = $objResponse->getXML();
+				}
+			}
+			else if ($this->aFunctionRequestTypes[$sFunctionName] != $requestMode)
+			{
+				$bFoundFunction = false;
+				$objResponse = new xajaxResponse();
+				$objResponse->addAlert("Incorrect Request Type.");
+				$sResponse = $objResponse->getXML();
+			}
+		}
+		
+		if ($bFoundFunction)
+		{
+			for ($i = 0; $i < sizeof($aArgs); $i++)
+			{
+				// If magic quotes is on, then we need to strip the slashes from the args
+				if (get_magic_quotes_gpc() == 1 && is_string($aArgs[$i])) {
+				
+					$aArgs[$i] = stripslashes($aArgs[$i]);
+				}
+				if (stristr($aArgs[$i],"<xjxobj>") != false)
+				{
+					$aArgs[$i] = $this->_xmlToArray("xjxobj",$aArgs[$i]);	
+				}
+				else if (stristr($aArgs[$i],"<xjxquery>") != false)
+				{
+					$aArgs[$i] = $this->_xmlToArray("xjxquery",$aArgs[$i]);	
+				}
+				else if ($this->bDecodeUTF8Input)
+				{
+					$aArgs[$i] = $this->_decodeUTF8Data($aArgs[$i]);	
+				}
+			}
+
+			if ($this->sPreFunction) {
+				$mPreResponse = $this->_callFunction($this->sPreFunction, array($sFunctionNameForSpecial, $aArgs));
+				if (is_array($mPreResponse) && $mPreResponse[0] === false) {
+					$bEndRequest = true;
+					$sPreResponse = $mPreResponse[1];
+				}
+				else {
+					$sPreResponse = $mPreResponse;
+				}
+				if (is_a($sPreResponse, "xajaxResponse")) {
+					$sPreResponse = $sPreResponse->getXML();
+				}
+				if ($bEndRequest) $sResponse = $sPreResponse;
+			}
+			
+			if (!$bEndRequest) {
+				if (!$this->_isFunctionCallable($sFunctionName)) {
+					$objResponse = new xajaxResponse();
+					$objResponse->addAlert("The Registered Function $sFunctionName Could Not Be Found.");
+					$sResponse = $objResponse->getXML();
+				}
+				else {
+					if ($bFunctionIsCatchAll) {
+						$aArgs = array($sFunctionNameForSpecial, $aArgs);
+					}
+					$sResponse = $this->_callFunction($sFunctionName, $aArgs);
+				}
+				if (is_a($sResponse, "xajaxResponse")) {
+					$sResponse = $sResponse->getXML();
+				}
+				if (!is_string($sResponse) || strpos($sResponse, "<xjx>") === FALSE) {
+					$objResponse = new xajaxResponse();
+					$objResponse->addAlert("No XML Response Was Returned By Function $sFunctionName.");
+					$sResponse = $objResponse->getXML();
+				}
+				else if ($sPreResponse != "") {
+					$sNewResponse = new xajaxResponse($this->sEncoding, $this->bOutputEntities);
+					$sNewResponse->loadXML($sPreResponse);
+					$sNewResponse->loadXML($sResponse);
+					$sResponse = $sNewResponse->getXML();
+				}
+			}
+		}
+		
+		$sContentHeader = "Content-type: text/xml;";
+		if ($this->sEncoding && strlen(trim($this->sEncoding)) > 0)
+			$sContentHeader .= " charset=".$this->sEncoding;
+		header($sContentHeader);
+		if ($this->bErrorHandler && !empty( $GLOBALS['xajaxErrorHandlerText'] )) {
+			$sErrorResponse = new xajaxResponse();
+			$sErrorResponse->addAlert("** PHP Error Messages: **" . $GLOBALS['xajaxErrorHandlerText']);
+			if ($this->sLogFile) {
+				$fH = @fopen($this->sLogFile, "a");
+				if (!$fH) {
+					$sErrorResponse->addAlert("** Logging Error **\n\nxajax was unable to write to the error log file:\n" . $this->sLogFile);
+				}
+				else {
+					fwrite($fH, "** xajax Error Log - " . strftime("%b %e %Y %I:%M:%S %p") . " **" . $GLOBALS['xajaxErrorHandlerText'] . "\n\n\n");
+					fclose($fH);
+				}
+			}
+
+			$sErrorResponse->loadXML($sResponse);
+			$sResponse = $sErrorResponse->getXML();
+			
+		}
+		if ($this->bCleanBuffer) while (@ob_end_clean());
+		print $sResponse;
+		if ($this->bErrorHandler) restore_error_handler();
+		
+		if ($this->bExitAllowed)
+			exit();
+	}
+
+	/**			
+	 * Prints the xajax Javascript header and wrapper code into your page by
+	 * printing the output of the getJavascript() method. It should only be
+	 * called between the <pre><head> </head></pre> tags in your HTML page.
+	 * Remember, if you only want to obtain the result of this function, use
+	 * {@link xajax::getJavascript()} instead.
+	 * 
+	 * <i>Usage:</i>
+	 * <code>
+	 *  <head>
+	 *		...
+	 *		< ?php $xajax->printJavascript(); ? >
+	 * </code>
+	 * 
+	 * @param string the relative address of the folder where xajax has been
+	 *               installed. For instance, if your PHP file is
+	 *               "http://www.myserver.com/myfolder/mypage.php"
+	 *               and xajax was installed in
+	 *               "http://www.myserver.com/anotherfolder", then $sJsURI
+	 *               should be set to "../anotherfolder". Defaults to assuming
+	 *               xajax is in the same folder as your PHP file.
+	 * @param string the relative folder/file pair of the xajax Javascript
+	 *               engine located within the xajax installation folder.
+	 *               Defaults to xajax_js/xajax.js.
+	 */
+	function printJavascript($sJsURI="", $sJsFile=NULL)
+	{
+		print $this->getJavascript($sJsURI, $sJsFile);
+	}
+	
+	/**
+	 * Returns the xajax Javascript code that should be added to your HTML page
+	 * between the <kbd><head> </head></kbd> tags.
+	 * 
+	 * <i>Usage:</i>
+	 * <code>
+	 *  < ?php $xajaxJSHead = $xajax->getJavascript(); ? >
+	 *	<head>
+	 *		...
+	 *		< ?php echo $xajaxJSHead; ? >
+	 * </code>
+	 * 
+	 * @param string the relative address of the folder where xajax has been
+	 *               installed. For instance, if your PHP file is
+	 *               "http://www.myserver.com/myfolder/mypage.php"
+	 *               and xajax was installed in
+	 *               "http://www.myserver.com/anotherfolder", then $sJsURI
+	 *               should be set to "../anotherfolder". Defaults to assuming
+	 *               xajax is in the same folder as your PHP file.
+	 * @param string the relative folder/file pair of the xajax Javascript
+	 *               engine located within the xajax installation folder.
+	 *               Defaults to xajax_js/xajax.js.
+	 * @return string
+	 */
+	function getJavascript($sJsURI="", $sJsFile=NULL)
+	{	
+		$html = $this->getJavascriptConfig();
+		$html .= $this->getJavascriptInclude($sJsURI, $sJsFile);
+		
+		return $html;
+	}
+	
+	/**
+	 * Returns a string containing inline Javascript that sets up the xajax
+	 * runtime (typically called internally by xajax from get/printJavascript).
+	 * 
+	 * @return string
+	 */
+	function getJavascriptConfig()
+	{
+		$html  = "\t<script type=\"text/javascript\">\n";
+		$html .= "var xajaxRequestUri=\"".$this->sRequestURI."\";\n";
+		$html .= "var xajaxDebug=".($this->bDebug?"true":"false").";\n";
+		$html .= "var xajaxStatusMessages=".($this->bStatusMessages?"true":"false").";\n";
+		$html .= "var xajaxWaitCursor=".($this->bWaitCursor?"true":"false").";\n";
+		$html .= "var xajaxDefinedGet=".XAJAX_GET.";\n";
+		$html .= "var xajaxDefinedPost=".XAJAX_POST.";\n";
+		$html .= "var xajaxLoaded=false;\n";
+
+		foreach($this->aFunctions as $sFunction => $bExists) {
+			$html .= $this->_wrap($sFunction,$this->aFunctionRequestTypes[$sFunction]);
+		}
+
+		$html .= "\t</script>\n";
+		return $html;		
+	}
+	
+	/**
+	 * Returns a string containing a Javascript include of the xajax.js file
+	 * along with a check to see if the file loaded after six seconds
+	 * (typically called internally by xajax from get/printJavascript).
+	 * 
+	 * @param string the relative address of the folder where xajax has been
+	 *               installed. For instance, if your PHP file is
+	 *               "http://www.myserver.com/myfolder/mypage.php"
+	 *               and xajax was installed in
+	 *               "http://www.myserver.com/anotherfolder", then $sJsURI
+	 *               should be set to "../anotherfolder". Defaults to assuming
+	 *               xajax is in the same folder as your PHP file.
+	 * @param string the relative folder/file pair of the xajax Javascript
+	 *               engine located within the xajax installation folder.
+	 *               Defaults to xajax_js/xajax.js.
+	 * @return string
+	 */
+	function getJavascriptInclude($sJsURI="", $sJsFile=NULL)
+	{
+		if ($sJsFile == NULL) $sJsFile = "xajax_js/xajax.js";
+			
+		if ($sJsURI != "" && substr($sJsURI, -1) != "/") $sJsURI .= "/";
+		
+		$html = "\t<script type=\"text/javascript\" src=\"" . $sJsURI . $sJsFile . "\"></script>\n";	
+		$html .= "\t<script type=\"text/javascript\">\n";
+		$html .= "window.setTimeout(function () { if (!xajaxLoaded) { alert('Error: the xajax Javascript file could not be included. Perhaps the URL is incorrect?\\nURL: {$sJsURI}{$sJsFile}'); } }, 6000);\n";
+		$html .= "\t</script>\n";
+		return $html;
+	}
+
+	/**
+	 * This method can be used to create a new xajax.js file out of the
+	 * xajax_uncompressed.js file (which will only happen if xajax.js doesn't
+	 * already exist on the filesystem).
+	 * 
+	 * @param string an optional argument containing the full server file path
+	 *               of xajax.js.
+	 */
+	function autoCompressJavascript($sJsFullFilename=NULL)
+	{	
+		$sJsFile = "xajax_js/xajax.js";
+		
+		if ($sJsFullFilename) {
+			$realJsFile = $sJsFullFilename;
+		}
+		else {
+			$realPath = realpath(dirname(__FILE__));
+			$realJsFile = $realPath . "/". $sJsFile;
+		}
+
+		// Create a compressed file if necessary
+		if (!file_exists($realJsFile)) {
+			$srcFile = str_replace(".js", "_uncompressed.js", $realJsFile);
+			if (!file_exists($srcFile)) {
+				trigger_error("The xajax uncompressed Javascript file could not be found in the <b>" . dirname($realJsFile) . "</b> folder. Error ", E_USER_ERROR);	
+			}
+			require(dirname(__FILE__)."/xajaxCompress.php");
+			$javaScript = implode('', file($srcFile));
+			$compressedScript = xajaxCompressJavascript($javaScript);
+			$fH = @fopen($realJsFile, "w");
+			if (!$fH) {
+				trigger_error("The xajax compressed javascript file could not be written in the <b>" . dirname($realJsFile) . "</b> folder. Error ", E_USER_ERROR);
+			}
+			else {
+				fwrite($fH, $compressedScript);
+				fclose($fH);
+			}
+		}
+	}
+	
+	/**
+	 * Returns the current URL based upon the SERVER vars.
+	 * 
+	 * @access private
+	 * @return string
+	 */
+	function _detectURI() {
+		$aURL = array();
+
+		// Try to get the request URL
+		if (!empty($_SERVER['REQUEST_URI'])) {
+			$aURL = parse_url($_SERVER['REQUEST_URI']);
+		}
+
+		// Fill in the empty values
+		if (empty($aURL['scheme'])) {
+			if (!empty($_SERVER['HTTP_SCHEME'])) {
+				$aURL['scheme'] = $_SERVER['HTTP_SCHEME'];
+			} else {
+				$aURL['scheme'] = (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off') ? 'https' : 'http';
+			}
+		}
+
+		if (empty($aURL['host'])) {
+			if (!empty($_SERVER['HTTP_HOST'])) {
+				if (strpos($_SERVER['HTTP_HOST'], ':') > 0) {
+					list($aURL['host'], $aURL['port']) = explode(':', $_SERVER['HTTP_HOST']);
+				} else {
+					$aURL['host'] = $_SERVER['HTTP_HOST'];
+				}
+			} else if (!empty($_SERVER['SERVER_NAME'])) {
+				$aURL['host'] = $_SERVER['SERVER_NAME'];
+			} else {
+				print "xajax Error: xajax failed to automatically identify your Request URI.";
+				print "Please set the Request URI explicitly when you instantiate the xajax object.";
+				exit();
+			}
+		}
+
+		if (empty($aURL['port']) && !empty($_SERVER['SERVER_PORT'])) {
+			$aURL['port'] = $_SERVER['SERVER_PORT'];
+		}
+
+		if (empty($aURL['path'])) {
+			if (!empty($_SERVER['PATH_INFO'])) {
+				$sPath = parse_url($_SERVER['PATH_INFO']);
+			} else {
+				$sPath = parse_url($_SERVER['PHP_SELF']);
+			}
+			$aURL['path'] = $sPath['path'];
+			unset($sPath);
+		}
+
+		if (!empty($aURL['query'])) {
+			$aURL['query'] = '?'.$aURL['query'];
+		}
+
+		// Build the URL: Start with scheme, user and pass
+		$sURL = $aURL['scheme'].'://';
+		if (!empty($aURL['user'])) {
+			$sURL.= $aURL['user'];
+			if (!empty($aURL['pass'])) {
+				$sURL.= ':'.$aURL['pass'];
+			}
+			$sURL.= '@';
+		}
+
+		// Add the host
+		$sURL.= $aURL['host'];
+
+		// Add the port if needed
+		if (!empty($aURL['port']) && (($aURL['scheme'] == 'http' && $aURL['port'] != 80) || ($aURL['scheme'] == 'https' && $aURL['port'] != 443))) {
+			$sURL.= ':'.$aURL['port'];
+		}
+
+		// Add the path and the query string
+		$sURL.= $aURL['path'].@$aURL['query'];
+
+		// Clean up
+		unset($aURL);
+		return $sURL;
+	}
+	
+	/**
+	 * Returns true if the function name is associated with an object callback,
+	 * false if not.
+	 * 
+	 * @param string the name of the function
+	 * @access private
+	 * @return boolean
+	 */
+	function _isObjectCallback($sFunction)
+	{
+		if (array_key_exists($sFunction, $this->aObjects)) return true;
+		return false;
+	}
+	
+	/**
+	 * Returns true if the function or object callback can be called, false if
+	 * not.
+	 * 
+	 * @param string the name of the function
+	 * @access private
+	 * @return boolean
+	 */
+	function _isFunctionCallable($sFunction)
+	{
+		if ($this->_isObjectCallback($sFunction)) {
+			if (is_object($this->aObjects[$sFunction][0])) {
+				return method_exists($this->aObjects[$sFunction][0], $this->aObjects[$sFunction][1]);
+			}
+			else {
+				return is_callable($this->aObjects[$sFunction]);
+			}
+		}
+		else {
+			return function_exists($sFunction);
+		}	
+	}
+	
+	/**
+	 * Calls the function, class method, or object method with the supplied
+	 * arguments.
+	 * 
+	 * @param string the name of the function
+	 * @param array  arguments to pass to the function
+	 * @access private
+	 * @return mixed the output of the called function or method
+	 */
+	function _callFunction($sFunction, $aArgs)
+	{
+		if ($this->_isObjectCallback($sFunction)) {
+			$mReturn = call_user_func_array($this->aObjects[$sFunction], $aArgs);
+		}
+		else {
+			$mReturn = call_user_func_array($sFunction, $aArgs);
+		}
+		return $mReturn;
+	}
+	
+	/**
+	 * Generates the Javascript wrapper for the specified PHP function.
+	 * 
+	 * @param string the name of the function
+	 * @param mixed  the request type
+	 * @access private
+	 * @return string
+	 */
+	function _wrap($sFunction,$sRequestType=XAJAX_POST)
+	{
+		$js = "function ".$this->sWrapperPrefix."$sFunction(){return xajax.call(\"$sFunction\", arguments, ".$sRequestType.");}\n";		
+		return $js;
+	}
+
+	/**
+	 * Takes a string containing xajax xjxobj XML or xjxquery XML and builds an
+	 * array representation of it to pass as an argument to the PHP function
+	 * being called.
+	 * 
+	 * @param string the root tag of the XML
+	 * @param string XML to convert
+	 * @access private
+	 * @return array
+	 */
+	function _xmlToArray($rootTag, $sXml)
+	{
+		$aArray = array();
+		$sXml = str_replace("<$rootTag>","<$rootTag>|~|",$sXml);
+		$sXml = str_replace("</$rootTag>","</$rootTag>|~|",$sXml);
+		$sXml = str_replace("<e>","<e>|~|",$sXml);
+		$sXml = str_replace("</e>","</e>|~|",$sXml);
+		$sXml = str_replace("<k>","<k>|~|",$sXml);
+		$sXml = str_replace("</k>","|~|</k>|~|",$sXml);
+		$sXml = str_replace("<v>","<v>|~|",$sXml);
+		$sXml = str_replace("</v>","|~|</v>|~|",$sXml);
+		$sXml = str_replace("<q>","<q>|~|",$sXml);
+		$sXml = str_replace("</q>","|~|</q>|~|",$sXml);
+		
+		$this->aObjArray = explode("|~|",$sXml);
+		
+		$this->iPos = 0;
+		$aArray = $this->_parseObjXml($rootTag);
+        
+		return $aArray;
+	}
+	
+	/**
+	 * A recursive function that generates an array from the contents of
+	 * $this->aObjArray.
+	 * 
+	 * @param string the root tag of the XML
+	 * @access private
+	 * @return array
+	 */
+	function _parseObjXml($rootTag)
+	{
+		$aArray = array();
+		
+		if ($rootTag == "xjxobj")
+		{
+			while(!stristr($this->aObjArray[$this->iPos],"</xjxobj>"))
+			{
+				$this->iPos++;
+				if(stristr($this->aObjArray[$this->iPos],"<e>"))
+				{
+					$key = "";
+					$value = null;
+						
+					$this->iPos++;
+					while(!stristr($this->aObjArray[$this->iPos],"</e>"))
+					{
+						if(stristr($this->aObjArray[$this->iPos],"<k>"))
+						{
+							$this->iPos++;
+							while(!stristr($this->aObjArray[$this->iPos],"</k>"))
+							{
+								$key .= $this->aObjArray[$this->iPos];
+								$this->iPos++;
+							}
+						}
+						if(stristr($this->aObjArray[$this->iPos],"<v>"))
+						{
+							$this->iPos++;
+							while(!stristr($this->aObjArray[$this->iPos],"</v>"))
+							{
+								if(stristr($this->aObjArray[$this->iPos],"<xjxobj>"))
+								{
+									$value = $this->_parseObjXml("xjxobj");
+									$this->iPos++;
+								}
+								else
+								{
+									$value .= $this->aObjArray[$this->iPos];
+									if ($this->bDecodeUTF8Input)
+									{
+										$value = $this->_decodeUTF8Data($value);
+									}
+								}
+								$this->iPos++;
+							}
+						}
+						$this->iPos++;
+					}
+					
+					$aArray[$key]=$value;
+				}
+			}
+		}
+		
+		if ($rootTag == "xjxquery")
+		{
+			$sQuery = "";
+			$this->iPos++;
+			while(!stristr($this->aObjArray[$this->iPos],"</xjxquery>"))
+			{
+				if (stristr($this->aObjArray[$this->iPos],"<q>") || stristr($this->aObjArray[$this->iPos],"</q>"))
+				{
+					$this->iPos++;
+					continue;
+				}
+				$sQuery	.= $this->aObjArray[$this->iPos];
+				$this->iPos++;
+			}
+			
+			parse_str($sQuery, $aArray);
+			if ($this->bDecodeUTF8Input)
+			{
+				foreach($aArray as $key => $value)
+				{
+					$aArray[$key] = $this->_decodeUTF8Data($value);
+				}
+			}
+			// If magic quotes is on, then we need to strip the slashes from the
+			// array values because of the parse_str pass which adds slashes
+			if (get_magic_quotes_gpc() == 1) {
+				$newArray = array();
+				foreach ($aArray as $sKey => $sValue) {
+					if (is_string($sValue))
+						$newArray[$sKey] = stripslashes($sValue);
+					else
+						$newArray[$sKey] = $sValue;
+				}
+				$aArray = $newArray;
+			}
+		}
+		
+		return $aArray;
+	}
+	
+	/**
+	 * Decodes string data from UTF-8 to the current xajax encoding.
+	 * 
+	 * @param string data to convert
+	 * @access private
+	 * @return string converted data
+	 */
+	function _decodeUTF8Data($sData)
+	{
+		$sValue = $sData;
+		if ($this->bDecodeUTF8Input)
+		{
+			$sFuncToUse = NULL;
+			
+			if (function_exists('iconv'))
+			{
+				$sFuncToUse = "iconv";
+			}
+			else if (function_exists('mb_convert_encoding'))
+			{
+				$sFuncToUse = "mb_convert_encoding";
+			}
+			else if ($this->sEncoding == "ISO-8859-1")
+			{
+				$sFuncToUse = "utf8_decode";
+			}
+			else
+			{
+				trigger_error("The incoming xajax data could not be converted from UTF-8", E_USER_NOTICE);
+			}
+			
+			if ($sFuncToUse)
+			{
+				if (is_string($sValue))
+				{
+					if ($sFuncToUse == "iconv")
+					{
+						$sValue = iconv("UTF-8", $this->sEncoding.'//TRANSLIT', $sValue);
+					}
+					else if ($sFuncToUse == "mb_convert_encoding")
+					{
+						$sValue = mb_convert_encoding($sValue, $this->sEncoding, "UTF-8");
+					}
+					else
+					{
+						$sValue = utf8_decode($sValue);
+					}
+				}
+			}
+		}
+		return $sValue;	
+	}
+		
+}// end class xajax 
+
+/**
+ * This function is registered with PHP's set_error_handler() function if
+ * the xajax error handling system is turned on.
+ */
+function xajaxErrorHandler($errno, $errstr, $errfile, $errline)
+{
+	$errorReporting = error_reporting();
+	if (($errno & $errorReporting) == 0) return;
+	
+	if ($errno == E_NOTICE) {
+		$errTypeStr = "NOTICE";
+	}
+	else if ($errno == E_WARNING) {
+		$errTypeStr = "WARNING";
+	}
+	else if ($errno == E_USER_NOTICE) {
+		$errTypeStr = "USER NOTICE";
+	}
+	else if ($errno == E_USER_WARNING) {
+		$errTypeStr = "USER WARNING";
+	}
+	else if ($errno == E_USER_ERROR) {
+		$errTypeStr = "USER FATAL ERROR";
+	}
+	else if ($errno == E_STRICT) {
+		return;
+	}
+	else {
+		$errTypeStr = "UNKNOWN: $errno";
+	}
+	$GLOBALS['xajaxErrorHandlerText'] .= "\n----\n[$errTypeStr] $errstr\nerror in line $errline of file $errfile";
+}
+
+?>
\ No newline at end of file
diff --git a/libextinc/xajax/xajaxCompress.php b/libextinc/xajax/xajaxCompress.php
new file mode 100644
index 0000000000000000000000000000000000000000..327a92dff541a0d5d5bcdd6a79059df16939c805
--- /dev/null
+++ b/libextinc/xajax/xajaxCompress.php
@@ -0,0 +1,182 @@
+<?php
+/**
+ * xajaxCompress.php :: function to compress Javascript
+ *
+ * xajax version 0.2.4
+ * copyright (c) 2005 by Jared White & J. Max Wilson
+ * http://www.xajaxproject.org
+ *
+ * xajax is an open source PHP class library for easily creating powerful
+ * PHP-driven, web-based Ajax Applications. Using xajax, you can asynchronously
+ * call PHP functions and update the content of your your webpage without
+ * reloading the page.
+ *
+ * xajax is released under the terms of the LGPL license
+ * http://www.gnu.org/copyleft/lesser.html#SEC3
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * @package xajax
+ * @version $Id$
+ * @copyright Copyright (c) 2005-2006  by Jared White & J. Max Wilson
+ * @license http://www.gnu.org/copyleft/lesser.html#SEC3 LGPL License
+ */
+
+/**
+ * Compresses the Javascript code for more efficient delivery.
+ * (used internally)
+ * 
+ * @param string contains the Javascript code to compress
+ */
+function xajaxCompressJavascript($sJS)
+{
+	//remove windows cariage returns
+	$sJS = str_replace("\r","",$sJS);
+	
+	//array to store replaced literal strings
+	$literal_strings = array();
+	
+	//explode the string into lines
+	$lines = explode("\n",$sJS);
+	//loop through all the lines, building a new string at the same time as removing literal strings
+	$clean = "";
+	$inComment = false;
+	$literal = "";
+	$inQuote = false;
+	$escaped = false;
+	$quoteChar = "";
+	
+	for($i=0;$i<count($lines);$i++)
+	{
+		$line = $lines[$i];
+		$inNormalComment = false;
+	
+		//loop through line's characters and take out any literal strings, replace them with ___i___ where i is the index of this string
+		for($j=0;$j<strlen($line);$j++)
+		{
+			$c = substr($line,$j,1);
+			$d = substr($line,$j,2);
+	
+			//look for start of quote
+			if(!$inQuote && !$inComment)
+			{
+				//is this character a quote or a comment
+				if(($c=="\"" || $c=="'") && !$inComment && !$inNormalComment)
+				{
+					$inQuote = true;
+					$inComment = false;
+					$escaped = false;
+					$quoteChar = $c;
+					$literal = $c;
+				}
+				else if($d=="/*" && !$inNormalComment)
+				{
+					$inQuote = false;
+					$inComment = true;
+					$escaped = false;
+					$quoteChar = $d;
+					$literal = $d;	
+					$j++;	
+				}
+				else if($d=="//") //ignore string markers that are found inside comments
+				{
+					$inNormalComment = true;
+					$clean .= $c;
+				}
+				else
+				{
+					$clean .= $c;
+				}
+			}
+			else //allready in a string so find end quote
+			{
+				if($c == $quoteChar && !$escaped && !$inComment)
+				{
+					$inQuote = false;
+					$literal .= $c;
+	
+					//subsitute in a marker for the string
+					$clean .= "___" . count($literal_strings) . "___";
+	
+					//push the string onto our array
+					array_push($literal_strings,$literal);
+	
+				}
+				else if($inComment && $d=="*/")
+				{
+					$inComment = false;
+					$literal .= $d;
+	
+					//subsitute in a marker for the string
+					$clean .= "___" . count($literal_strings) . "___";
+	
+					//push the string onto our array
+					array_push($literal_strings,$literal);
+	
+					$j++;
+				}
+				else if($c == "\\" && !$escaped)
+					$escaped = true;
+				else
+					$escaped = false;
+	
+				$literal .= $c;
+			}
+		}
+		if($inComment) $literal .= "\n";
+		$clean .= "\n";
+	}
+	//explode the clean string into lines again
+	$lines = explode("\n",$clean);
+	
+	//now process each line at a time
+	for($i=0;$i<count($lines);$i++)
+	{
+		$line = $lines[$i];
+	
+		//remove comments
+		$line = preg_replace("/\/\/(.*)/","",$line);
+	
+		//strip leading and trailing whitespace
+		$line = trim($line);
+	
+		//remove all whitespace with a single space
+		$line = preg_replace("/\s+/"," ",$line);
+	
+		//remove any whitespace that occurs after/before an operator
+		$line = preg_replace("/\s*([!\}\{;,&=\|\-\+\*\/\)\(:])\s*/","\\1",$line);
+	
+		$lines[$i] = $line;
+	}
+	
+	//implode the lines
+	$sJS = implode("\n",$lines);
+	
+	//make sure there is a max of 1 \n after each line
+	$sJS = preg_replace("/[\n]+/","\n",$sJS);
+	
+	//strip out line breaks that immediately follow a semi-colon
+	$sJS = preg_replace("/;\n/",";",$sJS);
+	
+	//curly brackets aren't on their own
+	$sJS = preg_replace("/[\n]*\{[\n]*/","{",$sJS);
+	
+	//finally loop through and replace all the literal strings:
+	for($i=0;$i<count($literal_strings);$i++)
+		$sJS = str_replace("___".$i."___",$literal_strings[$i],$sJS);
+	
+	return $sJS;
+}
+?>
\ No newline at end of file
diff --git a/libextinc/xajax/xajaxResponse.inc.php b/libextinc/xajax/xajaxResponse.inc.php
new file mode 100644
index 0000000000000000000000000000000000000000..1c0aeb6022afae1c910365dbd463f290f9365276
--- /dev/null
+++ b/libextinc/xajax/xajaxResponse.inc.php
@@ -0,0 +1,580 @@
+<?php
+/**
+ * xajaxResponse.inc.php :: xajax XML response class
+ *
+ * xajax version 0.2.4
+ * copyright (c) 2005 by Jared White & J. Max Wilson
+ * http://www.xajaxproject.org
+ *
+ * xajax is an open source PHP class library for easily creating powerful
+ * PHP-driven, web-based Ajax Applications. Using xajax, you can asynchronously
+ * call PHP functions and update the content of your your webpage without
+ * reloading the page.
+ *
+ * xajax is released under the terms of the LGPL license
+ * http://www.gnu.org/copyleft/lesser.html#SEC3
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * @package xajax
+ * @version $Id$
+ * @copyright Copyright (c) 2005-2006  by Jared White & J. Max Wilson
+ * @license http://www.gnu.org/copyleft/lesser.html#SEC3 LGPL License
+ */
+
+/*
+   ----------------------------------------------------------------------------
+   | Online documentation for this class is available on the xajax wiki at:   |
+   | http://wiki.xajaxproject.org/Documentation:xajaxResponse.inc.php         |
+   ----------------------------------------------------------------------------
+*/
+
+/**
+ * The xajaxResponse class is used to create responses to be sent back to your
+ * Web page.  A response contains one or more command messages for updating
+ * your page.
+ * Currently xajax supports 21 kinds of command messages, including some common
+ * ones such as:
+ * <ul>
+ * <li>Assign - sets the specified attribute of an element in your page</li>
+ * <li>Append - appends data to the end of the specified attribute of an
+ * element in your page</li>
+ * <li>Prepend - prepends data to the beginning of the specified attribute of
+ * an element in your page</li>
+ * <li>Replace - searches for and replaces data in the specified attribute of
+ * an element in your page</li>
+ * <li>Script - runs the supplied JavaScript code</li>
+ * <li>Alert - shows an alert box with the supplied message text</li>
+ * </ul>
+ *
+ * <i>Note:</i> elements are identified by their HTML id, so if you don't see
+ * your browser HTML display changing from the request, make sure you're using
+ * the right id names in your response.
+ * 
+ * @package xajax
+ */
+class xajaxResponse
+{
+	/**#@+
+	 * @access protected
+	 */
+	/**
+	 * @var string internal XML storage
+	 */	
+	var $xml;
+	/**
+	 * @var string the encoding type to use
+	 */
+	var $sEncoding;
+	/**
+	 * @var boolean if special characters in the XML should be converted to
+	 *              entities
+	 */
+	var $bOutputEntities;
+
+	/**#@-*/
+	
+	/**
+	 * The constructor's main job is to set the character encoding for the
+	 * response.
+	 * 
+	 * <i>Note:</i> to change the character encoding for all of the
+	 * responses, set the XAJAX_DEFAULT_ENCODING constant before you
+	 * instantiate xajax.
+	 * 
+	 * @param string  contains the character encoding string to use
+	 * @param boolean lets you set if you want special characters in the output
+	 *                converted to HTML entities
+	 * 
+	 */
+	function xajaxResponse($sEncoding=XAJAX_DEFAULT_CHAR_ENCODING, $bOutputEntities=false)
+	{
+		$this->setCharEncoding($sEncoding);
+		$this->bOutputEntities = $bOutputEntities;
+	}
+	
+	/**
+	 * Sets the character encoding for the response based on $sEncoding, which
+	 * is a string containing the character encoding to use. You don't need to
+	 * use this method normally, since the character encoding for the response
+	 * gets set automatically based on the XAJAX_DEFAULT_CHAR_ENCODING
+	 * constant.
+	 * 
+	 * @param string
+	 */
+	function setCharEncoding($sEncoding)
+	{
+		$this->sEncoding = $sEncoding;
+	}
+	
+	/**
+	 * Tells the response object to convert special characters to HTML entities
+	 * automatically (only works if the mb_string extension is available).
+	 */
+	function outputEntitiesOn()
+	{
+		$this->bOutputEntities = true;
+	}
+	
+	/**
+	 * Tells the response object to output special characters intact. (default
+	 * behavior)
+	 */
+	function outputEntitiesOff()
+	{
+		$this->bOutputEntities = false;
+	}
+
+	/**
+	 * Adds a confirm commands command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addConfirmCommands(1, "Do you want to preview the new data?");</kbd>
+	 *
+	 * @param integer the number of commands to skip if the user presses
+	 *                Cancel in the browsers's confirm dialog
+	 * @param string  the message to show in the browser's confirm dialog
+	 */
+	function addConfirmCommands($iCmdNumber, $sMessage)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"cc","t"=>$iCmdNumber),$sMessage);
+	}
+	
+	/**
+	 * Adds an assign command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addAssign("contentDiv", "innerHTML", "Some Text");</kbd>
+	 * 
+	 * @param string contains the id of an HTML element
+	 * @param string the part of the element you wish to modify ("innerHTML",
+	 *               "value", etc.)
+	 * @param string the data you want to set the attribute to
+	 */
+	function addAssign($sTarget,$sAttribute,$sData)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"as","t"=>$sTarget,"p"=>$sAttribute),$sData);
+	}
+
+	/**
+	 * Adds an append command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addAppend("contentDiv", "innerHTML", "Some New Text");</kbd>
+	 * 
+	 * @param string contains the id of an HTML element
+	 * @param string the part of the element you wish to modify ("innerHTML",
+	 *               "value", etc.)
+	 * @param string the data you want to append to the end of the attribute
+	 */
+	function addAppend($sTarget,$sAttribute,$sData)
+	{	
+		$this->xml .= $this->_cmdXML(array("n"=>"ap","t"=>$sTarget,"p"=>$sAttribute),$sData);
+	}
+
+	/**
+	 * Adds an prepend command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addPrepend("contentDiv", "innerHTML", "Some Starting Text");</kbd>
+	 * 
+	 * @param string contains the id of an HTML element
+	 * @param string the part of the element you wish to modify ("innerHTML",
+	 *               "value", etc.)
+	 * @param string the data you want to prepend to the beginning of the
+	 *               attribute
+	 */
+	function addPrepend($sTarget,$sAttribute,$sData)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"pp","t"=>$sTarget,"p"=>$sAttribute),$sData);
+	}
+
+	/**
+	 * Adds a replace command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addReplace("contentDiv", "innerHTML", "text", "<b>text</b>");</kbd>
+	 * 
+	 * @param string contains the id of an HTML element
+	 * @param string the part of the element you wish to modify ("innerHTML",
+	 *               "value", etc.)
+	 * @param string the string to search for
+	 * @param string the string to replace the search string when found in the
+	 *               attribute
+	 */
+	function addReplace($sTarget,$sAttribute,$sSearch,$sData)
+	{
+		$sDta = "<s><![CDATA[$sSearch]]></s><r><![CDATA[$sData]]></r>";
+		$this->xml .= $this->_cmdXML(array("n"=>"rp","t"=>$sTarget,"p"=>$sAttribute),$sDta);
+	}
+
+	/**
+	 * Adds a clear command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addClear("contentDiv", "innerHTML");</kbd>
+	 * 
+	 * @param string contains the id of an HTML element
+	 * @param string the part of the element you wish to clear ("innerHTML",
+	 *               "value", etc.)
+	 */	
+	function addClear($sTarget,$sAttribute)
+	{
+		$this->addAssign($sTarget,$sAttribute,'');
+	}
+	
+	/**
+	 * Adds an alert command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addAlert("This is important information");</kbd>
+	 * 
+	 * @param string the text to be displayed in the Javascript alert box
+	 */
+	function addAlert($sMsg)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"al"),$sMsg);
+	}
+
+	/**
+	 * Uses the addScript() method to add a Javascript redirect to another URL.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addRedirect("http://www.xajaxproject.org");</kbd>
+	 * 
+	 * @param string the URL to redirect the client browser to
+	 */	
+	function addRedirect($sURL)
+	{
+		//we need to parse the query part so that the values are rawurlencode()'ed
+		//can't just use parse_url() cos we could be dealing with a relative URL which
+		//  parse_url() can't deal with.
+		$queryStart = strpos($sURL, '?', strrpos($sURL, '/'));
+		if ($queryStart !== FALSE)
+		{
+			$queryStart++;
+			$queryEnd = strpos($sURL, '#', $queryStart);
+			if ($queryEnd === FALSE)
+				$queryEnd = strlen($sURL);
+			$queryPart = substr($sURL, $queryStart, $queryEnd-$queryStart);
+			parse_str($queryPart, $queryParts);
+			$newQueryPart = "";
+			foreach($queryParts as $key => $value)
+			{
+				$newQueryPart .= rawurlencode($key).'='.rawurlencode($value).ini_get('arg_separator.output');
+			}
+			$sURL = str_replace($queryPart, $newQueryPart, $sURL);
+		}
+		$this->addScript('window.location = "'.$sURL.'";');
+	}
+
+	/**
+	 * Adds a Javascript command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addScript("var x = prompt('get some text');");</kbd>
+	 * 
+	 * @param string contains Javascript code to be executed
+	 */
+	function addScript($sJS)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"js"),$sJS);
+	}
+
+	/**
+	 * Adds a Javascript function call command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addScriptCall("myJSFunction", "arg 1", "arg 2", 12345);</kbd>
+	 * 
+	 * @param string $sFunc the name of a Javascript function
+	 * @param mixed $args,... optional arguments to pass to the Javascript function
+	 */
+	function addScriptCall() {
+		$arguments = func_get_args();
+		$sFunc = array_shift($arguments);
+		$sData = $this->_buildObjXml($arguments);
+		$this->xml .= $this->_cmdXML(array("n"=>"jc","t"=>$sFunc),$sData);
+	}
+
+	/**
+	 * Adds a remove element command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addRemove("Div2");</kbd>
+	 * 
+	 * @param string contains the id of an HTML element to be removed
+	 */
+	function addRemove($sTarget)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"rm","t"=>$sTarget),'');
+	}
+
+	/**
+	 * Adds a create element command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addCreate("parentDiv", "h3", "myid");</kbd>
+	 * 
+	 * @param string contains the id of an HTML element to to which the new
+	 *               element will be appended.
+	 * @param string the tag to be added
+	 * @param string the id to be assigned to the new element
+	 * @param string deprecated, use the addCreateInput() method instead
+	 */
+	function addCreate($sParent, $sTag, $sId, $sType="")
+	{
+		if ($sType)
+		{
+			trigger_error("The \$sType parameter of addCreate has been deprecated.  Use the addCreateInput() method instead.", E_USER_WARNING);
+			return;
+		}
+		$this->xml .= $this->_cmdXML(array("n"=>"ce","t"=>$sParent,"p"=>$sId),$sTag);
+	}
+
+	/**
+	 * Adds a insert element command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addInsert("childDiv", "h3", "myid");</kbd>
+	 * 
+	 * @param string contains the id of the child before which the new element
+	 *               will be inserted
+	 * @param string the tag to be added
+	 * @param string the id to be assigned to the new element
+	 */
+	function addInsert($sBefore, $sTag, $sId)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"ie","t"=>$sBefore,"p"=>$sId),$sTag);
+	}
+
+	/**
+	 * Adds a insert element command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addInsertAfter("childDiv", "h3", "myid");</kbd>
+	 * 
+	 * @param string contains the id of the child after which the new element
+	 *               will be inserted
+	 * @param string the tag to be added
+	 * @param string the id to be assigned to the new element
+	 */
+	function addInsertAfter($sAfter, $sTag, $sId)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"ia","t"=>$sAfter,"p"=>$sId),$sTag);
+	}
+	
+	/**
+	 * Adds a create input command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addCreateInput("form1", "text", "username", "input1");</kbd>
+	 * 
+	 * @param string contains the id of an HTML element to which the new input
+	 *               will be appended
+	 * @param string the type of input to be created (text, radio, checkbox,
+	 *               etc.)
+	 * @param string the name to be assigned to the new input and the variable
+	 *               name when it is submitted
+	 * @param string the id to be assigned to the new input
+	 */
+	function addCreateInput($sParent, $sType, $sName, $sId)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"ci","t"=>$sParent,"p"=>$sId,"c"=>$sType),$sName);
+	}
+
+	/**
+	 * Adds an insert input command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addInsertInput("input5", "text", "username", "input1");</kbd>
+	 * 
+	 * @param string contains the id of the child before which the new element
+	 *               will be inserted
+	 * @param string the type of input to be created (text, radio, checkbox,
+	 *               etc.)
+	 * @param string the name to be assigned to the new input and the variable
+	 *               name when it is submitted
+	 * @param string the id to be assigned to the new input
+	 */
+	function addInsertInput($sBefore, $sType, $sName, $sId)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"ii","t"=>$sBefore,"p"=>$sId,"c"=>$sType),$sName);
+	}
+
+	/**
+	 * Adds an insert input command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addInsertInputAfter("input7", "text", "email", "input2");</kbd>
+	 * 
+	 * @param string contains the id of the child after which the new element
+	 *               will be inserted
+	 * @param string the type of input to be created (text, radio, checkbox,
+	 *               etc.)
+	 * @param string the name to be assigned to the new input and the variable
+	 *               name when it is submitted
+	 * @param string the id to be assigned to the new input
+	 */
+	function addInsertInputAfter($sAfter, $sType, $sName, $sId)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"iia","t"=>$sAfter,"p"=>$sId,"c"=>$sType),$sName);
+	}
+
+	/**
+	 * Adds an event command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addEvent("contentDiv", "onclick", "alert(\'Hello World\');");</kbd>
+	 * 
+	 * @param string contains the id of an HTML element
+	 * @param string the event you wish to set ("onclick", "onmouseover", etc.)
+	 * @param string the Javascript string you want the event to invoke
+	 */
+	function addEvent($sTarget,$sEvent,$sScript)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"ev","t"=>$sTarget,"p"=>$sEvent),$sScript);
+	}
+
+	/**
+	 * Adds a handler command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addHandler("contentDiv", "onclick", "content_click");</kbd>
+	 * 
+	 * @param string contains the id of an HTML element
+	 * @param string the event you wish to set ("onclick", "onmouseover", etc.)
+	 * @param string the name of a Javascript function that will handle the
+	 *               event. Multiple handlers can be added for the same event
+	 */
+	function addHandler($sTarget,$sEvent,$sHandler)
+	{	
+		$this->xml .= $this->_cmdXML(array("n"=>"ah","t"=>$sTarget,"p"=>$sEvent),$sHandler);
+	}
+
+	/**
+	 * Adds a remove handler command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addRemoveHandler("contentDiv", "onclick", "content_click");</kbd>
+	 * 
+	 * @param string contains the id of an HTML element
+	 * @param string the event you wish to remove ("onclick", "onmouseover",
+	 *               etc.)
+	 * @param string the name of a Javascript handler function that you want to
+	 *               remove
+	 */
+	function addRemoveHandler($sTarget,$sEvent,$sHandler)
+	{	
+		$this->xml .= $this->_cmdXML(array("n"=>"rh","t"=>$sTarget,"p"=>$sEvent),$sHandler);
+	}
+
+	/**
+	 * Adds an include script command message to the XML response.
+	 * 
+	 * <i>Usage:</i> <kbd>$objResponse->addIncludeScript("functions.js");</kbd>
+	 * 
+	 * @param string URL of the Javascript file to include
+	 */
+	function addIncludeScript($sFileName)
+	{
+		$this->xml .= $this->_cmdXML(array("n"=>"in"),$sFileName);
+	}
+
+	/**	
+	 * Returns the XML to be returned from your function to the xajax processor
+	 * on your page. Since xajax 0.2, you can also return an xajaxResponse
+	 * object from your function directly, and xajax will automatically request
+	 * the XML using this method call.
+	 * 
+	 * <i>Usage:</i> <kbd>return $objResponse->getXML();</kbd>
+	 * 
+	 * @return string response XML data
+	 */
+	function getXML()
+	{
+		$sXML = "<?xml version=\"1.0\"";
+		if ($this->sEncoding && strlen(trim($this->sEncoding)) > 0)
+			$sXML .= " encoding=\"".$this->sEncoding."\"";
+		$sXML .= " ?"."><xjx>" . $this->xml . "</xjx>";
+		
+		return $sXML;
+	}
+	
+	/**
+	 * Adds the commands of the provided response XML output to this response
+	 * object
+	 * 
+	 * <i>Usage:</i>
+	 * <code>$r1 = $objResponse1->getXML();
+	 * $objResponse2->loadXML($r1);
+	 * return $objResponse2->getXML();</code>
+	 * 
+	 * @param string the response XML (returned from a getXML() method) to add
+	 *               to the end of this response object
+	 */
+	function loadXML($mXML)
+	{
+		if (is_a($mXML, "xajaxResponse")) {
+			$mXML = $mXML->getXML();
+		}
+		$sNewXML = "";
+		$iStartPos = strpos($mXML, "<xjx>") + 5;
+		$sNewXML = substr($mXML, $iStartPos);
+		$iEndPos = strpos($sNewXML, "</xjx>");
+		$sNewXML = substr($sNewXML, 0, $iEndPos);
+		$this->xml .= $sNewXML;
+	}
+
+	/**
+	 * Generates XML from command data
+	 * 
+	 * @access private
+	 * @param array associative array of attributes
+	 * @param string data
+	 * @return string XML command
+	 */
+	function _cmdXML($aAttributes, $sData)
+	{
+		if ($this->bOutputEntities) {
+			if (function_exists('mb_convert_encoding')) {
+				$sData = call_user_func_array('mb_convert_encoding', array(&$sData, 'HTML-ENTITIES', $this->sEncoding));
+			}
+			else {
+				trigger_error("The xajax XML response output could not be converted to HTML entities because the mb_convert_encoding function is not available", E_USER_NOTICE);
+			}
+		}
+		$xml = "<cmd";
+		foreach($aAttributes as $sAttribute => $sValue)
+			$xml .= " $sAttribute=\"$sValue\"";
+		if ($sData !== null && !stristr($sData,'<![CDATA['))
+			$xml .= "><![CDATA[$sData]]></cmd>";
+		else if ($sData !== null)
+			$xml .= ">$sData</cmd>";
+		else
+			$xml .= "></cmd>";
+		
+		return $xml;
+	}
+
+	/**
+	 * Recursively serializes a data structure in XML so it can be sent to
+	 * the client. It could be thought of as the opposite of
+	 * {@link xajax::_parseObjXml()}.
+	 * 
+	 * @access private
+	 * @param mixed data structure to serialize to XML
+	 * @return string serialized XML
+	 */
+	function _buildObjXml($var) {
+		if (gettype($var) == "object") $var = get_object_vars($var);
+		if (!is_array($var)) {
+			return "<![CDATA[$var]]>";
+		}
+		else {
+			$data = "<xjxobj>";
+			foreach ($var as $key => $value) {
+				$data .= "<e>";
+				$data .= "<k>" . htmlspecialchars($key) . "</k>";
+				$data .= "<v>" . $this->_buildObjXml($value) . "</v>";
+				$data .= "</e>";
+			}
+			$data .= "</xjxobj>";
+			return $data;
+		}
+	}
+	
+}// end class xajaxResponse
+?>
diff --git a/templates/default/includes/header.php b/templates/default/includes/header.php
index 9e5db4635e9d103dabfe6d576bfcc0db8fdf7e60..61ae8deb84a5a74a8f924d26b1bcdd7c17c8e202 100644
--- a/templates/default/includes/header.php
+++ b/templates/default/includes/header.php
@@ -14,6 +14,12 @@ if(array_key_exists('header', $this->data)) {
 	<link rel="icon" type="image/icon" href="/<?php echo $this->data['baseurlpath']; ?>resources/icons/favicon.ico" />
 	<script type="text/javascript" src="/<?php echo $this->data['baseurlpath']; ?>resources/script.js"></script>
 	<meta name="ROBOTS" content="NOINDEX, NOFOLLOW" />
+
+<?php	
+if(array_key_exists('head', $this->data)) {
+	echo '<!-- head -->' . $this->data['head'] . '<!-- /head -->';
+}
+?>
 </head>
 <?php
 $onLoad = '';
diff --git a/templates/default/logout-iframe.php b/templates/default/logout-iframe.php
new file mode 100644
index 0000000000000000000000000000000000000000..c6d8f44ec5d72bf39aa463bf0d155d3c20d4c738
--- /dev/null
+++ b/templates/default/logout-iframe.php
@@ -0,0 +1,44 @@
+<?php 
+
+	
+	$this->data['head'] .= '<script language="JavaScript">
+// use pre-formatted output for this multiplication table
+var j;	// loop variables
+
+xajax_updateslostatus();
+for (j=1; j<=10; j++) {
+	setTimeout(\'xajax_updateslostatus()\',j*1000)
+}
+</script>';
+
+
+	$this->includeAtTemplateBase('includes/header.php');
+	
+#	$this->includeLanguageFile('consent.php'); 
+#	$this->includeInlineTranslation('spname', $this->data['sp_name']);
+#	$this->includeInlineTranslation('IDPNAME', $this->data['idp_name']);
+?>
+
+	<div id="content">
+
+	<p style="margin: .2em">[ <a href="<?php echo $this->data['logoutresponse']; ?>">Interrupt logging out and go back to service</a> ]</p>
+	
+	<?php
+	
+		foreach ($this->data['sparray'] AS $sp) {
+			echo '<iframe class="hiddeniframe" style="width: 200px; height: 100px" src="' . $sp['url'] . '" ></iframe>';
+		}
+		
+		foreach ($this->data['sparray'] AS $spentityid => $sp) {
+			echo '<div class="inprogress" style="border: 1px solid #888; background: #eee; color: #444; padding: .2em; margin: .7em" id="' . $spentityid . '">
+				<img style="float: left; margin: 3px" src="/' . $this->data['baseurlpath'] . 'resources/progress.gif" />Wait... is logging out from ' . $spentityid . '</div>';
+		}
+		
+	
+	?>
+	</div>
+
+
+	
+
+<?php $this->includeAtTemplateBase('includes/footer.php'); ?>
diff --git a/www/resources/default.css b/www/resources/default.css
index d067b73acfc8ad76f353676215a3228ae429c8e1..64cd821140573f5e7d2bbdac3c573944e0b3cd0e 100644
--- a/www/resources/default.css
+++ b/www/resources/default.css
@@ -255,3 +255,14 @@ table.attributes tr td {
 	padding-top: 5px;
 	vertical-align: top;
 }
+
+div.inprogress {
+	background: #eee;
+}
+div.loggedout {
+	background: #9f9 ! important ;
+}
+iframe.hiddeniframe {
+	display: none;
+}
+
diff --git a/www/resources/progress.gif b/www/resources/progress.gif
new file mode 100644
index 0000000000000000000000000000000000000000..529e72f45a2395e2eddb0487edbe79b306c92c3a
Binary files /dev/null and b/www/resources/progress.gif differ
diff --git a/www/saml2/idp/SingleLogoutService.php b/www/saml2/idp/SingleLogoutService.php
index b60ce02994e590c80ff89c205ca382093f84a07e..95ae16c0268deaa6d77d04aaf60f43052fbfb6e0 100644
--- a/www/saml2/idp/SingleLogoutService.php
+++ b/www/saml2/idp/SingleLogoutService.php
@@ -4,15 +4,14 @@
  * This SAML 2.0 endpoint can receive incomming LogoutRequests. It will also send LogoutResponses, 
  * and LogoutRequests and also receive LogoutResponses. It is implemeting SLO at the SAML 2.0 IdP.
  *
- * @author Andreas Ĺkre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
+ * @author Andreas Ă…kre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
  * @package simpleSAMLphp
  * @version $Id$
  */
 
 // TODO: Show error message, when shibboleth sp is logged in.
-// TODO: Propagate HTTP-REDIRECT SLO on SAML 2.0 bridge.
 
-require_once('../../../www/_include.php');
+require_once('../../_include.php');
 
 $config = SimpleSAML_Configuration::getInstance();
 $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
@@ -140,7 +139,6 @@ if (isset($_GET['SAMLRequest'])) {
 
 	$session->doLogout();
 
-
 	/* Fill in the $logoutInfo associative array with information about this logout request. */
 	$logoutInfo['Issuer'] = $logoutrequest->getIssuer();
 	$logoutInfo['RequestID'] = $logoutrequest->getRequestID();
diff --git a/www/saml2/idp/SingleLogoutServiceAjax.php b/www/saml2/idp/SingleLogoutServiceAjax.php
new file mode 100644
index 0000000000000000000000000000000000000000..02cd1d15843a198a3a60ee051d5b2bcba51ace5c
--- /dev/null
+++ b/www/saml2/idp/SingleLogoutServiceAjax.php
@@ -0,0 +1,260 @@
+<?php
+
+
+require_once('../../_include.php');
+
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutRequest.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutResponse.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPRedirect.php');
+//require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPPost.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php');
+
+
+
+
+session_start();
+
+$config = SimpleSAML_Configuration::getInstance();
+$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+
+
+
+
+$idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
+
+$session = SimpleSAML_Session::getInstance();
+
+
+
+
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . '../../xajax/xajax.inc.php');
+
+
+
+
+
+/*
+ * This function is called via AJAX and will send LogoutRequest to one single SP by
+ * sending a LogoutRequest using HTTP-REDIRECT
+ */
+function httpredirectslo($spentityid) {
+
+
+	
+	$config = SimpleSAML_Configuration::getInstance();
+	$metadata = new SimpleSAML_XML_MetaDataStore($config);
+	
+	$idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
+	
+	$session = SimpleSAML_Session::getInstance();
+
+	if (!isset($_GET['loggedoutservice'])) {
+	
+		try {
+			$lr = new SimpleSAML_XML_SAML20_LogoutRequest($config, $metadata);
+		
+			// ($issuer, $receiver, $nameid, $nameidformat, $sessionindex, $mode) {
+			$req = $lr->generate($idpentityid, $spentityid, $session->getNameID(), $session->getNameIDFormat(), $session->getSessionIndex(), 'IdP');
+			
+			$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+			
+			$relayState = SimpleSAML_Utilities::selfURL();
+	
+			
+			//$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP'
+			//$httpredirect->sendMessage($req, $spentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'IdP');
+			
+			//header('Location: vg.no');
+			header('Location: http://vg.no');
+			
+			exit();
+	
+		} catch(Exception $exception) {
+			
+			$et = new SimpleSAML_XHTML_Template($config, 'error.php');
+			
+			$et->data['header'] = 'Error sending logout request to service';
+			$et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.';	
+			$et->data['e'] = $exception;
+			
+			$et->show();
+			exit();
+		}
+
+
+	}
+
+
+
+
+	$newContent = "Successfully logged out from ".$spentityid;
+
+    // Instantiate the xajaxResponse object
+    $objResponse = new xajaxResponse();
+    
+    // add a command to the response to assign the innerHTML attribute of
+    // the element with id="SomeElementId" to whatever the new content is
+    $objResponse->addAssign($spentityid,"innerHTML", $newContent);
+    
+    //return the  xajaxResponse object
+    return $objResponse;
+}
+
+
+
+$xajax = new xajax();
+$xajax->registerFunction("httpredirectslo", XAJAX_GET);
+$xajax->processRequests();
+
+
+
+
+
+
+
+
+
+
+
+$session->dump_sp_sessions();
+
+
+
+
+
+$et = new SimpleSAML_XHTML_Template($config, 'logout-ajax.php');
+
+$et->data['header'] = 'SAML 2.0 IdP Ajax Logout';
+$et->data['listofsps'] = $session->get_sp_list();;
+$et->data['xajax'] = $xajax;
+$et->show();
+
+exit(0);
+
+
+
+
+
+
+/*
+ * If we get an LogoutRequest then we initiate the logout process.
+ */
+if (isset($_GET['SAMLRequest'])) {
+
+	$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+	$logoutrequest = $binding->decodeLogoutRequest($_GET);
+	
+	$session->setAuthenticated(false);
+
+	//$requestid = $authnrequest->getRequestID();
+	//$session->setAuthnRequest($requestid, $authnrequest);
+	
+	//echo '<pre>' . htmlentities($logoutrequest->getXML()) . '</pre>';
+	
+	error_log('IdP LogoutService: got Logoutrequest from ' . $logoutrequest->getIssuer() . '  ');
+	
+	$session->set_sp_logout_completed($logoutrequest->getIssuer() );
+	$session->setLogoutRequest($logoutrequest);
+
+/*
+ * We receive a Logout Response to a Logout Request that we have issued earlier.
+ */
+} elseif (isset($_GET['SAMLResponse'])) {
+
+	$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+	$loginresponse = $binding->decodeLogoutResponse($_GET);
+	
+	$session->set_sp_logout_completed($loginresponse->getIssuer());
+	
+	error_log('IdP LogoutService: got LogoutResponse from ' . $loginresponse->getIssuer() . '  ');
+}
+
+/*
+ * We proceed to send logout requests to all remaining SPs.
+ */
+$spentityid = $session->get_next_sp_logout();
+if ($spentityid) {
+
+	error_log('IdP LogoutService: next SP ' . $spentityid);
+
+	try {
+		$lr = new SimpleSAML_XML_SAML20_LogoutRequest($config, $metadata);
+	
+		// ($issuer, $receiver, $nameid, $nameidformat, $sessionindex, $mode) {
+		$req = $lr->generate($idpentityid, $spentityid, $session->getNameID(), $session->getNameIDFormat(), $session->getSessionIndex(), 'IdP');
+		
+		$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+		
+		$relayState = SimpleSAML_Utilities::selfURL();
+		if (isset($_GET['RelayState'])) {
+			$relayState = $_GET['RelayState'];
+		}
+		
+		//$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP'
+		$httpredirect->sendMessage($req, $spentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'IdP');
+		
+		exit();
+
+	} catch(Exception $exception) {
+		
+		$et = new SimpleSAML_XHTML_Template($config, 'error.php');
+		
+		$et->data['header'] = 'Error sending logout request to service';
+		$et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.';	
+		$et->data['e'] = $exception;
+		
+		$et->show();
+		exit(0);
+	}
+
+
+}
+
+/*
+ * Logout procedure is done and we send a Logout Response back to the SP
+ */
+error_log('IdP LogoutService:  SPs done ');
+try {
+
+	$logoutrequest = $session->getLogoutRequest();
+	if (!$logoutrequest) {
+		throw new Exception('Could not get reference to the logout request.');
+	}
+
+	$rg = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata);
+	
+	// generate($issuer, $receiver, $inresponseto, $mode )
+	
+	$logoutResponseXML = $rg->generate($idpentityid, $logoutrequest->getIssuer(), $logoutrequest->getRequestID(), 'IdP');
+	
+	//	echo '<pre>' . htmlentities($logoutResponseXML) . '</pre>';
+	//	exit();
+	
+	$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+	
+	$relayState = SimpleSAML_Utilities::selfURL();
+	if (isset($_GET['RelayState'])) {
+		$relayState = $_GET['RelayState'];
+	}
+	
+	//$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP'
+	$httpredirect->sendMessage($logoutResponseXML, $logoutrequest->getIssuer(), $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP');
+
+} catch(Exception $exception) {
+	
+	$et = new SimpleSAML_XHTML_Template($config, 'error.php');
+	
+	$et->data['header'] = 'Error sending response to service';
+	$et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.';	
+	$et->data['e'] = $exception;
+	
+	$et->show();
+
+}
+
+
+
+?>
\ No newline at end of file
diff --git a/www/saml2/idp/SingleLogoutServiceAjaxResponse.php b/www/saml2/idp/SingleLogoutServiceAjaxResponse.php
new file mode 100644
index 0000000000000000000000000000000000000000..a78e47d9e756494119553cdacffa7b4eb4aee8ae
--- /dev/null
+++ b/www/saml2/idp/SingleLogoutServiceAjaxResponse.php
@@ -0,0 +1,52 @@
+<?php
+
+
+require_once('../../_include.php');
+
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutRequest.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutResponse.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPRedirect.php');
+//require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPPost.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php');
+
+
+
+
+
+
+session_start();
+
+$config = SimpleSAML_Configuration::getInstance();
+$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+
+$idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
+
+$session = SimpleSAML_Session::getInstance();
+
+//$session->dump_sp_sessions();
+
+
+
+if (isset($_GET['SAMLResponse'])) {
+
+	$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+	$logoutresponse = $binding->decodeLogoutResponse($_GET);
+
+	$session->set_sp_logout_completed($logoutresponse->getIssuer());
+	
+	error_log('IdP LogoutService: got LogoutResponse at SingleLogoutServiceAjaxResponse from ' . $logoutresponse->getIssuer() . '  ');
+	
+	$relaystate = $logoutresponse->getRelayState();
+
+	$redirURL = SimpleSAML_Utilities::addURLparameter($relaystate, 'loggedoutservice=' . $logoutresponse->getIssuer() );
+	header('Location: ' . $redirURL);	
+} else {
+
+	error_log('Error on SingleLogoutServiceAjaxResponse');
+
+}
+
+?>
\ No newline at end of file
diff --git a/www/saml2/idp/SingleLogoutServiceiFrame.php b/www/saml2/idp/SingleLogoutServiceiFrame.php
new file mode 100644
index 0000000000000000000000000000000000000000..4d2c791add20bf6448de3c89f9a79e33b48f05bb
--- /dev/null
+++ b/www/saml2/idp/SingleLogoutServiceiFrame.php
@@ -0,0 +1,537 @@
+<?php
+
+/**
+ * This SAML 2.0 endpoint can receive incomming LogoutRequests. It will also send LogoutResponses, 
+ * and LogoutRequests and also receive LogoutResponses. It is implemeting SLO at the SAML 2.0 IdP.
+ *
+ * @author Andreas Ă…kre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+
+require_once('../../_include.php');
+
+$config = SimpleSAML_Configuration::getInstance();
+$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+$session = SimpleSAML_Session::getInstance();
+
+SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutServiceiFrame: Accessing SAML 2.0 IdP endpoint SingleLogoutService (iFrame version)');
+
+if (!$config->getValue('enable.saml20-idp', false))
+	SimpleSAML_Utilities::fatalError(isset($session) ? $session->getTrackID() : null, 'NOACCESS');
+
+try {
+	$idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
+} catch (Exception $exception) {
+	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'METADATA', $exception);
+}
+
+SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutServiceiFrame: Got IdP entity id: ' . $idpentityid);
+
+
+/**
+ * The $logoutInfo contains information about the current logout operation.
+ * It can have the following attributes:
+ * - 'RelayState' - The RelayState which should be returned to the SP which initiated the logout operation.
+ * - 'Issuer' - The entity id of the SP which initiated the logout operation.
+ * - 'RequestID' - The id of the LogoutRequest which initiated the logout operation.
+ */
+$logoutInfo = array();
+
+
+/**
+ * This function retrieves the logout info with the given ID.
+ *
+ * @param $id  The identifier of the logout info.
+ */
+function fetchLogoutInfo($id) {
+	global $session;
+	global $logoutInfo;
+
+	$logoutInfo = $session->getData('idplogoutresponsedata', $id);
+
+	if($logoutInfo === NULL) {
+		SimpleSAML_Logger::warning('SAML2.0 - IdP.SingleLogoutService: Lost logout information.');
+	}
+}
+
+
+/**
+ * This function saves the logout info with the given ID.
+ *
+ * @param $id  The identifier the logout info should be saved with.
+ */
+function saveLogoutInfo($id) {
+	global $session;
+	global $logoutInfo;
+
+	$session->setData('idplogoutresponsedata', $id, $logoutInfo);
+}
+
+
+// Include XAJAX definition.
+require_once(SimpleSAML_Utilities::resolvePath('libextinc') . '/xajax/xajax.inc.php');
+
+
+
+/*
+ * This function is called via AJAX and will send LogoutRequest to one single SP by
+ * sending a LogoutRequest using HTTP-REDIRECT
+ */
+function updateslostatus() {
+
+	$config = SimpleSAML_Configuration::getInstance();
+	$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+	$session = SimpleSAML_Session::getInstance();
+
+	$idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
+	
+	$listofsps = $session->get_sp_list(SimpleSAML_Session::STATE_LOGGEDOUT);
+
+    // Instantiate the xajaxResponse object
+    $objResponse = new xajaxResponse();
+
+	foreach ($listofsps AS $spentityid) {
+
+		error_log('Completed ' . $spentityid);
+		// add a command to the response to assign the innerHTML attribute of
+		// the element with id="SomeElementId" to whatever the new content is
+		$objResponse->addAssign($spentityid, "className", 'loggedout');
+		$objResponse->addAssign($spentityid, "innerHTML", 'Logging out from ' . $spentityid . ' successfully completed');
+
+	}
+    
+    //return the  xajaxResponse object
+    return $objResponse;
+}
+
+
+
+$xajax = new xajax();
+$xajax->registerFunction("updateslostatus");
+$xajax->processRequests();
+
+
+
+
+
+/*
+ * If we get an LogoutRequest then we initiate the logout process.
+ */
+if (isset($_GET['SAMLRequest'])) {
+
+	SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: Got SAML reuqest');
+
+	$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+
+	try {
+		$logoutrequest = $binding->decodeLogoutRequest($_GET);
+
+		if ($binding->validateQuery($logoutrequest->getIssuer(),'IdP')) {
+			SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Valid signature found for '.$logoutrequest->getRequestID());
+		}
+
+	} catch(Exception $exception) {
+	
+		SimpleSAML_Utilities::fatalError($session->getTrackID(), 'LOGOUTREQUEST', $exception);
+		
+	}
+	
+	// Extract some parameters from the logout request
+	#$requestid = $logoutrequest->getRequestID();
+	$requester = $logoutrequest->getIssuer();
+	#$relayState = $logoutrequest->getRelayState();
+
+	//$responder = $config->getValue('saml2-hosted-sp');
+	$responder = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
+	
+	
+	SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: got Logoutrequest from ' . $logoutrequest->getIssuer());
+	SimpleSAML_Logger::stats('saml20-idp-SLO spinit ' . $requester . ' ' . $responder);
+	
+	/* Check if we have a valid session. */
+	if($session === NULL) {
+	
+		/* Invalid session. To prevent the user from being unable to
+		 * log out from the service provider, we should just return a
+		 * LogoutResponse pretending that the logout was successful to
+		 * the SP that sent the LogoutRequest.
+		 */
+
+		SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Did not find a session here, but we are returning a LogoutResponse anyway.');
+
+		$spentityid = $logoutrequest->getIssuer();
+
+		/* Generate the response. */
+		$response = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata);
+		$responseText = $response->generate($idpentityid, $spentityid, $logoutrequest->getRequestID(), 'IdP');
+
+		/* Retrieve the relay state from the request. */
+		$relayState = $logoutrequest->getRelayState();
+
+		/* Send the response using the HTTP-Redirect binding. */
+		$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config,
+		$metadata);
+		$binding->sendMessage($responseText, $idpentityid, $spentityid, $relayState,
+			'SingleLogoutService', 'SAMLResponse', 'IdP');
+		exit;
+	}
+
+
+	$session->doLogout();
+
+
+	/* Fill in the $logoutInfo associative array with information about this logout request. */
+	$logoutInfo['Issuer'] = $logoutrequest->getIssuer();
+	$logoutInfo['RequestID'] = $logoutrequest->getRequestID();
+
+	$relayState = $logoutrequest->getRelayState();
+	if($relayState !== NULL) {
+		$logoutInfo['RelayState'] = $relayState;
+	}
+
+		
+	SimpleSAML_Logger::debug('SAML2.0 - IDP.SingleLogoutService: Setting cached request with issuer ' . $logoutrequest->getIssuer());
+	
+	$session->set_sp_logout_completed($logoutrequest->getIssuer() );
+	
+
+
+/*
+ * We receive a Logout Response to a Logout Request that we have issued earlier.
+ * If so, there is a misconfiguration.
+ */
+} elseif (isset($_GET['SAMLResponse'])) {
+
+	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'LOGOUTRESPONSE', 
+		new Exception('The SP is likely to be misconfigured. The LogoutResponse is sent to wrong endpoint. This iFrame endpoint only accepts LogoutRequests, and the response is to be sent to a separate endpoint. Please revisit the IdP-Remote metadata on the SP.')
+	);
+
+} else {
+	/*
+	 * We have no idea what to do here. It is neither a logout request, a logout
+	 * response nor a response from bridged SLO.
+	 */
+	SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: No request, response or bridge');
+	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'SLOSERVICEPARAMS');
+}
+
+
+
+$session->dump_sp_sessions();
+
+
+
+/*
+ * Generate a list of all service providers, and creat a LogoutRequest message for all these SPs.
+ */
+$listofsps = $session->get_sp_list();
+$sparray = array();
+foreach ($listofsps AS $spentityid) {
+
+	// ($issuer, $receiver, $nameid, $nameidformat, $sessionindex, $mode) {
+	$nameId = $session->getSessionNameId('saml20-sp-remote', $spentityid);
+	if($nameId === NULL) {
+		$nameId = $session->getNameID();
+	}
+	
+	$lr = new SimpleSAML_XML_SAML20_LogoutRequest($config, $metadata);
+	$req = $lr->generate($idpentityid, $spentityid, $nameId, $session->getSessionIndex(), 'IdP');
+
+	$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+
+
+	// $request, $localentityid, $remoteentityid, $relayState = null, $endpoint = 'SingleSignOnService', $direction = 'SAMLRequest', $mode = 'SP'
+	$url = $httpredirect->getRedirectURL($req, $idpentityid, $spentityid, NULL, 'SingleLogoutService', 'SAMLRequest', 'IdP');
+
+	$sparray[$spentityid] = array('url' => $url);
+
+}
+
+
+
+
+
+
+
+/*
+try {
+
+	$logoutrequest = $session->getLogoutRequest();
+	if (!$logoutrequest) {
+		throw new Exception('Could not get reference to the logout request.');
+	}
+
+	$rg = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata);
+	
+	
+	$logoutResponseXML = $rg->generate($idpentityid, $logoutrequest->getIssuer(), $logoutrequest->getRequestID(), 'IdP');
+	
+	
+	$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+	
+	$relayState = SimpleSAML_Utilities::selfURL();
+	if (isset($_GET['RelayState'])) {
+		$relayState = $_GET['RelayState'];
+	}
+	
+
+	$logoutresponse = $httpredirect->getRedirectURL($logoutResponseXML, $logoutrequest->getIssuer(), $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP');
+
+} catch(Exception $exception) {
+	
+	$et = new SimpleSAML_XHTML_Template($config, 'error.php');
+	
+	$et->data['header'] = 'Error sending response to service';
+	$et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.';	
+	$et->data['e'] = $exception;
+	
+	$et->show();
+
+}
+
+
+
+*/
+
+
+
+
+
+
+
+
+/*
+ * Logout procedure is done and we send a Logout Response back to the SP
+ */
+
+try {
+
+	if(!$logoutInfo) {
+		SimpleSAML_Utilities::fatalError($session->getTrackID(), 'LOGOUTINFOLOST');
+	}
+	
+	SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: Found logout info with these keys: ' . join(',', array_keys($logoutInfo)));
+	
+	/**
+	 * Clean up session object to save storage.
+	 */
+	if ($config->getValue('debug', false)) 
+		SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size before cleaning: ' . $session->getSize());
+		
+	$session->clean();
+	
+	if ($config->getValue('debug', false)) 
+		SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size after cleaning: ' . $session->getSize());
+	
+	
+	/*
+	 * Check if the Single Logout procedure is initated by an SP (alternatively IdP initiated SLO
+	 */
+	if (array_key_exists('Issuer', $logoutInfo)) {
+		
+		/**
+		 * Create a Logot Response.
+		 */
+		$rg = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata);
+	
+		// generate($issuer, $receiver, $inresponseto, $mode )
+		$logoutResponseXML = $rg->generate($idpentityid, $logoutInfo['Issuer'], $logoutInfo['RequestID'], 'IdP');
+	
+		// Create a HTTP-REDIRECT Binding.
+		$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+	
+		// Find the relaystate if cached.
+		$relayState = isset($logoutInfo['RelayState']) ? $logoutInfo['RelayState'] : null;
+	
+		// Parameters: $request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP'
+		$logoutresponse = $httpredirect->getRedirectURL($logoutResponseXML, $idpentityid, $logoutInfo['Issuer'], $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP');
+
+		
+	} elseif (array_key_exists('RelayState', $logoutInfo)) {
+
+		SimpleSAML_Utilities::redirect($logoutInfo['RelayState']);
+		exit;
+		
+	} else {
+	
+		echo 'You are logged out'; exit;
+	
+	}
+
+} catch(Exception $exception) {
+	
+	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'GENERATELOGOUTRESPONSE', $exception);
+	
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+$et = new SimpleSAML_XHTML_Template($config, 'logout-iframe.php');
+
+$et->data['header'] = 'SAML 2.0 IdP Ajax Logout';
+$et->data['sparray'] = $sparray;
+$et->data['logoutresponse'] = $logoutresponse;
+$et->data['xajax'] = $xajax;
+
+$et->data['head'] = $xajax->printJavascript();
+
+$et->show();
+
+exit(0);
+
+
+
+
+
+
+/*
+ * If we get an LogoutRequest then we initiate the logout process.
+ */
+if (isset($_GET['SAMLRequest'])) {
+
+	$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+	$logoutrequest = $binding->decodeLogoutRequest($_GET);
+	
+	$session->setAuthenticated(false);
+
+	//$requestid = $authnrequest->getRequestID();
+	//$session->setAuthnRequest($requestid, $authnrequest);
+	
+	//echo '<pre>' . htmlentities($logoutrequest->getXML()) . '</pre>';
+	
+	error_log('IdP LogoutService: got Logoutrequest from ' . $logoutrequest->getIssuer() . '  ');
+	
+	$session->set_sp_logout_completed($logoutrequest->getIssuer() );
+	$session->setLogoutRequest($logoutrequest);
+
+/*
+ * We receive a Logout Response to a Logout Request that we have issued earlier.
+ */
+} elseif (isset($_GET['SAMLResponse'])) {
+
+	$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+	$loginresponse = $binding->decodeLogoutResponse($_GET);
+	
+	$session->set_sp_logout_completed($loginresponse->getIssuer());
+	
+	error_log('IdP LogoutService: got LogoutResponse from ' . $loginresponse->getIssuer() . '  ');
+}
+
+/*
+ * We proceed to send logout requests to all remaining SPs.
+ */
+$spentityid = $session->get_next_sp_logout();
+if ($spentityid) {
+
+	error_log('IdP LogoutService: next SP ' . $spentityid);
+
+	try {
+		$lr = new SimpleSAML_XML_SAML20_LogoutRequest($config, $metadata);
+	
+		// ($issuer, $receiver, $nameid, $nameidformat, $sessionindex, $mode) {
+		$req = $lr->generate($idpentityid, $spentityid, $session->getNameID(), $session->getNameIDFormat(), $session->getSessionIndex(), 'IdP');
+		
+		$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+		
+		$relayState = SimpleSAML_Utilities::selfURL();
+		if (isset($_GET['RelayState'])) {
+			$relayState = $_GET['RelayState'];
+		}
+		
+		//$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP'
+		$httpredirect->sendMessage($req, $spentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'IdP');
+		
+		exit();
+
+	} catch(Exception $exception) {
+		
+		$et = new SimpleSAML_XHTML_Template($config, 'error.php');
+		
+		$et->data['header'] = 'Error sending logout request to service';
+		$et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.';	
+		$et->data['e'] = $exception;
+		
+		$et->show();
+		exit(0);
+	}
+
+
+}
+
+/*
+ * Logout procedure is done and we send a Logout Response back to the SP
+ */
+error_log('IdP LogoutService:  SPs done ');
+try {
+
+	$logoutrequest = $session->getLogoutRequest();
+	if (!$logoutrequest) {
+		throw new Exception('Could not get reference to the logout request.');
+	}
+
+	$rg = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata);
+	
+	// generate($issuer, $receiver, $inresponseto, $mode )
+	
+	$logoutResponseXML = $rg->generate($idpentityid, $logoutrequest->getIssuer(), $logoutrequest->getRequestID(), 'IdP');
+	
+	//	echo '<pre>' . htmlentities($logoutResponseXML) . '</pre>';
+	//	exit();
+	
+	$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+	
+	$relayState = SimpleSAML_Utilities::selfURL();
+	if (isset($_GET['RelayState'])) {
+		$relayState = $_GET['RelayState'];
+	}
+	
+	//$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP'
+	$httpredirect->sendMessage($logoutResponseXML, $logoutrequest->getIssuer(), $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP');
+
+} catch(Exception $exception) {
+	
+	$et = new SimpleSAML_XHTML_Template($config, 'error.php');
+	
+	$et->data['header'] = 'Error sending response to service';
+	$et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.';	
+	$et->data['e'] = $exception;
+	
+	$et->show();
+
+}
+
+
+
+?>
\ No newline at end of file
diff --git a/www/saml2/idp/SingleLogoutServiceiFrameResponse.php b/www/saml2/idp/SingleLogoutServiceiFrameResponse.php
new file mode 100644
index 0000000000000000000000000000000000000000..12ad063d4ddc01dc6f442737d3d0f59cfc8c6701
--- /dev/null
+++ b/www/saml2/idp/SingleLogoutServiceiFrameResponse.php
@@ -0,0 +1,50 @@
+<?php
+
+
+require_once('../../_include.php');
+
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutRequest.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutResponse.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPRedirect.php');
+//require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPPost.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php');
+
+
+
+sleep(rand(1,6));
+
+
+session_start();
+
+$config = SimpleSAML_Configuration::getInstance();
+$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+
+$idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
+
+$session = SimpleSAML_Session::getInstance();
+
+//$session->dump_sp_sessions();
+
+
+
+if (isset($_GET['SAMLResponse'])) {
+
+	$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+	$logoutresponse = $binding->decodeLogoutResponse($_GET);
+
+	$session->set_sp_logout_completed($logoutresponse->getIssuer());
+	
+	error_log('IdP LogoutService: got LogoutResponse at SingleLogoutServiceAjaxResponse from ' . $logoutresponse->getIssuer() . '  ');
+	
+	echo 'OK';
+	
+} else {
+
+	error_log('Error on SingleLogoutServiceAjaxResponse');
+	echo 'Not set: SAMLResponse';
+}
+
+?>
\ No newline at end of file
diff --git a/www/saml2/idp/metadata.php b/www/saml2/idp/metadata.php
index 82ed4d188afa5aaafa094e28b0ce3261e2779990..6c7fb1d44fe6ba70ed1944b8dac4695453b27d2b 100644
--- a/www/saml2/idp/metadata.php
+++ b/www/saml2/idp/metadata.php
@@ -33,16 +33,25 @@ try {
 	$cert = file_get_contents($publiccert);
 	$data = XMLSecurityDSig::get509XCert($cert, true);
 	
+	$logouttype = 'traditional';
+	if (array_key_exists('logouttype', $idpmeta)) $logouttype = $idpmeta['logouttype'];
+	
+	$urlSLO = $metadata->getGenerated('SingleLogoutService', 'saml20-idp-hosted', array('logouttype' => $logouttype));
+	$urlSLOr = $metadata->getGenerated('SingleLogoutServiceResponse', 'saml20-idp-hosted', array('logouttype' => $logouttype));
 	
 	$metaflat = "
 	'" . htmlspecialchars($idpentityid) . "' =>  array(
 		'name'                 => 'Type in a name for this entity',
 		'description'          => 'and a proper description that would help users know when to select this IdP.',
-		'SingleSignOnService'  => '" . htmlspecialchars($metadata->getGenerated('SingleSignOnService', 'saml20-idp-hosted')) . "',
-		'SingleLogoutService'  => '" . htmlspecialchars($metadata->getGenerated('SingleLogoutService', 'saml20-idp-hosted')) . "',
+		'SingleSignOnService'  => '" . htmlspecialchars($metadata->getGenerated('SingleSignOnService', 'saml20-idp-hosted', array())) . "',
+		'SingleLogoutService'  => '" . htmlspecialchars($urlSLO) . "'," . 
+			(($urlSLO !== $urlSLOr) ? "
+		'SingleLogoutServiceResponse'  => '" . htmlspecialchars($urlSLOr) . "'," : "") . "
 		'certFingerprint'      => '" . strtolower(sha1(base64_decode($data))) ."'
 	),
 ";
+
+
 	
 	$metaxml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 	<EntityDescriptor xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
@@ -64,8 +73,8 @@ try {
         <!-- Logout endpoints -->
         <SingleLogoutService
             Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
-            Location="' . htmlspecialchars($metadata->getGenerated('SingleLogoutService', 'saml20-idp-hosted')) . '"
-            ResponseLocation="' . htmlspecialchars($metadata->getGenerated('SingleLogoutService', 'saml20-idp-hosted')) . '"
+            Location="' . htmlspecialchars($urlSLO) . '"
+            ResponseLocation="' . htmlspecialchars($urlSLOr) . '"
             />
 
         
diff --git a/www/saml2/idp/xajax_js/xajax.js b/www/saml2/idp/xajax_js/xajax.js
new file mode 100644
index 0000000000000000000000000000000000000000..0dca8f52005bc720b13eadb61526510fba7f0051
--- /dev/null
+++ b/www/saml2/idp/xajax_js/xajax.js
@@ -0,0 +1,172 @@
+/* xajax Javascript library :: version 0.2.4 */
+Array.prototype.containsValue=function(valueToCheck){for(var i=0;i<this.length;i++){if(this[i]==valueToCheck)return true;}
+return false;}
+function Xajax(){this.DebugMessage=function(text){if(text.length > 1000)text=text.substr(0,1000)+"...\n[long response]\n...";try{if(this.debugWindow==undefined||this.debugWindow.closed==true){this.debugWindow=window.open('about:blank','xajax-debug','width=800,height=600,scrollbars=1,resizable,status');this.debugWindow.document.write('<html><head><title>Xajax debug output</title></head><body><h2>Xajax debug output</h2><div id="debugTag"></div></body></html>');}
+text=text.replace(/&/g,"&amp;")
+text=text.replace(/</g,"&lt;")
+text=text.replace(/>/g,"&gt;")
+debugTag=this.debugWindow.document.getElementById('debugTag');debugTag.innerHTML=('<b>'+(new Date()).toString()+'</b>: '+text+'<hr/>')+debugTag.innerHTML;}catch(e){alert("Xajax Debug:\n "+text);}
+};this.workId='xajaxWork'+new Date().getTime();this.depth=0;this.responseErrorsForAlert=["400","401","402","403","404","500","501","502","503"];this.getRequestObject=function(){if(xajaxDebug)this.DebugMessage("Initializing Request Object..");var req=null;if(typeof XMLHttpRequest!="undefined")
+req=new XMLHttpRequest();if(!req&&typeof ActiveXObject!="undefined"){try{req=new ActiveXObject("Msxml2.XMLHTTP");}
+catch(e){try{req=new ActiveXObject("Microsoft.XMLHTTP");}
+catch(e2){try{req=new ActiveXObject("Msxml2.XMLHTTP.4.0");}
+catch(e3){req=null;}
+}
+}
+}
+if(!req&&window.createRequest)
+req=window.createRequest();if(!req)this.DebugMessage("Request Object Instantiation failed.");return req;}
+this.$=function(sId){if(!sId){return null;}
+var returnObj=document.getElementById(sId);if(!returnObj&&document.all){returnObj=document.all[sId];}
+if(xajaxDebug&&!returnObj&&sId!=this.workId){this.DebugMessage("Element with the id \""+sId+"\" not found.");}
+return returnObj;}
+this.include=function(sFileName){var objHead=document.getElementsByTagName('head');var objScript=document.createElement('script');objScript.type='text/javascript';objScript.src=sFileName;objHead[0].appendChild(objScript);}
+this.stripOnPrefix=function(sEventName){sEventName=sEventName.toLowerCase();if(sEventName.indexOf('on')==0){sEventName=sEventName.replace(/on/,'');}
+return sEventName;}
+this.addOnPrefix=function(sEventName){sEventName=sEventName.toLowerCase();if(sEventName.indexOf('on')!=0){sEventName='on'+sEventName;}
+return sEventName;}
+this.addHandler=function(sElementId,sEvent,sFunctionName){if(window.addEventListener){sEvent=this.stripOnPrefix(sEvent);eval("this.$('"+sElementId+"').addEventListener('"+sEvent+"',"+sFunctionName+",false);");}
+else{sAltEvent=this.addOnPrefix(sEvent);eval("this.$('"+sElementId+"').attachEvent('"+sAltEvent+"',"+sFunctionName+",false);");}
+}
+this.removeHandler=function(sElementId,sEvent,sFunctionName){if(window.addEventListener){sEvent=this.stripOnPrefix(sEvent);eval("this.$('"+sElementId+"').removeEventListener('"+sEvent+"',"+sFunctionName+",false);");}
+else{sAltEvent=this.addOnPrefix(sEvent);eval("this.$('"+sElementId+"').detachEvent('"+sAltEvent+"',"+sFunctionName+",false);");}
+}
+this.create=function(sParentId,sTag,sId){var objParent=this.$(sParentId);objElement=document.createElement(sTag);objElement.setAttribute('id',sId);if(objParent)
+objParent.appendChild(objElement);}
+this.insert=function(sBeforeId,sTag,sId){var objSibling=this.$(sBeforeId);objElement=document.createElement(sTag);objElement.setAttribute('id',sId);objSibling.parentNode.insertBefore(objElement,objSibling);}
+this.insertAfter=function(sAfterId,sTag,sId){var objSibling=this.$(sAfterId);objElement=document.createElement(sTag);objElement.setAttribute('id',sId);objSibling.parentNode.insertBefore(objElement,objSibling.nextSibling);}
+this.getInput=function(sType,sName,sId){var Obj;if(!window.addEventListener){Obj=document.createElement('<input type="'+sType+'" id="'+sId+'" name="'+sName+'">');}
+else{Obj=document.createElement('input');Obj.setAttribute('type',sType);Obj.setAttribute('name',sName);Obj.setAttribute('id',sId);}
+return Obj;}
+this.createInput=function(sParentId,sType,sName,sId){var objParent=this.$(sParentId);var objElement=this.getInput(sType,sName,sId);if(objParent&&objElement)
+objParent.appendChild(objElement);}
+this.insertInput=function(sBeforeId,sType,sName,sId){var objSibling=this.$(sBeforeId);var objElement=this.getInput(sType,sName,sId);if(objElement&&objSibling&&objSibling.parentNode)
+objSibling.parentNode.insertBefore(objElement,objSibling);}
+this.insertInputAfter=function(sAfterId,sType,sName,sId){var objSibling=this.$(sAfterId);var objElement=this.getInput(sType,sName,sId);if(objElement&&objSibling&&objSibling.parentNode){objSibling.parentNode.insertBefore(objElement,objSibling.nextSibling);}
+}
+this.remove=function(sId){objElement=this.$(sId);if(objElement&&objElement.parentNode&&objElement.parentNode.removeChild){objElement.parentNode.removeChild(objElement);}
+}
+this.replace=function(sId,sAttribute,sSearch,sReplace){var bFunction=false;if(sAttribute=="innerHTML")
+sSearch=this.getBrowserHTML(sSearch);eval("var txt=this.$('"+sId+"')."+sAttribute);if(typeof txt=="function"){txt=txt.toString();bFunction=true;}
+if(txt.indexOf(sSearch)>-1){var newTxt='';while(txt.indexOf(sSearch)>-1){x=txt.indexOf(sSearch)+sSearch.length+1;newTxt+=txt.substr(0,x).replace(sSearch,sReplace);txt=txt.substr(x,txt.length-x);}
+newTxt+=txt;if(bFunction){eval('this.$("'+sId+'").'+sAttribute+'=newTxt;');}
+else if(this.willChange(sId,sAttribute,newTxt)){eval('this.$("'+sId+'").'+sAttribute+'=newTxt;');}
+}
+}
+this.getFormValues=function(frm){var objForm;var submitDisabledElements=false;if(arguments.length > 1&&arguments[1]==true)
+submitDisabledElements=true;var prefix="";if(arguments.length > 2)
+prefix=arguments[2];if(typeof(frm)=="string")
+objForm=this.$(frm);else
+objForm=frm;var sXml="<xjxquery><q>";if(objForm&&objForm.tagName=='FORM'){var formElements=objForm.elements;for(var i=0;i < formElements.length;i++){if(!formElements[i].name)
+continue;if(formElements[i].name.substring(0,prefix.length)!=prefix)
+continue;if(formElements[i].type&&(formElements[i].type=='radio'||formElements[i].type=='checkbox')&&formElements[i].checked==false)
+continue;if(formElements[i].disabled&&formElements[i].disabled==true&&submitDisabledElements==false)
+continue;var name=formElements[i].name;if(name){if(sXml!='<xjxquery><q>')
+sXml+='&';if(formElements[i].type=='select-multiple'){for(var j=0;j < formElements[i].length;j++){if(formElements[i].options[j].selected==true)
+sXml+=name+"="+encodeURIComponent(formElements[i].options[j].value)+"&";}
+}
+else{sXml+=name+"="+encodeURIComponent(formElements[i].value);}
+}
+}
+}
+sXml+="</q></xjxquery>";return sXml;}
+this.objectToXML=function(obj){var sXml="<xjxobj>";for(i in obj){try{if(i=='constructor')
+continue;if(obj[i]&&typeof(obj[i])=='function')
+continue;var key=i;var value=obj[i];if(value&&typeof(value)=="object"&&this.depth <=50){this.depth++;value=this.objectToXML(value);this.depth--;}
+sXml+="<e><k>"+key+"</k><v>"+value+"</v></e>";}
+catch(e){if(xajaxDebug)this.DebugMessage(e.name+": "+e.message);}
+}
+sXml+="</xjxobj>";return sXml;}
+this._nodeToObject=function(node){if(node.nodeName=='#cdata-section'){var data="";for(var j=0;j<node.parentNode.childNodes.length;j++){data+=node.parentNode.childNodes[j].data;}
+return data;}
+else if(node.nodeName=='xjxobj'){var data=new Array();for(var j=0;j<node.childNodes.length;j++){var child=node.childNodes[j];var key;var value;if(child.nodeName=='e'){for(var k=0;k<child.childNodes.length;k++){if(child.childNodes[k].nodeName=='k'){key=child.childNodes[k].firstChild.data;}
+else if(child.childNodes[k].nodeName=='v'){value=this._nodeToObject(child.childNodes[k].firstChild);}
+}
+if(key!=null&&value!=null){data[key]=value;key=value=null;}
+}
+}
+return data;}
+}
+this.loadingFunction=function(){};this.doneLoadingFunction=function(){};var loadingTimeout;this.call=function(sFunction,aArgs,sRequestType){var i,r,postData;if(document.body&&xajaxWaitCursor)
+document.body.style.cursor='wait';if(xajaxStatusMessages==true)window.status='Sending Request...';clearTimeout(loadingTimeout);loadingTimeout=setTimeout("xajax.loadingFunction();",400);if(xajaxDebug)this.DebugMessage("Starting xajax...");if(sRequestType==null){var xajaxRequestType=xajaxDefinedPost;}
+else{var xajaxRequestType=sRequestType;}
+var uri=xajaxRequestUri;var value;switch(xajaxRequestType){case xajaxDefinedGet:{var uriGet=uri.indexOf("?")==-1?"?xajax="+encodeURIComponent(sFunction):"&xajax="+encodeURIComponent(sFunction);if(aArgs){for(i=0;i<aArgs.length;i++){value=aArgs[i];if(typeof(value)=="object")
+value=this.objectToXML(value);uriGet+="&xajaxargs[]="+encodeURIComponent(value);}
+}
+uriGet+="&xajaxr="+new Date().getTime();uri+=uriGet;postData=null;}break;case xajaxDefinedPost:{postData="xajax="+encodeURIComponent(sFunction);postData+="&xajaxr="+new Date().getTime();if(aArgs){for(i=0;i <aArgs.length;i++){value=aArgs[i];if(typeof(value)=="object")
+value=this.objectToXML(value);postData=postData+"&xajaxargs[]="+encodeURIComponent(value);}
+}
+}break;default:
+alert("Illegal request type: "+xajaxRequestType);return false;break;}
+r=this.getRequestObject();if(!r)return false;r.open(xajaxRequestType==xajaxDefinedGet?"GET":"POST",uri,true);if(xajaxRequestType==xajaxDefinedPost){try{r.setRequestHeader("Method","POST "+uri+" HTTP/1.1");r.setRequestHeader("Content-Type","application/x-www-form-urlencoded");}
+catch(e){alert("Your browser does not appear to  support asynchronous requests using POST.");return false;}
+}
+r.onreadystatechange=function(){if(r.readyState!=4)
+return;if(r.status==200){if(xajaxDebug)xajax.DebugMessage("Received:\n"+r.responseText);if(r.responseXML&&r.responseXML.documentElement)
+xajax.processResponse(r.responseXML);else{var errorString="Error: the XML response that was returned from the server is invalid.";errorString+="\nReceived:\n"+r.responseText;trimmedResponseText=r.responseText.replace(/^\s+/g,"");trimmedResponseText=trimmedResponseText.replace(/\s+$/g,"");if(trimmedResponseText!=r.responseText)
+errorString+="\nYou have whitespace in your response.";alert(errorString);document.body.style.cursor='default';if(xajaxStatusMessages==true)window.status='Invalid XML response error';}
+}
+else{if(xajax.responseErrorsForAlert.containsValue(r.status)){var errorString="Error: the server returned the following HTTP status: "+r.status;errorString+="\nReceived:\n"+r.responseText;alert(errorString);}
+document.body.style.cursor='default';if(xajaxStatusMessages==true)window.status='Invalid XML response error';}
+delete r;r=null;}
+if(xajaxDebug)this.DebugMessage("Calling "+sFunction+" uri="+uri+" (post:"+postData+")");r.send(postData);if(xajaxStatusMessages==true)window.status='Waiting for data...';delete r;return true;}
+this.getBrowserHTML=function(html){tmpXajax=this.$(this.workId);if(!tmpXajax){tmpXajax=document.createElement("div");tmpXajax.setAttribute('id',this.workId);tmpXajax.style.display="none";tmpXajax.style.visibility="hidden";document.body.appendChild(tmpXajax);}
+tmpXajax.innerHTML=html;var browserHTML=tmpXajax.innerHTML;tmpXajax.innerHTML='';return browserHTML;}
+this.willChange=function(element,attribute,newData){if(!document.body){return true;}
+if(attribute=="innerHTML"){newData=this.getBrowserHTML(newData);}
+elementObject=this.$(element);if(elementObject){var oldData;eval("oldData=this.$('"+element+"')."+attribute);if(newData!==oldData)
+return true;}
+return false;}
+this.viewSource=function(){return "<html>"+document.getElementsByTagName("HTML")[0].innerHTML+"</html>";}
+this.processResponse=function(xml){clearTimeout(loadingTimeout);this.doneLoadingFunction();if(xajaxStatusMessages==true)window.status='Processing...';var tmpXajax=null;xml=xml.documentElement;if(xml==null)
+return;var skipCommands=0;for(var i=0;i<xml.childNodes.length;i++){if(skipCommands > 0){skipCommands--;continue;}
+if(xml.childNodes[i].nodeName=="cmd"){var cmd;var id;var property;var data;var search;var type;var before;var objElement=null;for(var j=0;j<xml.childNodes[i].attributes.length;j++){if(xml.childNodes[i].attributes[j].name=="n"){cmd=xml.childNodes[i].attributes[j].value;}
+else if(xml.childNodes[i].attributes[j].name=="t"){id=xml.childNodes[i].attributes[j].value;}
+else if(xml.childNodes[i].attributes[j].name=="p"){property=xml.childNodes[i].attributes[j].value;}
+else if(xml.childNodes[i].attributes[j].name=="c"){type=xml.childNodes[i].attributes[j].value;}
+}
+if(xml.childNodes[i].childNodes.length > 1&&xml.childNodes[i].firstChild.nodeName=="#cdata-section"){data="";for(var j=0;j<xml.childNodes[i].childNodes.length;j++){data+=xml.childNodes[i].childNodes[j].data;}
+}
+else if(xml.childNodes[i].firstChild&&xml.childNodes[i].firstChild.nodeName=='xjxobj'){data=this._nodeToObject(xml.childNodes[i].firstChild);objElement="XJX_SKIP";}
+else if(xml.childNodes[i].childNodes.length > 1){for(var j=0;j<xml.childNodes[i].childNodes.length;j++){if(xml.childNodes[i].childNodes[j].childNodes.length > 1&&xml.childNodes[i].childNodes[j].firstChild.nodeName=="#cdata-section"){var internalData="";for(var k=0;k<xml.childNodes[i].childNodes[j].childNodes.length;k++){internalData+=xml.childNodes[i].childNodes[j].childNodes[k].nodeValue;}
+}else{var internalData=xml.childNodes[i].childNodes[j].firstChild.nodeValue;}
+if(xml.childNodes[i].childNodes[j].nodeName=="s"){search=internalData;}
+if(xml.childNodes[i].childNodes[j].nodeName=="r"){data=internalData;}
+}
+}
+else if(xml.childNodes[i].firstChild)
+data=xml.childNodes[i].firstChild.nodeValue;else
+data="";if(objElement!="XJX_SKIP")objElement=this.$(id);var cmdFullname;try{if(cmd=="cc"){cmdFullname="addConfirmCommands";var confirmResult=confirm(data);if(!confirmResult){skipCommands=id;}
+}
+if(cmd=="al"){cmdFullname="addAlert";alert(data);}
+else if(cmd=="js"){cmdFullname="addScript/addRedirect";eval(data);}
+else if(cmd=="jc"){cmdFullname="addScriptCall";var scr=id+'(';if(data[0]!=null){scr+='data[0]';for(var l=1;l<data.length;l++){scr+=',data['+l+']';}
+}
+scr+=');';eval(scr);}
+else if(cmd=="in"){cmdFullname="addIncludeScript";this.include(data);}
+else if(cmd=="as"){cmdFullname="addAssign/addClear";if(this.willChange(id,property,data)){eval("objElement."+property+"=data;");}
+}
+else if(cmd=="ap"){cmdFullname="addAppend";eval("objElement."+property+"+=data;");}
+else if(cmd=="pp"){cmdFullname="addPrepend";eval("objElement."+property+"=data+objElement."+property);}
+else if(cmd=="rp"){cmdFullname="addReplace";this.replace(id,property,search,data)
+}
+else if(cmd=="rm"){cmdFullname="addRemove";this.remove(id);}
+else if(cmd=="ce"){cmdFullname="addCreate";this.create(id,data,property);}
+else if(cmd=="ie"){cmdFullname="addInsert";this.insert(id,data,property);}
+else if(cmd=="ia"){cmdFullname="addInsertAfter";this.insertAfter(id,data,property);}
+else if(cmd=="ci"){cmdFullname="addCreateInput";this.createInput(id,type,data,property);}
+else if(cmd=="ii"){cmdFullname="addInsertInput";this.insertInput(id,type,data,property);}
+else if(cmd=="iia"){cmdFullname="addInsertInputAfter";this.insertInputAfter(id,type,data,property);}
+else if(cmd=="ev"){cmdFullname="addEvent";property=this.addOnPrefix(property);eval("this.$('"+id+"')."+property+"= function(){"+data+";}");}
+else if(cmd=="ah"){cmdFullname="addHandler";this.addHandler(id,property,data);}
+else if(cmd=="rh"){cmdFullname="addRemoveHandler";this.removeHandler(id,property,data);}
+}
+catch(e){if(xajaxDebug)
+alert("While trying to '"+cmdFullname+"' (command number "+i+"), the following error occured:\n"
++e.name+": "+e.message+"\n"
++(id&&!objElement?"Object with id='"+id+"' wasn't found.\n":""));}
+delete objElement;delete cmd;delete cmdFullname;delete id;delete property;delete search;delete data;delete type;delete before;delete internalData;delete j;delete k;}
+}
+delete xml;delete i;document.body.style.cursor='default';if(xajaxStatusMessages==true)window.status='Done';}
+}
+var xajax=new Xajax();xajaxLoaded=true;
\ No newline at end of file
diff --git a/www/saml2/idp/xajax_js/xajax_uncompressed.js b/www/saml2/idp/xajax_js/xajax_uncompressed.js
new file mode 100644
index 0000000000000000000000000000000000000000..df51ccdeb27dad60cbe2847058eaa683d916a7ec
--- /dev/null
+++ b/www/saml2/idp/xajax_js/xajax_uncompressed.js
@@ -0,0 +1,795 @@
+/* xajax Javascript library :: version 0.2.4 */
+
+Array.prototype.containsValue = function(valueToCheck)
+{
+	for (var i=0;i<this.length;i++) {
+		if (this[i] == valueToCheck) return true;
+	}
+	return false;
+}
+
+function Xajax()
+{
+	this.DebugMessage = function(text)
+	{
+		if (text.length > 1000) text = text.substr(0,1000)+"...\n[long response]\n...";
+		try {
+			if (this.debugWindow == undefined || this.debugWindow.closed == true) {
+				this.debugWindow = window.open('about:blank', 'xajax-debug', 'width=800,height=600,scrollbars=1,resizable,status');
+				this.debugWindow.document.write('<html><head><title>Xajax debug output</title></head><body><h2>Xajax debug output</h2><div id="debugTag"></div></body></html>');
+			}
+			text = text.replace(/&/g, "&amp;")
+			text = text.replace(/</g, "&lt;")
+			text = text.replace(/>/g, "&gt;")
+			debugTag = this.debugWindow.document.getElementById('debugTag');
+			debugTag.innerHTML = ('<b>'+(new Date()).toString()+'</b>: ' + text + '<hr/>') + debugTag.innerHTML;
+		} catch (e) {
+			alert("Xajax Debug:\n " + text);
+		}
+	};
+	
+	this.workId = 'xajaxWork'+ new Date().getTime();
+	this.depth = 0;
+	this.responseErrorsForAlert = ["400","401","402","403","404","500","501","502","503"];
+	
+	//Get the XMLHttpRequest Object
+	this.getRequestObject = function()
+	{
+		if (xajaxDebug) this.DebugMessage("Initializing Request Object..");
+		var req = null;
+		if (typeof XMLHttpRequest != "undefined")
+			req = new XMLHttpRequest();
+		if (!req && typeof ActiveXObject != "undefined")
+		{
+			try
+			{
+				req=new ActiveXObject("Msxml2.XMLHTTP");
+			}
+			catch (e)
+			{
+				try
+				{
+					req=new ActiveXObject("Microsoft.XMLHTTP");
+				}
+				catch (e2)
+				{
+					try {
+						req=new ActiveXObject("Msxml2.XMLHTTP.4.0");
+					}
+					catch (e3)
+					{
+						req=null;
+					}
+				}
+			}
+		}
+		if(!req && window.createRequest)
+			req = window.createRequest();
+		
+		if (!req) this.DebugMessage("Request Object Instantiation failed.");
+			
+		return req;
+	}
+
+	// xajax.$() is shorthand for document.getElementById()
+	this.$ = function(sId)
+	{
+		if (!sId) {
+			return null;
+		}
+		var returnObj = document.getElementById(sId);
+		if (!returnObj && document.all) {
+			returnObj = document.all[sId];
+		}
+		if (xajaxDebug && !returnObj && sId != this.workId) {
+			this.DebugMessage("Element with the id \"" + sId + "\" not found.");
+		}
+		return returnObj;
+	}
+	
+	// xajax.include(sFileName) dynamically includes an external javascript file
+	this.include = function(sFileName)
+	{
+		var objHead = document.getElementsByTagName('head');
+		var objScript = document.createElement('script');
+		objScript.type = 'text/javascript';
+		objScript.src = sFileName;
+		objHead[0].appendChild(objScript);
+	}
+	
+	this.stripOnPrefix = function(sEventName)
+	{
+		sEventName = sEventName.toLowerCase();
+		if (sEventName.indexOf('on') == 0)
+		{
+			sEventName = sEventName.replace(/on/,'');
+		}
+		
+		return sEventName;
+	}
+	
+	this.addOnPrefix = function(sEventName)
+	{
+		sEventName = sEventName.toLowerCase();
+		if (sEventName.indexOf('on') != 0)
+		{
+			sEventName = 'on' + sEventName;
+		}
+		
+		return sEventName;
+	}
+	
+	// xajax.addHandler adds an event handler to an element
+	this.addHandler = function(sElementId, sEvent, sFunctionName)
+	{
+		if (window.addEventListener)
+		{
+			sEvent = this.stripOnPrefix(sEvent);
+			eval("this.$('"+sElementId+"').addEventListener('"+sEvent+"',"+sFunctionName+",false);");
+		}
+		else
+		{
+			sAltEvent = this.addOnPrefix(sEvent);
+			eval("this.$('"+sElementId+"').attachEvent('"+sAltEvent+"',"+sFunctionName+",false);");
+		}
+	}
+	
+	// xajax.removeHandler removes an event handler from an element
+	this.removeHandler = function(sElementId, sEvent, sFunctionName)
+	{
+		if (window.addEventListener)
+		{
+			sEvent = this.stripOnPrefix(sEvent);
+			eval("this.$('"+sElementId+"').removeEventListener('"+sEvent+"',"+sFunctionName+",false);");
+		}
+		else
+		{
+			sAltEvent = this.addOnPrefix(sEvent);
+			eval("this.$('"+sElementId+"').detachEvent('"+sAltEvent+"',"+sFunctionName+",false);");
+		}
+	}
+	
+	// xajax.create creates a new child node under a parent
+	this.create = function(sParentId, sTag, sId)
+	{
+		var objParent = this.$(sParentId);
+		objElement = document.createElement(sTag);
+		objElement.setAttribute('id',sId);
+		if (objParent)
+			objParent.appendChild(objElement);
+	}
+	
+	// xajax.insert inserts a new node before another node
+	this.insert = function(sBeforeId, sTag, sId)
+	{
+		var objSibling = this.$(sBeforeId);
+		objElement = document.createElement(sTag);
+		objElement.setAttribute('id',sId);
+		objSibling.parentNode.insertBefore(objElement, objSibling);
+	}
+
+	// xajax.insertAfter inserts a new node after another node
+	this.insertAfter = function(sAfterId, sTag, sId)
+	{
+		var objSibling = this.$(sAfterId);
+		objElement = document.createElement(sTag);
+		objElement.setAttribute('id',sId);
+		objSibling.parentNode.insertBefore(objElement, objSibling.nextSibling);
+	}
+	
+	this.getInput = function(sType, sName, sId)
+	{
+		var Obj;
+		if (!window.addEventListener)
+		{
+			Obj = document.createElement('<input type="'+sType+'" id="'+sId+'" name="'+sName+'">');
+		}
+		else
+		{
+			Obj = document.createElement('input');
+			Obj.setAttribute('type',sType);
+			Obj.setAttribute('name',sName);
+			Obj.setAttribute('id',sId);
+		}
+		return Obj;
+	}
+	
+	// xajax.createInput creates a new input node under a parent
+	this.createInput = function(sParentId, sType, sName, sId)
+	{
+		var objParent = this.$(sParentId);
+		var objElement = this.getInput(sType, sName, sId);
+		if (objParent && objElement)
+			objParent.appendChild(objElement);
+	}
+	
+	// xajax.insertInput creates a new input node before another node
+	this.insertInput = function(sBeforeId, sType, sName, sId)
+	{
+		var objSibling = this.$(sBeforeId);
+		var objElement = this.getInput(sType, sName, sId);
+		if (objElement && objSibling && objSibling.parentNode)
+			objSibling.parentNode.insertBefore(objElement, objSibling);
+	}
+
+	// xajax.insertInputAfter creates a new input node after another node
+	this.insertInputAfter = function(sAfterId, sType, sName, sId)
+	{
+		var objSibling = this.$(sAfterId);
+		var objElement = this.getInput(sType, sName, sId);
+		if (objElement && objSibling && objSibling.parentNode) {
+			objSibling.parentNode.insertBefore(objElement, objSibling.nextSibling);
+		}
+	}
+		
+	// xajax.remove deletes an element
+	this.remove = function(sId)
+	{
+		objElement = this.$(sId);
+		if (objElement && objElement.parentNode && objElement.parentNode.removeChild)
+		{
+			objElement.parentNode.removeChild(objElement);
+		}
+	}
+	
+	//xajax.replace searches for text in an attribute of an element and replaces it
+	//with a different text
+	this.replace = function(sId,sAttribute,sSearch,sReplace)
+	{
+		var bFunction = false;
+		
+		if (sAttribute == "innerHTML")
+			sSearch = this.getBrowserHTML(sSearch);
+		
+		eval("var txt=this.$('"+sId+"')."+sAttribute);
+		if (typeof txt == "function")
+        {
+            txt = txt.toString();
+            bFunction = true;
+        }
+		if (txt.indexOf(sSearch)>-1)
+		{
+			var newTxt = '';
+			while (txt.indexOf(sSearch) > -1)
+			{
+				x = txt.indexOf(sSearch)+sSearch.length+1;
+				newTxt += txt.substr(0,x).replace(sSearch,sReplace);
+				txt = txt.substr(x,txt.length-x);
+			}
+			newTxt += txt;
+			if (bFunction)
+			{
+				eval('this.$("'+sId+'").'+sAttribute+'=newTxt;');
+			}
+			else if (this.willChange(sId,sAttribute,newTxt))
+			{
+				eval('this.$("'+sId+'").'+sAttribute+'=newTxt;');
+			}
+		}
+	}
+	
+	// xajax.getFormValues() builds a query string XML message from the elements of a form object
+	// * The first argument is the id of the form
+	// * The second argument (optional) can be set to true if you want to submit disabled elements
+	// * The third argument (optional) allows you to specify a string prefix that a form element
+	//   name must contain if you want that element to be submitted
+	this.getFormValues = function(frm)
+	{
+		var objForm;
+		var submitDisabledElements = false;
+		if (arguments.length > 1 && arguments[1] == true)
+			submitDisabledElements = true;
+		var prefix="";
+		if(arguments.length > 2)
+			prefix = arguments[2];
+		
+		if (typeof(frm) == "string")
+			objForm = this.$(frm);
+		else
+			objForm = frm;
+		var sXml = "<xjxquery><q>";
+		if (objForm && objForm.tagName == 'FORM')
+		{
+			var formElements = objForm.elements;
+			for( var i=0; i < formElements.length; i++)
+			{
+				if (!formElements[i].name)
+					continue;
+				if (formElements[i].name.substring(0, prefix.length) != prefix)
+					continue;
+				if (formElements[i].type && (formElements[i].type == 'radio' || formElements[i].type == 'checkbox') && formElements[i].checked == false)
+					continue;
+				if (formElements[i].disabled && formElements[i].disabled == true && submitDisabledElements == false)
+					continue;
+				var name = formElements[i].name;
+				if (name)
+				{
+					if (sXml != '<xjxquery><q>')
+						sXml += '&';
+					if(formElements[i].type=='select-multiple')
+					{
+						for (var j = 0; j < formElements[i].length; j++)
+						{
+							if (formElements[i].options[j].selected == true)
+								sXml += name+"="+encodeURIComponent(formElements[i].options[j].value)+"&";
+						}
+					}
+					else
+					{
+						sXml += name+"="+encodeURIComponent(formElements[i].value);
+					}
+				} 
+			}
+		}
+		
+		sXml +="</q></xjxquery>";
+		
+		return sXml;
+	}
+	
+	// Generates an XML message that xajax can understand from a javascript object
+	this.objectToXML = function(obj)
+	{
+		var sXml = "<xjxobj>";
+		for (i in obj)
+		{
+			try
+			{
+				if (i == 'constructor')
+					continue;
+				if (obj[i] && typeof(obj[i]) == 'function')
+					continue;
+					
+				var key = i;
+				var value = obj[i];
+				if (value && typeof(value)=="object" && this.depth <= 50)
+				{
+					this.depth++;
+					value = this.objectToXML(value);
+					this.depth--;
+				}
+				
+				sXml += "<e><k>"+key+"</k><v>"+value+"</v></e>";
+				
+			}
+			catch(e)
+			{
+				if (xajaxDebug) this.DebugMessage(e.name+": "+e.message);
+			}
+		}
+		sXml += "</xjxobj>";
+	
+		return sXml;
+	}
+
+	// unserializes data structure from xajaxResponse::_buildObjXml()
+	this._nodeToObject = function(node) {
+		// parentNode here is weird, have to tune
+		if (node.nodeName == '#cdata-section') {
+			var data = "";
+			for (var j=0; j<node.parentNode.childNodes.length; j++) {
+				data += node.parentNode.childNodes[j].data;
+			}
+			return data;
+		}
+		else if (node.nodeName == 'xjxobj') {
+			var data = new Array();
+			for (var j=0; j<node.childNodes.length; j++) {
+				var child = node.childNodes[j];
+				var key;
+				var value;
+				if (child.nodeName == 'e') {
+					for (var k=0; k<child.childNodes.length; k++) {
+						if (child.childNodes[k].nodeName == 'k') {
+							key = child.childNodes[k].firstChild.data;
+						}
+						else if (child.childNodes[k].nodeName == 'v') {
+							value = this._nodeToObject(child.childNodes[k].firstChild);
+						}
+					}
+					if (key != null && value != null) {
+						data[key] = value;
+						key = value = null;
+					}
+				}
+			}
+			return data;
+		}		
+	}
+
+	this.loadingFunction = function(){};
+	this.doneLoadingFunction = function(){};
+	var loadingTimeout;
+
+	// Sends a XMLHttpRequest to call the specified PHP function on the server
+	// * sRequestType is optional -- defaults to POST
+	this.call = function(sFunction, aArgs, sRequestType)
+	{
+		var i,r,postData;
+		if (document.body && xajaxWaitCursor)
+			document.body.style.cursor = 'wait';
+		if (xajaxStatusMessages == true) window.status = 'Sending Request...';
+		clearTimeout(loadingTimeout);
+		loadingTimeout = setTimeout("xajax.loadingFunction();",400);
+		if (xajaxDebug) this.DebugMessage("Starting xajax...");
+		if (sRequestType == null) {
+		   var xajaxRequestType = xajaxDefinedPost;
+		}
+		else {
+			var xajaxRequestType = sRequestType;
+		}
+		var uri = xajaxRequestUri;
+		var value;
+		switch(xajaxRequestType)
+		{
+			case xajaxDefinedGet:{
+				var uriGet = uri.indexOf("?")==-1?"?xajax="+encodeURIComponent(sFunction):"&xajax="+encodeURIComponent(sFunction);
+				if (aArgs) {
+					for (i = 0; i<aArgs.length; i++)
+					{
+						value = aArgs[i];
+						if (typeof(value)=="object")
+							value = this.objectToXML(value);
+						uriGet += "&xajaxargs[]="+encodeURIComponent(value);
+					}
+				}
+				uriGet += "&xajaxr=" + new Date().getTime();
+				uri += uriGet;
+				postData = null;
+				} break;
+			case xajaxDefinedPost:{
+				postData = "xajax="+encodeURIComponent(sFunction);
+				postData += "&xajaxr="+new Date().getTime();
+				if (aArgs) {
+					for (i = 0; i <aArgs.length; i++)
+					{
+						value = aArgs[i];
+						if (typeof(value)=="object")
+							value = this.objectToXML(value);
+						postData = postData+"&xajaxargs[]="+encodeURIComponent(value);
+					}
+				}
+				} break;
+			default:
+				alert("Illegal request type: " + xajaxRequestType); return false; break;
+		}
+		r = this.getRequestObject();
+		if (!r) return false;
+		r.open(xajaxRequestType==xajaxDefinedGet?"GET":"POST", uri, true);
+		if (xajaxRequestType == xajaxDefinedPost)
+		{
+			try
+			{
+				r.setRequestHeader("Method", "POST " + uri + " HTTP/1.1");
+				r.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+			}
+			catch(e)
+			{
+				alert("Your browser does not appear to  support asynchronous requests using POST.");
+				return false;
+			}
+		}
+		r.onreadystatechange = function()
+		{
+			if (r.readyState != 4)
+				return;
+			
+			if (r.status==200)
+			{
+				if (xajaxDebug) xajax.DebugMessage("Received:\n" + r.responseText);
+				if (r.responseXML && r.responseXML.documentElement)
+					xajax.processResponse(r.responseXML);
+				else {
+					var errorString = "Error: the XML response that was returned from the server is invalid.";
+					errorString += "\nReceived:\n" + r.responseText;
+					trimmedResponseText = r.responseText.replace( /^\s+/g, "" );// strip leading space
+					trimmedResponseText = trimmedResponseText.replace( /\s+$/g, "" );// strip trailing
+					if (trimmedResponseText != r.responseText)
+						errorString += "\nYou have whitespace in your response.";
+					alert(errorString);
+					document.body.style.cursor = 'default';
+					if (xajaxStatusMessages == true) window.status = 'Invalid XML response error';				
+				}
+			}
+			else {
+				if (xajax.responseErrorsForAlert.containsValue(r.status)) {
+					var errorString = "Error: the server returned the following HTTP status: " + r.status;
+					errorString += "\nReceived:\n" + r.responseText;
+					alert(errorString);
+				}
+				document.body.style.cursor = 'default';
+				if (xajaxStatusMessages == true) window.status = 'Invalid XML response error';								
+			}
+			
+			delete r;
+			r = null;
+		}
+		if (xajaxDebug) this.DebugMessage("Calling "+sFunction +" uri="+uri+" (post:"+ postData +")");
+		r.send(postData);
+		if (xajaxStatusMessages == true) window.status = 'Waiting for data...';
+		delete r;
+		return true;
+	}
+	
+	//Gets the text as it would be if it were being retrieved from
+	//the innerHTML property in the current browser
+	this.getBrowserHTML = function(html)
+	{
+		tmpXajax = this.$(this.workId);
+		if (!tmpXajax)
+		{
+			tmpXajax = document.createElement("div");
+			tmpXajax.setAttribute('id',this.workId);
+			tmpXajax.style.display = "none";
+			tmpXajax.style.visibility = "hidden";
+			document.body.appendChild(tmpXajax);
+		}
+		tmpXajax.innerHTML = html;
+		var browserHTML = tmpXajax.innerHTML;
+		tmpXajax.innerHTML = '';	
+		
+		return browserHTML;
+	}
+	
+	// Tests if the new Data is the same as the extant data
+	this.willChange = function(element, attribute, newData)
+	{
+		if (!document.body)
+		{
+			return true;
+		}
+		if (attribute == "innerHTML")
+		{
+			newData = this.getBrowserHTML(newData);
+		}
+		elementObject = this.$(element);
+		if (elementObject) {
+			var oldData;		
+			eval("oldData=this.$('"+element+"')."+attribute);
+			if (newData !== oldData)
+				return true;
+		}
+
+		return false;
+	}
+	
+	//Returns the source code of the page after it's been modified by xajax
+	this.viewSource = function()
+	{
+		return "<html>"+document.getElementsByTagName("HTML")[0].innerHTML+"</html>";
+	}
+	
+	//Process XML xajaxResponses returned from the request
+	this.processResponse = function(xml)
+	{
+		clearTimeout(loadingTimeout);
+		this.doneLoadingFunction();
+		if (xajaxStatusMessages == true) window.status = 'Processing...';
+		var tmpXajax = null;
+		xml = xml.documentElement;
+		if (xml == null)
+			return;
+		
+		var skipCommands = 0;
+		for (var i=0; i<xml.childNodes.length; i++)
+		{
+			if (skipCommands > 0) {
+				skipCommands--;
+				continue;
+			}
+			if (xml.childNodes[i].nodeName == "cmd")
+			{
+				var cmd;
+				var id;
+				var property;
+				var data;
+				var search;
+				var type;
+				var before;
+				var objElement = null;
+
+				for (var j=0; j<xml.childNodes[i].attributes.length; j++)
+				{
+					if (xml.childNodes[i].attributes[j].name == "n")
+					{
+						cmd = xml.childNodes[i].attributes[j].value;
+					}
+					else if (xml.childNodes[i].attributes[j].name == "t")
+					{
+						id = xml.childNodes[i].attributes[j].value;
+					}
+					else if (xml.childNodes[i].attributes[j].name == "p")
+					{
+						property = xml.childNodes[i].attributes[j].value;
+					}
+					else if (xml.childNodes[i].attributes[j].name == "c")
+					{
+						type = xml.childNodes[i].attributes[j].value;
+					}
+				}
+				if (xml.childNodes[i].childNodes.length > 1 && xml.childNodes[i].firstChild.nodeName == "#cdata-section")
+				{
+					data = "";
+					for (var j=0; j<xml.childNodes[i].childNodes.length; j++)
+					{
+						data += xml.childNodes[i].childNodes[j].data;
+					}
+				}
+				else if (xml.childNodes[i].firstChild && xml.childNodes[i].firstChild.nodeName == 'xjxobj') {
+					data = this._nodeToObject(xml.childNodes[i].firstChild);
+					objElement = "XJX_SKIP";
+				}
+				else if (xml.childNodes[i].childNodes.length > 1)
+				{
+					for (var j=0; j<xml.childNodes[i].childNodes.length; j++)
+					{
+						if (xml.childNodes[i].childNodes[j].childNodes.length > 1 && xml.childNodes[i].childNodes[j].firstChild.nodeName == "#cdata-section")
+						{
+							var internalData = "";
+							for (var k=0; k<xml.childNodes[i].childNodes[j].childNodes.length;k++)
+							{
+								internalData+=xml.childNodes[i].childNodes[j].childNodes[k].nodeValue;
+							}
+						} else {
+							var internalData = xml.childNodes[i].childNodes[j].firstChild.nodeValue;
+						}
+					
+						if (xml.childNodes[i].childNodes[j].nodeName == "s")
+						{
+							search = internalData;
+						}
+						if (xml.childNodes[i].childNodes[j].nodeName == "r")
+						{
+							data = internalData;
+						}
+					}
+				}
+				else if (xml.childNodes[i].firstChild)
+					data = xml.childNodes[i].firstChild.nodeValue;
+				else
+					data = "";
+				
+				if (objElement != "XJX_SKIP") objElement = this.$(id);
+				var cmdFullname;
+				try
+				{
+					if (cmd=="cc") {
+						cmdFullname = "addConfirmCommands";
+						var confirmResult = confirm(data);
+						if (!confirmResult) {
+							skipCommands = id;
+						}
+					}
+					if (cmd=="al")
+					{
+						cmdFullname = "addAlert";
+						alert(data);
+					}
+					else if (cmd=="js")
+					{
+						cmdFullname = "addScript/addRedirect";
+						eval(data);
+					}
+					else if (cmd=="jc")
+					{
+						cmdFullname = "addScriptCall";
+						var scr = id + '(';
+						if (data[0] != null) {
+							scr += 'data[0]';
+							for (var l=1; l<data.length; l++) {
+								scr += ',data['+l+']';
+							}
+						}
+						scr += ');';
+						eval(scr);
+					}
+					else if (cmd=="in")
+					{
+						cmdFullname = "addIncludeScript";
+						this.include(data);
+					}
+					else if (cmd=="as")
+					{
+						cmdFullname = "addAssign/addClear";
+						if (this.willChange(id,property,data))
+						{
+							eval("objElement."+property+"=data;");
+						}
+					}
+					else if (cmd=="ap")
+					{
+						cmdFullname = "addAppend";
+						eval("objElement."+property+"+=data;");
+					}
+					else if (cmd=="pp")
+					{
+						cmdFullname = "addPrepend";
+						eval("objElement."+property+"=data+objElement."+property);
+					}
+					else if (cmd=="rp")
+					{
+						cmdFullname = "addReplace";
+						this.replace(id,property,search,data)
+					}
+					else if (cmd=="rm")
+					{
+						cmdFullname = "addRemove";
+						this.remove(id);
+					}
+					else if (cmd=="ce")
+					{
+						cmdFullname = "addCreate";
+						this.create(id,data,property);
+					}
+					else if (cmd=="ie")
+					{
+						cmdFullname = "addInsert";
+						this.insert(id,data,property);
+					}
+					else if (cmd=="ia")
+					{
+						cmdFullname = "addInsertAfter";
+						this.insertAfter(id,data,property);
+					}
+					else if (cmd=="ci")
+					{
+						cmdFullname = "addCreateInput";
+						this.createInput(id,type,data,property);
+					}
+					else if (cmd=="ii")
+					{
+						cmdFullname = "addInsertInput";
+						this.insertInput(id,type,data,property);
+					}
+					else if (cmd=="iia")
+					{
+						cmdFullname = "addInsertInputAfter";
+						this.insertInputAfter(id,type,data,property);
+					}
+					else if (cmd=="ev")
+					{
+						cmdFullname = "addEvent";
+						property = this.addOnPrefix(property);
+						eval("this.$('"+id+"')."+property+"= function(){"+data+";}");
+					}
+					else if (cmd=="ah")
+					{
+						cmdFullname = "addHandler";
+						this.addHandler(id, property, data);
+					}
+					else if (cmd=="rh")
+					{
+						cmdFullname = "addRemoveHandler";
+						this.removeHandler(id, property, data);
+					}
+				}
+				catch(e)
+				{
+					if (xajaxDebug)
+						alert("While trying to '"+cmdFullname+"' (command number "+i+"), the following error occured:\n"
+							+ e.name+": "+e.message+"\n"
+							+ (id&&!objElement?"Object with id='"+id+"' wasn't found.\n":""));
+				}
+				delete objElement;
+				delete cmd;
+				delete cmdFullname;
+				delete id;
+				delete property;
+				delete search;
+				delete data;
+				delete type;
+				delete before;
+				delete internalData;
+				delete j;
+				delete k;
+			}	
+		}
+		delete xml;
+		delete i;
+		document.body.style.cursor = 'default';
+		if (xajaxStatusMessages == true) window.status = 'Done';
+	}
+}
+
+var xajax = new Xajax();
+xajaxLoaded = true;