Index: t3lib/extjs/dataprovider/class.extdirect_dataprovider_state.php
===================================================================
--- t3lib/extjs/dataprovider/class.extdirect_dataprovider_state.php	(revision )
+++ t3lib/extjs/dataprovider/class.extdirect_dataprovider_state.php	(revision )
@@ -0,0 +1,86 @@
+<?php
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Steffen Kamper <steffen@typo3.org>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script 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 General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * ExtDirect DataProvider for State
+ */
+class extDirect_DataProvider_State {
+
+	/** @var extDirect_DataProvider_BackenduserSettings */
+	protected $userSettings;
+
+	/**
+	 * Constructor
+	 *
+	 * @return void
+	 */
+	public function __construct() {
+			//all data is saved in BE_USER->uc
+		$this->userSettings = t3lib_div::makeInstance('extDirect_DataProvider_BackenduserSettings');
+	}
+
+	/**
+	 * Gets state for given key
+	 *
+	 * @param  stdClass $parameter
+	 * @return array
+	 */
+	public function getState($parameter) {
+		$key = $parameter->params->key;
+		$data = $this->userSettings->get($key);
+
+		return array(
+			'success' => TRUE,
+			'data' =>  $data
+		);
+	}
+
+	/**
+	 * Save the state for a given key
+	 *
+	 * @param  stdClass $parameter
+	 * @return array
+	 */
+	public function setState($parameter) {
+		$key = $parameter->params->key;
+		$data = json_decode($parameter->params->data);
+
+		$this->userSettings->set($key . '.' . $data[0]->name, $data[0]->value);
+		return array(
+			'success' => TRUE,
+			'params' => $parameter
+		);
+	}
+
+
+}
+
+if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/extjs/dataprovider/class.extdirect_dataprovider_state.php'])) {
+	include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/extjs/dataprovider/class.extdirect_dataprovider_state.php']);
+}
+
+?>
\ No newline at end of file
Index: typo3/template.php
===================================================================
--- typo3/template.php	(revision 9758)
+++ typo3/template.php	(revision )
@@ -244,6 +244,8 @@
 	protected $pageRenderer;
 	protected $pageHeaderFooterTemplateFile = '';	// alternative template file
 
+	protected $extDirectStateProvider = FALSE;
+
 	/**
 	 * Whether flashmessages should be rendered or not
 	 *
@@ -337,6 +339,14 @@
 
 
 
+   /**
+	 * Sets inclusion of StateProvider
+	 *
+	 * @return void
+	 */
+	public function setExtDirectStateProvider() {
+		$this->extDirectStateProvider = TRUE;
+	}
 
 
 
@@ -346,7 +356,6 @@
 
 
 
