diff --git a/t3lib/class.t3lib_tceforms.php b/t3lib/class.t3lib_tceforms.php
index 5778a44..9f8fcb6 100644
--- a/t3lib/class.t3lib_tceforms.php
+++ b/t3lib/class.t3lib_tceforms.php
@@ -183,6 +183,7 @@
 
 require_once(PATH_t3lib.'class.t3lib_diff.php');
 require_once(PATH_t3lib.'class.t3lib_tceforms_inline.php');
+require_once(PATH_t3lib . 'tceforms/class.t3lib_tceforms_suggest.php');
 
 	/**
 	 * Frontend version of TCEForms has been moved into its own file and should
@@ -369,6 +370,10 @@ class t3lib_TCEforms	{
 		if (!isset($GLOBALS['ajaxID']) || strpos($GLOBALS['ajaxID'], 't3lib_TCEforms_inline::')!==0) {
 			$this->inline = t3lib_div::makeInstance('t3lib_TCEforms_inline');
 		}
+			// Create instance of t3lib_TCEforms_suggest only if this a non-Suggest-AJAX call: 
+		if (!isset($GLOBALS['ajaxID']) || strpos($GLOBALS['ajaxID'], 't3lib_TCEforms_suggest::')!==0) {
+			$this->suggest = t3lib_div::makeInstance('t3lib_TCEforms_suggest');
+		}
 
 			// Prepare user defined objects (if any) for hooks which extend this function:
 		$this->hookObjectsMainFields = array();
@@ -403,6 +408,7 @@ class t3lib_TCEforms	{
 		$this->titleLen = $BE_USER->uc['titleLen'];		// @deprecated since TYPO3 4.1
 
 		$this->inline->init($this);
+		$this->suggest->init($this);
 	}
 
 
@@ -3814,6 +3820,15 @@ class t3lib_TCEforms	{
 							$sOnChange = $assignValue.';this.blur();this.selectedIndex=0;'.implode('',$fieldChangeFunc);
 							$outArr[] = '<select name="_WIZARD'.$fName.'" onchange="'.htmlspecialchars($sOnChange).'">'.implode('',$opt).'</select>';
 						break;
+						case 'suggest':
+							if (isset($PA['fieldTSConfig']['wizards.']['suggest.']['hide']) && ((bool)$PA['fieldTSConfig']['wizards.']['suggest.']['hide'] == TRUE)) {
+								break;
+							}
+
+							$suggestSelector = $this->suggest->renderSuggestSelector($PA['itemFormElName'], $table, $field, $row, $PA, $hideSuggest, $this);
+
+							$outArr[] = $suggestSelector;
+						break;
 					}
 
 						// Color wizard colorbox:
@@ -5148,6 +5163,12 @@ class t3lib_TCEforms	{
 				';
 			}
 
+				// if Suggest fields were processed, add the JS functions
+			if ($this->suggest->suggestCount > 0) {
+				$GLOBALS['SOBE']->doc->loadScriptaculous();
+				$this->loadJavascriptLib('../t3lib/js/jsfunc.tceforms_suggest.js');
+			}
+
 				// Toggle icons:
 			$toggleIcon_open = '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/pil2down.gif','width="12" height="7"').' hspace="2" alt="Open" title="Open" />';
 			$toggleIcon_close = '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/pil2right.gif','width="7" height="12"').' hspace="2" alt="Close" title="Close" />';
diff --git a/t3lib/classes/class.t3lib_tceforms_suggest.php b/t3lib/classes/class.t3lib_tceforms_suggest.php
new file mode 100644
index 0000000..b66e559
--- /dev/null
+++ b/t3lib/classes/class.t3lib_tceforms_suggest.php
@@ -0,0 +1,212 @@
+<?php
+
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2007 Andreas Wolf <typo3@andreaswolf.info>
+*  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
+*  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!
+***************************************************************/
+/**
+ * AJAX functions for use in TCEforms
+ *
+ * $Id:$
+ *
+ * @author Andreas Wolf <typo3@andreaswolf.info
+ */
+
+class t3lib_TCEforms_suggest {
+	public $suggestCount = 0;					// count the number of ajax selectors used
+
+
+	/**
+	 * Initialize an instance of t3lib_TCEforms_sugges
+	 *
+	 * @param  t3lib_TCEforms  $tceForms  Reference to an TCEforms instance
+	 * @return void
+	 */
+	public function init(&$tceForms) {
+		$this->TCEformsObj =& $tceForms;
+		$this->backPath =& $tceForms->backPath;
+	}
+
+	/**
+	 * Renders an ajax-enabled text field. Also adds required JS
+	 *
+	 * @param  string  $fieldname   The fieldname in the form
+	 * @param  string  $table       The table we render this selector for
+	 * @param  string  $field       The field we render this selector for
+	 * @param  array   $row         The row which is currently edited
+	 * @param  array   $config      The TSconfig of the field
+	 * @param  boolean $hideSuggest Whether or not to hide the selector by defaul
+	 * @return string  The HTML code for the selector
+	 */
+	public function renderSuggestSelector($fieldname, $table, $field, array $row, array $config, $hideSuggest) {
+		$this->suggestCount++;
+
+		if ($hideSuggest) {
+			$hide = ' style="display:none;"';
+		}
+
+		// TODO: move CSS to styleshee
+		$selector = '<div id="suggest-'.$table.'-'.$field.'-'.$row['uid'].'"'.$hide.'>'.$this->getLL('find_record').'
+			<input type="text" id="'.$fieldname.'Suggest" style="width:200px !important;" /></div
+			<div class="suggest_choices" id="'.$fieldname.'SuggestChoices" style="display:none;"></div>';
+		
+		$this->TCEformsObj->additionalJS_post[] = '
+			suggest.createSuggestField("'.$fieldname.'", "'.$table.'", "'.$field.'", "'.$row['uid'].'", '.$row['pid'].');
+		';
+		
+		return $selector;
+	}
+	
+	/**
+	 * Ajax handler for the "suggest" feature in TCEforms
+	 *
+	 * @param  string  $search    The string to search for in the database
+	 * @param  string  $table     The table which is currently edited
+	 * @param  string  $field     The field which is currently edited
+	 * @param  integer $uid       The uid of the record which is currently edited (may be NEWxxxxxx)
+	 * @param  integer $pid       The pid of the record which is currently edited (always numeric
+	 * @param  string  $formfield The field which is edited
+	 * @return void
+	 */
+	public function processAjaxRequest($params,  &$ajaxObj) {
+		global $TCA, $LANG;
+		
+			// instantiate the global language object here because in AJAX calls it's not available,
+			// but we need it.
+			// @see: typo3/template.php
+		require_once(PATH_typo3.'sysext/lang/lang.php');
+		$LANG = t3lib_div::makeInstance('language');
+		$LANG->init($BE_USER->uc['lang']);
+		
+		
+			// get parameters from $_GET/$_POST
+		$search = t3lib_div::_GP('value');
+		$table = t3lib_div::_GP('table');
+		$field = t3lib_div::_GP('field');
+		$uid = t3lib_div::_GP('uid');
+		$pageId = t3lib_div::_GP('pid');
+		$formfield = t3lib_div::_GP('formfield');
+		
+		
+		t3lib_div::loadTCA($table);
+		
+		if (is_numeric($uid)) {
+				// already existing element, so get the TSconfig of the page itself...
+			if ($table == 'pages') {
+				$pageId = $uid;
+				// ... or the page containing the elemen
+			} else {
+				$row = t3lib_BEfunc::getRecord($table, $uid);
+				$pageId = $row['pid'];
+			}
+		} // New element, so use given id of parent page (i.e., don't modify it here)
+		
+		$TSconfig = t3lib_BEfunc::getPagesTSconfig($pageId);
+		
+		$queryTables = t3lib_div::trimExplode(',', $TCA[$table]['columns'][$field]['config']['allowed']);
+		
+		$resultRows = array();
+		foreach ($queryTables as $queryTable) {
+			t3lib_div::loadTCA($queryTable);
+			
+				// if the table does not exist, skip i
+			if (count($TCA[$queryTable]) == 0) continue;
+			
+				// merge the configurations of different "levels" to get the working configuration for this table and
+				// field (i.e., go from the most general to the most special configuration)
+			$config = is_array($TSconfig['TCEMAIN.']['default.']['suggest.']) ?
+			            $TSconfig['TCEMAIN.']['default.']['suggest.'] : array();
+			$config = t3lib_div::array_merge_recursive_overrule($config,
+			            (is_array($TSconfig['TCEMAIN.'][$queryTable.'.']['suggest.']) ?
+			              $TSconfig['TCEMAIN.'][$queryTable.'.']['suggest.'] : array()));
+				// use $table instead of $queryTable here because we overlay a config for the input-field here, not for
+				// the queried table
+			$config = t3lib_div::array_merge_recursive_overrule($config,
+			            (is_array($TSconfig['TCEFORM.'][$table.'.'][$field.'.']['suggest.']) ?
+			              $TSconfig['TCEFORM.'][$table.'.'][$field.'.']['suggest.'] : array()));
+			$config = t3lib_div::array_merge_recursive_overrule($config,
+			            (is_array($TSconfig['TCEFORM.'][$table.'.'][$field.'.']['suggest.'][$queryTable.'.']) ?
+			              $TSconfig['TCEFORM.'][$table.'.'][$field.'.']['suggest.'][$queryTable.'.'] : array()));
+			
+			$class = t3lib_div::makeInstanceClassName($config['suggestClass']);
+			if (!class_exists($class)) {
+				$class = 'suggestDefaultSelector';
+			}
+			$obj = new $class($queryTable, $config);
+			
+			$params = array('value' => $search);
+			$rows = $obj->queryTable($params, $this);
+			
+			
+			if (!$rows) {
+				continue;
+			}
+			$resultRows = t3lib_div::array_merge($resultRows, $rows);
+			unset($rows);
+		}
+		
+		$listItems = array();
+		if (count($resultRows) > 0) {
+				// traverse all found records, ...
+			foreach ($resultRows as $key => $row) {
+				$rowsSort[$key] = $row['text'];
+			}
+				// ... sort them...
+			asort($rowsSort);
+			$rowsSort = array_keys($rowsSort);
+			
+				// ... and put together the selector
+			$returnRows = array();
+			for ($i = 0; $i < 10 && $i < count($resultRows); $i++) {
+				$row = $resultRows[$rowsSort[$i]];
+				$returnRows[] = '<li'.($row['class']!=''?' class="'.$row['class'].'"':'').
+				                ' id="'.$row['table'].'-'.$row['uid'].'-'.$table.'-'.$uid.'-'.$field.
+				                '" style="'.$row['style'].'">'.$row['text'].'</li>';
+			}
+		}
+		if (count($returnRows) > 0) {
+			$list = implode('', $returnRows);
+		} else {
+			$list = '';
+		}
+		
+			// encode the content in utf-8, otherwise it won't be shown correctly in the backend
+		$ajaxObj->addContent(0, $LANG->csConvObj->utf8_encode('<ul>'.$list.'</ul>', $LANG->charSet));
+	}
+	
+	/**
+	 * Returns language label from locallang_core.php
+	 *
+	 * @param	string		The label key
+	 * @return	string		The value of the label, fetched for the current backend language.
+	 */
+	private function getLL($str) {
+		return $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.'.$str);
+	}
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['classes/t3lib/class.t3lib_tceforms_suggest.php'])	{
+	include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['classes/t3lib/class.t3lib_tceforms_suggest.php']);
+}
diff --git a/t3lib/config_default.php b/t3lib/config_default.php
index 03b5438..c687185 100644
--- a/t3lib/config_default.php
+++ b/t3lib/config_default.php
@@ -244,6 +244,7 @@ $TYPO3_CONF_VARS = Array(
 			't3lib_TCEforms_inline::createNewRecord'		    => 't3lib/class.t3lib_tceforms_inline.php:t3lib_TCEforms_inline->processAjaxRequest',
 			't3lib_TCEforms_inline::synchronizeLocalizeRecords'	=> 't3lib/class.t3lib_tceforms_inline.php:t3lib_TCEforms_inline->processAjaxRequest',
 			't3lib_TCEforms_inline::setExpandedCollapsedState'	=> 't3lib/class.t3lib_tceforms_inline.php:t3lib_TCEforms_inline->processAjaxRequest',
+			't3lib_TCEforms_suggest::searchRecord'              => 't3lib/tceforms/class.t3lib_tceforms_suggest.php:t3lib_TCEforms_suggest->processAjaxRequest',
 			'ShortcutMenu::getGroups'    => 'typo3/classes/class.shortcutmenu.php:ShortcutMenu->getAjaxShortcutGroups',
 			'ShortcutMenu::saveShortcut' => 'typo3/classes/class.shortcutmenu.php:ShortcutMenu->setAjaxShortcut',
 			'ShortcutMenu::render'       => 'typo3/classes/class.shortcutmenu.php:ShortcutMenu->renderAjax',
diff --git a/t3lib/js/jsfunc.tceforms_suggest.js b/t3lib/js/jsfunc.tceforms_suggest.js
new file mode 100644
index 0000000..43dad7f
--- /dev/null
+++ b/t3lib/js/jsfunc.tceforms_suggest.js
@@ -0,0 +1,84 @@
+/***************************************************************
+*  AJAX selectors for TCEforms
+*
+* $Id:$
+*
+*  Copyright notice
+*
+*  (c) 2007 Andreas Wolf <typo3@andreaswolf.info>
+*  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.
+*
+*  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!
+***************************************************************/
+
+/**
+ * Class for JS handling of suggest fields in TCEforms.
+ *
+ * @author  Andreas Wolf <typo3@andreaswolf.info>
+ */
+if (!TCEForms) {
+	var TCEForms = {};
+}
+TCEForms.Suggest = {
+	minimumCharacters: 2,
+
+	/**
+	 * Wrapper for script.aculo.us' Autocompleter functionality: Assigns a new autocompletion object to
+	 * the input field of a given Suggest selector.
+	 *
+	 * @param  string  objectId  The ID of the object to assign the completer to
+	 * @param  string  table     The table of the record which is currently edited
+	 * @param  string  field     The field which is currently edited
+	 * @param  integer uid       The uid of the record which is currently edited
+	 * @param  integer pid       The pid of the record which is currently edited
+	 * @return void
+	 */
+	createField: function(objectId, table, field, uid, pid) {
+		new Ajax.Autocompleter(
+			objectId + 'Suggest',
+			objectId + 'SuggestChoices',
+			top.TS.PATH_typo3 + 'ajax.php',
+			{
+				paramName: 'value',
+				minChars: this.minimumCharacters,
+				updateElement: TCEForms.Suggest.addElementToList,
+				parameters: 'ajaxID=t3lib_TCEforms_suggest::searchRecord&table=' + table + '&field=' + field + '&formfield=' + objectId + '&uid=' + uid + '&pid=' + pid
+			}
+		);
+	},
+
+	/**
+	 * Adds an element to the list of items in a group selector.
+	 *
+	 * @param  object  item  The item to add to the list (usually an li element)
+	 * @return void
+	 */
+	addElementToList: function(item) {
+		arr = item.id.split('-');
+		ins_table = arr[0];
+		ins_uid = arr[1];
+		rec_table = arr[2];
+		rec_uid = arr[3];
+		rec_field = arr[4];
+
+		var formEl = 'data[' + rec_table + '][' + rec_uid + '][' + rec_field + ']';
+		setFormValueFromBrowseWin(formEl, ins_table + '_' + ins_uid, item.firstChild.textContent);
+		TBE_EDITOR.fieldChanged(rec_table, rec_uid, rec_field, formEl);
+
+		document.getElementById(formEl + 'Suggest').value = '';
+	}
+}
\ No newline at end of file
diff --git a/t3lib/stddb/tables.php b/t3lib/stddb/tables.php
index c0ac55e..1fea425 100644
--- a/t3lib/stddb/tables.php
+++ b/t3lib/stddb/tables.php
@@ -224,8 +224,13 @@ $TCA['pages'] = array(
 				'size' => '1',
 				'maxitems' => '1',
 				'minitems' => '0',
-				'show_thumbs' => '1'
-			)
+				'show_thumbs' => '1',
+				'wizards' => array(
+					'suggest' => array(
+						'type' => 'suggest'
+					)
+				)
+			),
 		),
 		'tx_impexp_origuid' => array('config'=>array('type'=>'passthrough')),
 		't3ver_label' => array(
diff --git a/t3lib/tceforms/class.t3lib_tceforms_suggest.php b/t3lib/tceforms/class.t3lib_tceforms_suggest.php
new file mode 100644
index 0000000..9289528
--- /dev/null
+++ b/t3lib/tceforms/class.t3lib_tceforms_suggest.php
@@ -0,0 +1,202 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2007-2009 Andreas Wolf <typo3@andreaswolf.info>
+*  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!
+***************************************************************/
+/**
+ * AJAX functions for use in TCEforms
+ *
+ * $Id:$
+ *
+ * @author Andreas Wolf <typo3@andreaswolf.info>
+ */
+
+require_once(PATH_typo3 . 'classes/class.typo3_formsuggestselector.php');
+
+class t3lib_TCEforms_suggest {
+		// count the number of ajax selectors used
+	public $suggestCount = 0;
+
+
+	/**
+	 * Initialize an instance of t3lib_TCEforms_suggest
+	 *
+	 * @param  t3lib_TCEforms  $tceForms  Reference to an TCEforms instance
+	 * @return void
+	 */
+	public function init(&$tceForms) {
+		$this->TCEformsObj =& $tceForms;
+		$this->backPath =& $tceForms->backPath;
+	}
+
+	/**
+	 * Renders an ajax-enabled text field. Also adds required JS
+	 *
+	 * @param  string  $fieldname   The fieldname in the form
+	 * @param  string  $table       The table we render this selector for
+	 * @param  string  $field       The field we render this selector for
+	 * @param  array   $row         The row which is currently edited
+	 * @param  array   $config      The TSconfig of the field
+	 * @param  boolean $hideByDefault Whether or not to hide the selector by default
+	 * @return string  The HTML code for the selector
+	 */
+	public function renderSuggestSelector($fieldname, $table, $field, array $row, array $config, $hideByDefault) {
+		$this->suggestCount++;
+
+		if ($hideByDefault) {
+			$hide = ' typo3-TCEforms-suggest-hidden';
+		}
+
+		$selector = '<div class="typo3-TCEforms-suggest' . $hide . '" id="suggest-'.$table.'-'.$field.'-'.$row['uid'].'">Search for records:<br />' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.find_record') . '<input type="text" id="' . $fieldname . 'Suggest" class="typo3-TCEforms-suggest-search" />
+			<div  class="typo3-TCEforms-suggest-choices" id="' . $fieldname . 'SuggestChoices"></div></div>';
+
+		$this->TCEformsObj->additionalJS_post[] = '
+			TCEForms.Suggest.createField("' . $fieldname . '", "' . $table . '", "' . $field . '", "' . $row['uid'] . '", ' . $row['pid'] . ');
+		';
+
+		return $selector;
+	}
+
+	/**
+	 * Ajax handler for the "suggest" feature in TCEforms
+	 *
+	 * @param  string  $search    The string to search for in the database
+	 * @param  string  $table     The table which is currently edited
+	 * @param  string  $field     The field which is currently edited
+	 * @param  integer $uid       The uid of the record which is currently edited (may be NEWxxxxxx)
+	 * @param  integer $pid       The pid of the record which is currently edited (always numeric
+	 * @param  string  $formfield The field which is edited
+	 * @return void
+	 */
+	public function processAjaxRequest($params,  &$ajaxObj) {
+		global $TCA, $LANG;
+
+			// get parameters from $_GET/$_POST
+		$search = t3lib_div::_GP('value');
+		$table = t3lib_div::_GP('table');
+		$field = t3lib_div::_GP('field');
+		$uid = t3lib_div::_GP('uid');
+		$pageId = t3lib_div::_GP('pid');
+		$formfield = t3lib_div::_GP('formfield');
+
+
+		t3lib_div::loadTCA($table);
+
+		if (is_numeric($uid)) {
+				// already existing element, so get the TSconfig of the page itself...
+			if ($table == 'pages') {
+				$pageId = $uid;
+				// ... or the page containing the element
+			} else {
+				$row = t3lib_BEfunc::getRecord($table, $uid);
+				$pageId = $row['pid'];
+			}
+		} // New element, so use given id of parent page (i.e., don't modify it here)
+
+		$TSconfig = t3lib_BEfunc::getPagesTSconfig($pageId);
+		$wizardConfig = $TCA[$table]['columns'][$field]['config']['wizards']['suggest']['config'];
+
+		$queryTables = t3lib_div::trimExplode(',', $TCA[$table]['columns'][$field]['config']['allowed']);
+
+		$resultRows = array();
+		foreach ($queryTables as $queryTable) {
+			t3lib_div::loadTCA($queryTable);
+
+				// if the table does not exist, skip it
+			if (count($TCA[$queryTable]) == 0) continue;
+
+			$config = (array)$wizardConfig['default'];
+			$config = t3lib_div::array_merge_recursive_overrule($config,
+			            is_array($wizardConfig[$queryTable]) ?
+			              $wizardConfig[$queryTable] : array());
+
+				// merge the configurations of different "levels" to get the working configuration for this table and
+				// field (i.e., go from the most general to the most special configuration)
+			$config = t3lib_div::array_merge_recursive_overrule($config,
+			            is_array($TSconfig['TCEMAIN.']['default.']['suggest.']) ?
+			              $TSconfig['TCEMAIN.']['default.']['suggest.'] : array());
+			$config = t3lib_div::array_merge_recursive_overrule($config,
+			            (is_array($TSconfig['TCEMAIN.'][$queryTable.'.']['suggest.']) ?
+			              $TSconfig['TCEMAIN.'][$queryTable.'.']['suggest.'] : array()));
+				// use $table instead of $queryTable here because we overlay a config for the input-field here, not for
+				// the queried table
+			$config = t3lib_div::array_merge_recursive_overrule($config,
+			            (is_array($TSconfig['TCEFORM.'][$table.'.'][$field.'.']['suggest.']) ?
+			              $TSconfig['TCEFORM.'][$table.'.'][$field.'.']['suggest.'] : array()));
+			$config = t3lib_div::array_merge_recursive_overrule($config,
+			            (is_array($TSconfig['TCEFORM.'][$table.'.'][$field.'.']['suggest.'][$queryTable.'.']) ?
+			              $TSconfig['TCEFORM.'][$table.'.'][$field.'.']['suggest.'][$queryTable.'.'] : array()));
+
+			$className = $config['suggestClass'];
+
+			if (!class_exists($className)) {
+				$className = 'TYPO3_formSuggestSelector';
+			}
+			$obj = t3lib_div::makeInstance($className, $queryTable, $config);
+
+			$params = array('value' => $search);
+			$rows = $obj->queryTable($params, $this);
+
+
+			if (!$rows) {
+				continue;
+			}
+			$resultRows = t3lib_div::array_merge($resultRows, $rows);
+			unset($rows);
+		}
+
+		$listItems = array();
+		if (count($resultRows) > 0) {
+				// traverse all found records, ...
+			foreach ($resultRows as $key => $row) {
+				$rowsSort[$key] = $row['text'];
+			}
+				// ... sort them...
+			asort($rowsSort);
+			$rowsSort = array_keys($rowsSort);
+
+				// ... and put together the selector
+			$returnRows = array();
+			for ($i = 0; $i < 10 && $i < count($resultRows); $i++) {
+				$row = $resultRows[$rowsSort[$i]];
+				$returnRows[] = '<li'.($row['class']!=''?' class="'.$row['class'].'"':'').
+				                ' id="'.$row['table'].'-'.$row['uid'].'-'.$table.'-'.$uid.'-'.$field.
+				                '" style="'.$row['style'].'">'.$row['text'].'</li>';
+			}
+		}
+		if (count($returnRows) > 0) {
+			$list = implode('', $returnRows);
+		} else {
+			$list = '';
+		}
+
+		$list = '<ul>' . $list . '</ul>';
+		$ajaxObj->addContent(0, $list);
+	}
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['classes/t3lib/tceforms/class.t3lib_tceforms_suggest.php']) {
+	include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['classes/t3lib/tceforms/class.t3lib_tceforms_suggest.php']);
+}
diff --git a/typo3/classes/class.typo3_formsuggestselector.php b/typo3/classes/class.typo3_formsuggestselector.php
new file mode 100644
index 0000000..2ec4fe3
--- /dev/null
+++ b/typo3/classes/class.typo3_formsuggestselector.php
@@ -0,0 +1,440 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2007-2009 Andreas Wolf <typo3@andreaswolf.info>
+*  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!
+***************************************************************/
+
+/**
+ * Demo implementation of a handler class for the ajax record selector.
+ *
+ * Normally other implementations should be inherited from this one. queryTable() should not be overwritten under normal
+ * circumstances.
+ *
+ * @author Andreas Wolf <typo3@andreaswolf.info>
+ *
+ */
+class TYPO3_formSuggestSelector {
+	/**
+	 * The name of the table to query
+	 *
+	 * @var string
+	 */
+	protected $table = '';
+
+	/**
+	 * The name of the foreign table to query (records from this table will be used for displaying instead of the ones
+	 * from table)
+	 *
+	 * @var string
+	 */
+	protected $mmForeignTable = '';
+
+	/**
+	 * Holds the configuration for the table we query
+	 *
+	 * @var array
+	 */
+	protected $tableConfig = array();
+
+	/**
+	 * Counter to limit the recursions when querying the table; also needed to choose the range of records to select
+	 *
+	 * @var integer
+	 */
+	protected $recursionCounter = 0;
+
+	/**
+	 * The select-clause to use when selecting the records (is manipulated and used by different functions, so it has to
+	 * be a global var)
+	 *
+	 * @var string
+	 */
+	protected $selectClause = '';
+
+	/**
+	 * Configuration for this selector from TSconfig
+	 *
+	 * @var array
+	 */
+	protected $config = array();
+
+	/**
+	 * The CSS class(es) to use for rendering the selector
+	 *
+	 * @var string
+	 */
+	protected $cssClasses = '';
+
+
+
+	/**
+	 * The constructor of this class
+	 *
+	 * @param  string $table
+	 * @param  array  $config  The configuration (TSconfig) to use for this selector
+	 * @return void
+	 */
+	public function __construct($table, $config) {
+		$this->table = $table;
+		$this->config = $config;
+
+			// Get the configuration for this table
+		$this->tableConfig = $GLOBALS['TCA'][$table];
+
+		if ($config['cssClasses'] != '') {
+			$this->cssClasses = $config['cssClasses'];
+		}
+
+
+		$depth = intval($config['pidDepth']);
+
+		$pageIds = t3lib_div::trimExplode(',', $config['pid']);
+		foreach ($pageIds as $pageId) {
+			if ($pageId > 0) {
+				$pagesTemp .= $pageId.',';
+				$pageArr = $this->getAllSubpagesOfPage($pageId, $depth);
+
+				foreach ($pageArr as $pages) {
+					$pagesTemp .= $pages.',';
+				}
+			}
+		}
+		$this->pages = array_unique(t3lib_div::trimExplode(',', $pagesTemp, true));
+
+
+
+		if ($params['table'] == 'pages') {
+			$this->addWhere = ' AND ' . $this->tceforms->readPerms();
+		}
+			// if table is versionized, only get the records from the Live Workspace - the overlay of WS-records is
+			// done below
+		if ($GLOBALS['TCA'][$params['table']]['ctrl']['versioningWS'] == true) {
+			$this->addWhere .= ' AND t3ver_wsid = 0';
+		}
+	}
+
+	/**
+	 * Queries a table for records and completely processes them
+	 *
+	 * Returns a two-dimensional array of almost finished records; the only need to be put into a <li>-structure
+	 *
+	 * If you subclass this class, you will most likely only want to overwrite the functions called from here, but not
+	 * this function itself
+	 *
+	 * @param  array  $params
+	 * @param  object $ref
+	 * @return array  The fetched and processed rows
+	 */
+	public function queryTable(&$params, &$ref) {
+		$this->params = &$params;
+		$this->start = $this->recursionCounter * 50;
+
+		$this->prepareSelectStatement();
+
+		$rows = array();
+
+		$result = $this->selectRecords();
+		if (!$result) return false;
+
+		while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) {
+				// check if we already have collected 10 records
+			if (count($rows) > 10) break;
+
+			$this->manipulateRecord($row);
+
+			$this->makeWorkspaceOverlay($row);
+
+				// check if the user has access to the record
+			if (!$this->checkRecordAccess($row, $row['uid'])) {
+				continue;
+			}
+
+			$iconPath = $this->getIcon($row);
+
+			$uid = ($row['t3ver_oid'] > 0 ? $row['t3ver_oid'] : $row['uid']);
+
+			$path = $this->getRecordPath($row, $uid);
+
+			if (strlen($path) > 30) {
+				$croppedPath = $GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet,$path,10).'...'.$GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet,$path,-20);
+			} else {
+				$croppedPath = $path;
+			}
+
+
+
+			$label = $this->getLabel($row);
+
+			$entry = array(
+				'text'  => '<span class="label">' . $label . '</span><span class="uid">(' . $uid . ')</span><br /><span class="path">' . $croppedPath . '</span>',
+				'table' => ($this->mmForeignTable ? $this->mmForeignTable : $this->table),
+				'path'  => $path,
+				'uid'   => $uid,
+				'icon'  => $iconPath,
+				'style' => 'background-image:url(' . $iconPath . ');',
+				'class' => $this->cssClasses,
+			);
+
+			$entry = $this->renderRecord($row, $entry);
+
+
+			$rows[$table . '_' . $uid] = $entry;
+		}
+
+			// if there are less than ten records, call this function again to get more records
+		if (count($rows) < 10 && $GLOBALS['TYPO3_DB']->sql_num_rows($result) >= 50 && $recursionCounter < 10) {
+			$tmp = self::queryTable($this->params['value'], ++$recursionCounter);
+			$rows = array_merge($tmp, $rows);
+		}
+
+		return $rows;
+	}
+
+	/**
+	 * Prepare the statement for selecting the records which will be returned to the selector. May also return some
+	 * other records (e.g. from a mm-table) which will be used later on to select the real records
+	 *
+	 * @return  void
+	 */
+	protected function prepareSelectStatement() {
+
+		$searchWholePhrase = $this->config['searchWholePhrase'];
+
+		if (isset($this->pages)) {
+			$this->selectClause = $this->tableConfig['ctrl']['label'].' LIKE \''.($searchWholePhrase ? '%' : '') . $GLOBALS['TYPO3_DB']->escapeStrForLike($this->params['value'], $this->table).'%\' AND deleted=0';
+		}
+
+		$this->selectClause .= $this->getPageIdClause($this->pages) ? ' AND ('.$this->getPageIdClause($this->pages).')' : '';
+
+			// add the global clauses to the where-statement
+		$this->selectClause .= $this->addWhere;
+	}
+
+	/**
+	 * Selects all subpages of one page, optionally only upto a certain level
+	 *
+	 * @param  integer $uid
+	 * @param  integer $depth
+	 */
+	protected function getAllSubpagesOfPage($uid, $depth = 99) {
+		$pageIds = array($uid);
+		$level = 0;
+
+		$pages = array();
+		$pages[$level] = $uid;
+
+			// check for
+		while (($depth - $level) >= 0 && $pageIds != '') {
+			++$level;
+
+			$rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', 'pages', '(' . $this->getPageIdClause($pageIds) . ')', '', '', '', 'uid');
+			if (count($rows) > 0) {
+				$pageIds = array_keys($rows);
+				$pageIdsString = implode(',', $pageIds);
+
+				$pages[$level] = $pageIdsString;
+			} else {
+				break;
+			}
+		}
+
+		return $pages;
+	}
+
+	/**
+	 * Manipulate a record before using it to render the selector; may be used to replace a MM-relation etc.
+	 *
+	 * @param  array $row
+	 */
+	protected function manipulateRecord(&$row) {
+	}
+
+	/**
+	 * Select the recrods from the table (uses the statement prepared in $this->selectClause)
+	 *
+	 * @return  resource  The resource which holds the records
+	 */
+	protected function selectRecords() {
+		$result = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $this->table, $this->selectClause, '', '', $this->start . ', 50');
+
+		if (!$result) {
+			$result = false;
+		}
+		return $result;
+	}
+
+	/**
+	 *  Selects whether the logged in Backend User is allowed to read a specific record
+	 */
+	protected function checkRecordAccess($row, $uid) {
+		$retValue = true;
+		if (($this->mmForeignTable ? $this->mmForeignTable : $this->table) == 'pages') {
+			$row = t3lib_BEfunc::readPageAccess($uid, $GLOBALS['BE_USER']->getPagePermsClause(1));
+			if (!$row)
+				$retValue = false;
+		} else {
+			$parent = t3lib_BEfunc::readPageAccess($row['pid'], $GLOBALS['BE_USER']->getPagePermsClause(1));
+			if (!is_array($parent))
+				$retValue = false;
+		}
+
+		return $retValue;
+	}
+
+	/**
+	 * Overlay the given record with its workspace-version, if any
+	 *
+	 * @return  void
+	 */
+	protected function makeWorkspaceOverlay(&$row) {
+			// check for workspace-versions
+		if ($GLOBALS['BE_USER']->workspace != 0 && $this->tableConfig['ctrl']['versioningWS'] == true) {
+			t3lib_BEfunc::workspaceOL(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row);
+		}
+	}
+
+	/**
+	 * Return the icon for a record - just a wrapper for two functions from t3lib_iconWorks
+	 *
+	 * @param  array  $row The record to get the icon for
+	 * @return string The path to the icon
+	 */
+	protected function getIcon($row) {
+		$icon = t3lib_iconWorks::getIcon(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row);
+		$icon = t3lib_iconWorks::skinImg('', $icon, '', 1);
+
+		return $icon;
+	}
+
+	/**
+	 * Returns the path for a record. Is the whole path for all records except pages - for these the last part is cut
+	 * off, because it contains the pagetitle itself, which would be double information
+	 *
+	 * The path is returned uncut, cutting has to be done by calling function.
+	 *
+	 * @param  array   $row The row
+	 * @param  integer $uid The records uid
+	 * @return string  The record-path
+	 */
+	protected function getRecordPath(&$row, $uid) {
+
+		if ($this->config['titleSegmentLimit']) {
+			$titleLimit = $this->config['titleSegmentLimit'];
+		} else {
+			$titleLimit = 0;
+		}
+
+		if (($this->mmForeignTable ? $this->mmForeignTable : $this->table) == 'pages') {
+			$path = t3lib_BEfunc::getRecordPath($uid, '', $titleLimit);
+
+				// for pages we only want the first (n-1) parts of the path, because the n-th part is the page itself
+			$path = substr($path, 0, strrpos($path, '/', -2)) . '/';
+		} else {
+			$path = t3lib_BEfunc::getRecordPath($row['pid'], '', $titleLimit);
+		}
+
+		return $path;
+	}
+
+	/**
+	 * Returns a label for a given record; usually only a wrapper for t3lib_BEfunc::getRecordTitle
+	 *
+	 * @param  array $row
+	 * @return The label for the record
+	 */
+	protected function getLabel($row) {
+		$label = t3lib_BEfunc::getRecordTitle(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row);
+		if ($label !== '') {
+			$label =  t3lib_div::fixed_lgd_cs($label, 20);
+		} else {
+			$label = '<em>' . $this->getLL('l_no_title') . '</em>';
+		}
+
+		return $label;
+	}
+
+	/**
+	 * Calls a user function for rendering the page.
+	 *
+	 * This user function should manipulate $entry, especially $entry['text']
+	 *
+	 * @param  array $row   The row
+	 * @param  array $entry The entry to render
+	 * @return array The rendered entry (will be put into a <li> later on
+	 */
+	protected function renderRecord($row, $entry) {
+			// call renderlet if available (normal pages etc. usually don't have one)
+		$func = $this->config['renderFunc'];
+		if ($func != '') {
+			$params = array(
+				'table' => $this->table,
+				'uid' => $row['uid'],
+				'row' => $row,
+				'entry' => &$entry
+			);
+			t3lib_div::callUserFunction($func, $params, $this, '');
+		}
+
+		return $entry;
+	}
+
+	/**
+	 * Returns language label from locallang_core.php
+	 * Labels must be prefixed with either "l_" or "m_".
+	 * The prefix "l_" maps to the prefix "labels." inside locallang_core.php
+	 * The prefix "m_" maps to the prefix "mess." inside locallang_core.php
+	 *
+	 * @param	string		The label key
+	 * @return	string		The value of the label, fetched for the current backend language.
+	 */
+	function getLL($str) {
+		$content = '';
+
+		switch(substr($str, 0, 2)) {
+			case 'l_':
+				$content = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.' . substr($str, 2));
+			break;
+			case 'm_':
+				$content = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.' . substr($str, 2));
+			break;
+		}
+		return $content;
+	}
+
+	/**
+	 * Returns a string as to use a replacement for IN (...) in an SQL where-clause. Needed for compatibility with
+	 * MSSQL
+	 *
+	 * @param  array  $pageIds The pages to use for the clause
+	 * @return string
+	 */
+	private function getPageIdClause($pageIds) {
+		$pageArr = array();
+		foreach ($pageIds as $pageId) {
+			$pageArr[] = 'pid=' . $pageId;
+		}
+
+		return implode(' OR ', $pageArr);
+	}
+}
diff --git a/typo3/gfx/group_suggest.gif b/typo3/gfx/group_suggest.gif
new file mode 100644
index 0000000..6a39a4c
Binary files /dev/null and b/typo3/gfx/group_suggest.gif differ
diff --git a/typo3/stylesheet.css b/typo3/stylesheet.css
index f555f81..4325eef 100644
--- a/typo3/stylesheet.css
+++ b/typo3/stylesheet.css
@@ -1070,6 +1070,63 @@ table.typo3-TCEforms div.typo3-dyntabmenu-tabs {
 	margin-top: 10px;
 }
 