-
 	/*****************************************
 	 *
 	 * EVALUATION FUNCTIONS
@@ -807,6 +816,14 @@
 		// add docstyles
 		$this->docStyle();
 
+	   if ($this->extDirectStateProvider) {
+			$this->pageRenderer->addJsFile(
+				$this->backPath . 'ajax.php?ajaxID=ExtDirect::getAPI&namespace=TYPO3.ExtDirectStateProvider',
+				NULL,
+				FALSE
+			);
+			$this->pageRenderer->addJsFile($this->backPath . '../t3lib/js/extjs/ExtDirect.StateProvider.js');
+		}
 
 			// add jsCode for overriding the console with a debug panel connection
 		$this->pageRenderer->addJsInlineCode(
Index: t3lib/js/extjs/ExtDirect.StateProvider.js
===================================================================
--- t3lib/js/extjs/ExtDirect.StateProvider.js	(revision )
+++ t3lib/js/extjs/ExtDirect.StateProvider.js	(revision )
@@ -0,0 +1,358 @@
+Ext.ns('TYPO3.state');
+
+/**
+ * Creates new ExtDirectProvider
+ * @constructor
+ * @param {Object} config Configuration object
+ * @author Jozef Sakalos
+ * @author Steffen Kamper
+ */
+// {{{
+TYPO3.state.ExtDirectProvider = function(config) {
+
+	this.addEvents(
+		/**
+		 * @event readsuccess
+		 * Fires after state has been successfully received from server and restored
+		 * @param {HttpProvider} this
+		 */
+			'readsuccess',
+		/**
+		 * @event readfailure
+		 * Fires in the case of an error when attempting to read state from server
+		 * @param {HttpProvider} this
+		 */
+			'readfailure',
+		/**
+		 * @event savesuccess
+		 * Fires after the state has been successfully saved to server
+		 * @param {HttpProvider} this
+		 */
+			'savesuccess',
+		/**
+		 * @event savefailure
+		 * Fires in the case of an error when attempting to save state to the server
+		 * @param {HttpProvider} this
+		 */
+			'savefailure'
+			);
+
+	// call parent
+	TYPO3.state.ExtDirectProvider.superclass.constructor.call(this);
+
+	Ext.apply(this, config, {
+		// defaults
+		delay: 750, // buffer changes for 750 ms
+		dirty: false,
+		started: false,
+		autoStart: true,
+		autoRead: true,
+		key: 'States.General',
+		logFailure: false,
+		logSuccess: false,
+		queue: [],
+		saveBaseParams: {},
+		readBaseParams: {},
+		paramNames:{
+			key: 'key',
+			name: 'name',
+			value: 'value',
+			data: 'data'
+		}
+	});
+
+	if (this.autoRead) {
+		this.readState();
+	}
+
+	this.dt = new Ext.util.DelayedTask(this.submitState, this);
+	if (this.autoStart) {
+		this.start();
+	}
+};
+
+
+Ext.extend(TYPO3.state.ExtDirectProvider, Ext.state.Provider, {
+
+	// localizable texts
+	saveSuccessText: 'Save Success',
+	saveFailureText: 'Save Failure',
+	readSuccessText: 'Read Success',
+	readFailureText: 'Read Failure',
+	dataErrorText: 'Data Error',
+
+
+
+	/**
+	 * Initializes state from the passed state object or array.
+	 * Use this with loading page using initial state in TYPO3.settings
+	 *
+	 * @param {Array/Object} state State to initialize state manager with
+	 */
+	initState: function(state) {
+		if (Ext.isArray(state)) {
+			Ext.each(state, function(item) {
+				this.state[item.name] = this.decodeValue(item[this.paramNames.value]);
+			}, this);
+		} else if (Ext.isObject(state)) {
+			Ext.iterate(state, function(key, value){
+				this.state[key] = this.decodeValue(value);
+			}, this);
+		} else {
+			this.state = {};
+		}
+	},
+
+	/**
+	 * Sets the passed state variable name to the passed value and queues the change
+	 * @param {String} name Name of the state variable
+	 * @param {Mixed} value Value of the state variable
+	 */
+	set: function(name, value) {
+		if (!name) {
+			return;
+		}
+		this.queueChange(name, value);
+	},
+
+
+	/**
+	 * Starts submitting state changes to server
+	 */
+	start: function() {
+		this.dt.delay(this.delay);
+		this.started = true;
+	},
+
+
+	/**
+	 * Stops submitting state changes
+	 */
+	stop: function() {
+		this.dt.cancel();
+		this.started = false;
+	},
+
+
+	/**
+	 * private, queues the state change if state has changed
+	 */
+	queueChange: function(name, value) {
+		var o = {};
+		var i;
+		var found = false;
+
+
+		var lastValue = this.state[name];
+		for (i = 0; i < this.queue.length; i++) {
+			if (this.queue[i].name === name) {
+				lastValue = this.decodeValue(this.queue[i].value);
+			}
+		}
+		var changed = undefined === lastValue || lastValue !== value;
+
+		if (changed) {
+			o[this.paramNames.name] = name;
+			o[this.paramNames.value] = this.encodeValue(value);
+			for (i = 0; i < this.queue.length; i++) {
+				if (this.queue[i].name === o.name) {
+					this.queue[i] = o;
+					found = true;
+				}
+			}
+			if (false === found) {
+				this.queue.push(o);
+			}
+			this.dirty = true;
+		}
+		if (this.started) {
+			this.start();
+		}
+		return changed;
+	},
+
+
+	/**
+	 * private, submits state to server by asynchronous Ajax request
+	 */
+	submitState: function() {
+		if (!this.dirty) {
+			this.dt.delay(this.delay);
+			return;
+		}
+		this.dt.cancel();
+
+		var o = {
+			scope: this,
+			success: this.onSaveSuccess,
+			failure: this.onSaveFailure,
+			queue: this.queue, //this.clone(this.queue),
+			params: {}
+		};
+
+		var params = Ext.apply({}, this.saveBaseParams);
+		params[this.paramNames.key] = this.key;
+		params[this.paramNames.data] = Ext.encode(o.queue);
+
+		Ext.apply(o.params, params);
+
+		// be optimistic
+		this.dirty = false;
+
+	   TYPO3.ExtDirectStateProvider.ExtDirect.setState(o, function(response, options) {
+		   if (response.success) {
+				this.onSaveSuccess(response, options);
+		   } else {
+				this.onSaveFailure(response, options);
+		   }
+	   }, this);
+	},
+
+
+	/**
+	 * Clears the state variable
+	 * @param {String} name Name of the variable to clear
+	 */
+	clear: function(name) {
+		this.set(name, undefined);
+	},
+
+
+	/**
+	 * private, save success callback
+	 */
+	onSaveSuccess: function(response, options) {
+		var o = response;
+		if (!o.success) {
+			if (this.logFailure) {
+				this.log(this.saveFailureText, o, response);
+			}
+			this.dirty = true;
+		} else {
+			Ext.each(response.params.queue, function(item) {
+				if (!item) {
+					return;
+				}
+				var name = item[this.paramNames.name];
+				var value = this.decodeValue(item[this.paramNames.value]);
+
+				if (value === undefined  || value === null) {
+					TYPO3.state.ExtDirectProvider.superclass.clear.call(this, name);
+				} else {
+						// parent sets value and fires event
+					TYPO3.state.ExtDirectProvider.superclass.set.call(this, name, value);
+				}
+			}, this);
+			if (!this.dirty) {
+				this.queue = [];
+			}else {
+				var i, j, found;
+				for (i = 0; i < response.params.queue.length; i++) {
+					found = false;
+					for (j = 0; j < this.queue.length; j++) {
+						if (response.params.queue[i].name === this.queue[j].name) {
+							found = true;
+							break;
+						}
+					}
+					if (found && this.encodeValue(response.params.queue[i].value) === this.encodeValue(this.queue[j].value)) {
+						this.queue.remove(this.queue[j]);
+					}
+				}
+			}
+			if (this.logSuccess) {
+				this.log(this.saveSuccessText, o, response);
+			}
+			this.fireEvent('savesuccess', this);
+		}
+	},
+
+
+	/**
+	 * private, save failure callback
+	 */
+	onSaveFailure: function(response, options) {
+		if (true === this.logFailure) {
+			this.log(this.saveFailureText, response);
+		}
+		this.dirty = true;
+		this.fireEvent('savefailure', this);
+	},
+
+
+	/**
+	 * private, read state callback
+	 */
+	onReadFailure: function(response, options) {
+		if (this.logFailure) {
+			this.log(this.readFailureText, response);
+		}
+		this.fireEvent('readfailure', this);
+
+	},
+
+
+	/**
+	 * private, read success callback
+	 */
+	onReadSuccess: function(response, options) {
+		var o = response, data;
+		if (!o.success) {
+			if (this.logFailure) {
+				this.log(this.readFailureText, o, response);
+			}
+		} else {
+			data = o[this.paramNames.data];
+			Ext.iterate(data, function(key, value) {
+				this.state[key] = this.decodeValue(value);
+			}, this);
+			this.queue = [];
+			this.dirty = false;
+			if (this.logSuccess) {
+				this.log(this.readSuccessText, data, response);
+			}
+			this.fireEvent('readsuccess', this);
+		}
+	},
+
+
+	/**
+	 * Reads saved state from server by sending asynchronous Ajax request and processing the response
+	 */
+	readState: function() {
+		var o = {
+			scope: this,
+			params:{}
+		};
+
+		var params = Ext.apply({}, this.readBaseParams);
+		params[this.paramNames.key] = this.key;
+
+		Ext.apply(o.params, params);
+		TYPO3.ExtDirectStateProvider.ExtDirect.getState(o, function(response, options) {
+		   if (response.success) {
+				this.onReadSuccess(response, options);
+		   } else {
+				this.onReadFailure(response, options);
+		   }
+	   }, this);
+	},
+
+
+	/**
+	 * private, logs errors or successes
+	 */
+	log: function() {
+		if (console) {
+			console.log.apply(console, arguments);
+		}
+	},
+
+	logState: function() {
+	   if (console) {
+			console.log(this.state);
+		}
+	}
+
+});
+