+table.typo3-TCEforms div.typo3-TCEforms-suggest {
+	margin-left:10px;
+}
+
+table.typo3-TCEforms div.typo3-TCEforms-suggest-hidden {
+	display: none;
+}
+
+.typo3-TCEforms-suggest-search {
+	width: 200px !important;
+	background-image:url(gfx/zoom.gif);
+	background-position:2px center;
+	background-repeat:no-repeat;
+	padding-left:16px;
+}
+
+div.typo3-TCEforms-suggest-choices {
+	position:absolute;
+	width:250px !important;
+	background-color:white;
+	border:1px solid #888;
+	margin:0px;
+	padding:0px;
+}
+div.typo3-TCEforms-suggest-choices UL {
+	list-style-type:none;
+	margin:0px;
+	padding:0px;
+}
+div.typo3-TCEforms-suggest-choices LI.selected { background-color: #ffb !important;}
+div.typo3-TCEforms-suggest-choices LI {
+	list-style-type:none;
+	display:block;
+	margin:0;
+	padding:2px;
+	padding-left:24px;
+	height:32px;
+	cursor:pointer;
+	background-color:#EFEFF4;
+	background-repeat:no-repeat;
+	background-position:4px center;
+}
+div.typo3-TCEforms-suggest-choices LI.pages {
+	background-color:#FCC;
+}
+div.typo3-TCEforms-suggest-choices SPAN.label {
+	font-weight:bold;
+}
+div.typo3-TCEforms-suggest-choices SPAN.uid {
+	font-size:0.9em;
+	margin-left:0.3em;
+}
+div.typo3-TCEforms-suggest-choices SPAN.path {
+	font-size:0.9em;
+	margin-top:0.3em;
+}
+
 
 /* - - - - - - - - - - - - - - - - - - - - -
 
@@ -2796,13 +2853,3 @@ div {
 	border: 1px dotted #666;
 }
 */
-
-
-
-
-
-
-
-
-
-
diff --git a/typo3/sysext/t3skin/stylesheets/typo3-TCEforms.css b/typo3/sysext/t3skin/stylesheets/typo3-TCEforms.css
index 4c47b39..2527a85 100644
--- a/typo3/sysext/t3skin/stylesheets/typo3-TCEforms.css
+++ b/typo3/sysext/t3skin/stylesheets/typo3-TCEforms.css
@@ -211,3 +211,12 @@ TABLE.typo3-TCEforms td.thumbnails {
 .typo3-TCEforms-palette SELECT.select {
 	margin-top: 2px;
 }
+
+.typo3-TCEforms-suggest-search {
+	width: 200px !important;
+	background-image:url(../icons/gfx/zoom.gif);
+	background-position:2px center;
+	background-repeat:no-repeat;
+	padding-left:20px;
+	vertical-align:middle;
+}
