Index: admin/auth.php
===================================================================
--- admin/auth.php	(revision 1061)
+++ admin/auth.php	(revision 794)
@@ -14,5 +14,6 @@
 
 # If we have a session cookie, go to index.php
-if (isset($_SESSION['sess_user_id'])) {
+if (isset($_SESSION['sess_user_id']))
+{
 	http::redirect('index.php');
 }
@@ -22,7 +23,18 @@
 $dlang = http::getAcceptLanguage();
 $dlang = ($dlang == '' ? 'en' : $dlang);
-if ($dlang != 'en' && preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$dlang)) {
+if ($dlang != 'en' && preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$dlang))
+{
 	l10n::set(dirname(__FILE__).'/../locales/'.$dlang.'/main');
 }
+
+$page_url = http::getHost().$_SERVER['REQUEST_URI'];
+
+$change_pwd = $core->auth->allowPassChange() && isset($_POST['new_pwd']) && isset($_POST['new_pwd_c']) && isset($_POST['login_data']);
+$login_data = !empty($_POST['login_data']) ? html::escapeHTML($_POST['login_data']) : null;
+$recover = $core->auth->allowPassChange() && !empty($_REQUEST['recover']);
+$safe_mode = !empty($_REQUEST['safe_mode']);
+$akey = $core->auth->allowPassChange() && !empty($_GET['akey']) ? $_GET['akey'] : null;
+$user_id = $user_pwd = $user_key = $user_email = null;
+$err = $msg = null;
 
 # Auto upgrade
@@ -31,307 +43,389 @@
 	try {
 		if (($changes = dotclearUpgrade($core)) !== false) {
-			$_ctx->setMessage(__('Dotclear has been upgraded.').'<!-- '.$changes.' -->');
-		}
-	}
-	catch (Exception $e) {
-		$_ctx->addError($e->getMessage());
-	}
-}
-
-/**
-Actions for authentication on admin pages
-*/
-class adminPageAuth
-{
-	# Send new password from recover email
-	public static function send($akey)
-	{
-		global $core, $_ctx;
-		
-		$_ctx->akey = true;
-		
-		try	{
-			$recover_res = $core->auth->recoverUserPassword($akey);
-			
-			$subject = mb_encode_mimeheader('DotClear '.__('Your new password'),'UTF-8','B');
-			$message =
-			__('Username:').' '.$recover_res['user_id']."\n".
-			__('Password:').' '.$recover_res['new_pass']."\n\n".
-			preg_replace('/\?(.*)$/','',http::getHost().$_SERVER['REQUEST_URI']);
-			
-			$headers[] = 'From: dotclear@'.$_SERVER['HTTP_HOST'];
-			$headers[] = 'Content-Type: text/plain; charset=UTF-8;';
-			
-			mail::sendMail($recover_res['user_email'],$subject,$message,$headers);
-			$_ctx->setMessage(__('Your new password is in your mailbox.'));
-		}
-		catch (Exception $e) {
-			$_ctx->addError($e->getMessage());
-		}
-	}
-	
-	# Authentication process
-	public static function process($form,$user_id,$user_pwd,$user_key=null)
-	{
-		global $core, $_ctx;
-		
-		# We check the user
-		$check_user = $core->auth->checkUser($user_id,$user_pwd,$user_key) === true;
-		
-		$cookie_admin = http::browserUID(DC_MASTER_KEY.$user_id.
-			crypt::hmac(DC_MASTER_KEY,$user_pwd)).bin2hex(pack('a32',$user_id));
-		
-		if ($check_user && $core->auth->mustChangePassword())
-		{
-			$form->login_data = join('/',array(
-				base64_encode($user_id),
-				$cookie_admin,
-				$form->user_remember == '' ? '0' : '1'
-			));
-			
-			if (!$core->auth->allowPassChange()) {
-				$_ctx->addError(__('You have to change your password before you can login.'));
-			} else {
-				$_ctx->addError(__('In order to login, you have to change your password now.'));
-				$_ctx->change_pwd = true;
-			}
-		}
-		elseif ($check_user && $form->safe_mode != '' && !$core->auth->isSuperAdmin()) 
-		{
-			$_ctx->addError(__('Safe Mode can only be used for super administrators.'));
-		}
-		elseif ($check_user)
-		{
-			$core->session->start();
-			$_SESSION['sess_user_id'] = $user_id;
-			$_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY);
-			
-			if ($form->blog != '') {
-				$_SESSION['sess_blog_id'] = $form->blog;
-			}
-			
-			if ($form->safe_mode != '' && $core->auth->isSuperAdmin()) {
-				$_SESSION['sess_safe_mode'] = true;
-			}
-			
-			if ($form->user_remember != '') {
-				setcookie('dc_admin',$cookie_admin,strtotime('+15 days'),'','',DC_ADMIN_SSL);
-			}
-			
-			http::redirect('index.php');
-		}
-		else
-		{
-			if (isset($_COOKIE['dc_admin'])) {
-				unset($_COOKIE['dc_admin']);
-				setcookie('dc_admin',false,-600,'','',DC_ADMIN_SSL);
-			}
-			$_ctx->addError(__('Wrong username or password'));
-		}
-	}
-	
-	# Login form action
-	public static function login($form)
-	{
-		global $_ctx;
-		
-		if ($form->user_id != '' && $form->user_pwd != '') {
-			self::process($form,$form->user_id,$form->user_pwd);
-		}
-		
-		# Send post values to form
-		$form->user_id = $form->user_id;
-	}
-
-	# Recover password form action
-	public static function recover($form)
-	{
-		global $core, $_ctx;
-		
-		if ($form->user_id == '' || $form->user_email == '') {
-			return;
-		}
-		
-		$user_id = $form->user_id;
-		$user_email = $form->user_email;
-		$page_url = http::getHost().$_SERVER['REQUEST_URI'];
-		
-		try {
-			$recover_key = $core->auth->setRecoverKey($user_id,$user_email);
-			
-			$subject = mail::B64Header('DotClear '.__('Password reset'));
-			$message =
-			__('Someone has requested to reset the password for the following site and username.')."\n\n".
-			$page_url."\n".__('Username:').' '.$user_id."\n\n".
-			__('To reset your password visit the following address, otherwise just ignore this email and nothing will happen.')."\n".
-			$page_url.'?akey='.$recover_key;
-			
-			$headers[] = 'From: '.(defined('DC_ADMIN_MAILFROM') && DC_ADMIN_MAILFROM ? DC_ADMIN_MAILFROM : 'dotclear@local');
-			$headers[] = 'Content-Type: text/plain; charset=UTF-8;';
-			
-			mail::sendMail($user_email,$subject,$message,$headers);
-			$_ctx->setMessage(sprintf(__('The e-mail was sent successfully to %s.'),$user_email));
-		}
-		catch (Exception $e) {
-			$_ctx->addError($e->getMessage());
-		}
-		
-		# Send post values to form
-		$form->user_id = $form->user_id;
-		$form->user_email = $form->user_email;
-	}
-
-	# Change password form action
-	public static function change($form)
-	{
-		global $core, $_ctx;
-		
-		if ($form->login_data) {
-			return;
-		}
-		$_ctx->change_pwd = true;
-		
-		$new_pwd = (string) $form->new_pwd;
-		$new_pwd_c = (string) $form->new_pwd_c;
-		
-		try {
-			$tmp_data = explode('/',$form->login_data);
-			if (count($tmp_data) != 3) {
-				throw new Exception();
-			}
-			$data = array(
-				'user_id'=>base64_decode($tmp_data[0]),
-				'cookie_admin'=>$tmp_data[1],
-				'user_remember'=>$tmp_data[2]=='1'
-			);
-			if ($data['user_id'] === false) {
-				throw new Exception();
-			}
-			
-			# Check login informations
-			$check_user = false;
-			if (isset($data['cookie_admin']) && strlen($data['cookie_admin']) == 104)
-			{
-				$user_id = substr($data['cookie_admin'],40);
-				$user_id = @unpack('a32',@pack('H*',$user_id));
-				if (is_array($user_id))
-				{
-					$user_id = $user_id[1];
-					$user_key = substr($data['cookie_admin'],0,40);
-					$check_user = $core->auth->checkUser($user_id,null,$user_key) === true;
-				}
-			}
-			
-			if (!$core->auth->allowPassChange() || !$check_user) {
-				$_ctx->change_pwd = false;
-				throw new Exception();
-			}
-			
-			if ($new_pwd != $new_pwd_c) {
-				throw new Exception(__("Passwords don't match"));
-			}
-			
-			if ($core->auth->checkUser($user_id,$new_pwd) === true) {
-				throw new Exception(__("You didn't change your password."));
-			}
-			
-			$cur = $core->con->openCursor($core->prefix.'user');
-			$cur->user_change_pwd = 0;
-			$cur->user_pwd = $new_pwd;
-			$core->updUser($core->auth->userID(),$cur);
-			
-			$core->session->start();
-			$_SESSION['sess_user_id'] = $user_id;
-			$_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY);
-			
-			if ($data['user_remember']) {
-				setcookie('dc_admin',$data['cookie_admin'],strtotime('+15 days'),'','',DC_ADMIN_SSL);
-			}
-			
-			http::redirect('index.php');
-		}
-		catch (Exception $e) {
-			$_ctx->addError($e->getMessage());
-		}
-		
-		# Send post values to form
-		$form->login_data = $form->login_data;
-	}
-}
-
-# Form fields
-$form = new dcForm($core,'auth','auth.php');
-$form
-	->addField(
-		new dcFieldText('user_id','',array(
-			"label" => __('Username:'))))
-	->addField(
-		new dcFieldPassword('user_pwd','',array(
-			"label" => __('Password:'))))
-	->addField(
-		new dcFieldText('user_email','',array(
-			"label" => __('Email:'))))
-	->addField(
-		new dcFieldPassword('new_pwd','',array(
-			"label" => __('New password:'))))
-	->addField(
-		new dcFieldPassword('new_pwd_c','',array(
-			"label" => __('Confirm password:'))))
-	->addField(
-		new dcFieldCheckbox ('user_remenber',1,array(
-			"label" => __('Remember my ID on this computer'))))
-	->addField(
-		new dcFieldSubmit('auth_login',__('log in'),array(
-			'action' => array('adminPageAuth','login'))))
-	->addField(
-		new dcFieldSubmit('auth_recover',__('recover'),array(
-			'action' => array('adminPageAuth','recover'))))
-	->addField(
-		new dcFieldSubmit('auth_change',__('change'),array(
-			'action' => array('adminPageAuth','change'))))
-	->addField(
-		new dcFieldHidden ('safe_mode','0'))
-	->addField(
-		new dcFieldHidden ('recover','0'))
-	->addField(
-		new dcFieldHidden ('login_data',''))
-	->addField(
-		new dcFieldHidden ('blog',''));
-
-# Context variables
-$_ctx->allow_pass_change = $core->auth->allowPassChange();
-$_ctx->change_pwd = $core->auth->allowPassChange() && $form->new_pwd != '' && $form->new_pwd_c != '' && $form->login_data != '';
-$_ctx->recover = $form->recover = $core->auth->allowPassChange() && !empty($_REQUEST['recover']);
-$_ctx->setSafeMode(!empty($_REQUEST['safe_mode'])); 
-$form->safe_mode = !empty($_REQUEST['safe_mode']);
-$_ctx->akey = false;
-
-# If we have no POST login informations and have COOKIE login informations, go throug auth process
-if ($form->user_id == '' && $form->user_pwd == '' 
- && isset($_COOKIE['dc_admin']) && strlen($_COOKIE['dc_admin']) == 104) {
-
+			$msg = __('Dotclear has been upgraded.').'<!-- '.$changes.' -->';
+		}
+	} catch (Exception $e) {
+		$err = $e->getMessage();
+	}
+}
+
+# If we have POST login informations, go throug auth process
+if (!empty($_POST['user_id']) && !empty($_POST['user_pwd']))
+{
+	$user_id = !empty($_POST['user_id']) ? $_POST['user_id'] : null;
+	$user_pwd = !empty($_POST['user_pwd']) ? $_POST['user_pwd'] : null;
+}
+# If we have COOKIE login informations, go throug auth process
+elseif (isset($_COOKIE['dc_admin']) && strlen($_COOKIE['dc_admin']) == 104)
+{
 	# If we have a remember cookie, go through auth process with user_key
 	$user_id = substr($_COOKIE['dc_admin'],40);
 	$user_id = @unpack('a32',@pack('H*',$user_id));
-	
-	if (is_array($user_id)) {
+	if (is_array($user_id))
+	{
 		$user_id = $user_id[1];
 		$user_key = substr($_COOKIE['dc_admin'],0,40);
-		$user_pwd = '';
-		
-		adminPageAuth::process($form,$user_id,$user_pwd,$user_key);
-	}
-}
-# If we have an akey, go throug send password process
-elseif ($core->auth->allowPassChange() && !empty($_GET['akey'])) {
-	adminPageAuth::send($_GET['akey']);
+		$user_pwd = null;
+	}
+	else
+	{
+		$user_id = null;
+	}
+}
+
+# Recover password
+if ($recover && !empty($_POST['user_id']) && !empty($_POST['user_email']))
+{
+	$user_id = !empty($_POST['user_id']) ? $_POST['user_id'] : null;
+	$user_email = !empty($_POST['user_email']) ? $_POST['user_email'] : '';
+	try
+	{
+		$recover_key = $core->auth->setRecoverKey($user_id,$user_email);
+		
+		$subject = mail::B64Header('DotClear '.__('Password reset'));
+		$message =
+		__('Someone has requested to reset the password for the following site and username.')."\n\n".
+		$page_url."\n".__('Username:').' '.$user_id."\n\n".
+		__('To reset your password visit the following address, otherwise just ignore this email and nothing will happen.')."\n".
+		$page_url.'?akey='.$recover_key;
+		
+		$headers[] = 'From: '.(defined('DC_ADMIN_MAILFROM') && DC_ADMIN_MAILFROM ? DC_ADMIN_MAILFROM : 'dotclear@local');
+		$headers[] = 'Content-Type: text/plain; charset=UTF-8;';
+		
+		mail::sendMail($user_email,$subject,$message,$headers);
+		$msg = sprintf(__('The e-mail was sent successfully to %s.'),$user_email);
+	}
+	catch (Exception $e)
+	{
+		$err = $e->getMessage();
+	}
+}
+# Send new password
+elseif ($akey)
+{
+	try
+	{
+		$recover_res = $core->auth->recoverUserPassword($akey);
+		
+		$subject = mb_encode_mimeheader('DotClear '.__('Your new password'),'UTF-8','B');
+		$message =
+		__('Username:').' '.$recover_res['user_id']."\n".
+		__('Password:').' '.$recover_res['new_pass']."\n\n".
+		preg_replace('/\?(.*)$/','',$page_url);
+		
+		$headers[] = 'From: dotclear@'.$_SERVER['HTTP_HOST'];
+		$headers[] = 'Content-Type: text/plain; charset=UTF-8;';
+		
+		mail::sendMail($recover_res['user_email'],$subject,$message,$headers);
+		$msg = __('Your new password is in your mailbox.');
+	}
+	catch (Exception $e)
+	{
+		$err = $e->getMessage();
+	}
+}
+# Change password and retry to log
+elseif ($change_pwd)
+{
+	try
+	{
+		$tmp_data = explode('/',$_POST['login_data']);
+		if (count($tmp_data) != 3) {
+			throw new Exception();
+		}
+		$data = array(
+			'user_id'=>base64_decode($tmp_data[0]),
+			'cookie_admin'=>$tmp_data[1],
+			'user_remember'=>$tmp_data[2]=='1'
+		);
+		if ($data['user_id'] === false) {
+			throw new Exception();
+		}
+		
+	# Check login informations
+	$check_user = false;
+	if (isset($data['cookie_admin']) && strlen($data['cookie_admin']) == 104)
+	{
+		$user_id = substr($data['cookie_admin'],40);
+		$user_id = @unpack('a32',@pack('H*',$user_id));
+		if (is_array($user_id))
+		{
+			$user_id = $user_id[1];
+			$user_key = substr($data['cookie_admin'],0,40);
+			$check_user = $core->auth->checkUser($user_id,null,$user_key) === true;
+		}
+	}
+	
+		if (!$core->auth->allowPassChange() || !$check_user) {
+			$change_pwd = false;
+			throw new Exception();
+		}
+		
+		if ($_POST['new_pwd'] != $_POST['new_pwd_c']) {
+			throw new Exception(__("Passwords don't match"));
+		}
+		
+		if ($core->auth->checkUser($user_id,$_POST['new_pwd']) === true) {
+			throw new Exception(__("You didn't change your password."));
+		}
+		
+		$cur = $core->con->openCursor($core->prefix.'user');
+		$cur->user_change_pwd = 0;
+		$cur->user_pwd = $_POST['new_pwd'];
+		$core->updUser($core->auth->userID(),$cur);
+		
+		$core->session->start();
+		$_SESSION['sess_user_id'] = $user_id;
+		$_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY);
+		
+		if ($data['user_remember'])
+		{
+			setcookie('dc_admin',$data['cookie_admin'],strtotime('+15 days'),'','',DC_ADMIN_SSL);
+		}
+		
+		http::redirect('index.php');
+	}
+	catch (Exception $e)
+	{
+		$err = $e->getMessage();
+	}
+}
+# Try to log
+elseif ($user_id !== null && ($user_pwd !== null || $user_key !== null))
+{
+	# We check the user
+	$check_user = $core->auth->checkUser($user_id,$user_pwd,$user_key) === true;
+	
+	$cookie_admin = http::browserUID(DC_MASTER_KEY.$user_id.
+		crypt::hmac(DC_MASTER_KEY,$user_pwd)).bin2hex(pack('a32',$user_id));
+	
+	if ($check_user && $core->auth->mustChangePassword())
+	{
+		$login_data = join('/',array(
+			base64_encode($user_id),
+			$cookie_admin,
+			empty($_POST['user_remember'])?'0':'1'
+		));
+		
+		if (!$core->auth->allowPassChange()) {
+			$err = __('You have to change your password before you can login.');
+		} else {
+			$err = __('In order to login, you have to change your password now.');
+			$change_pwd = true;
+		}
+	}
+	elseif ($check_user && !empty($_POST['safe_mode']) && !$core->auth->isSuperAdmin()) 
+	{
+		$err = __('Safe Mode can only be used for super administrators.');
+	}
+	elseif ($check_user)
+	{
+		$core->session->start();
+		$_SESSION['sess_user_id'] = $user_id;
+		$_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY);
+		
+		if (!empty($_POST['blog'])) {
+			$_SESSION['sess_blog_id'] = $_POST['blog'];
+		}
+		
+		if (!empty($_POST['safe_mode']) && $core->auth->isSuperAdmin()) {
+			$_SESSION['sess_safe_mode'] = true;
+		}
+		
+		if (!empty($_POST['user_remember'])) {
+			setcookie('dc_admin',$cookie_admin,strtotime('+15 days'),'','',DC_ADMIN_SSL);
+		}
+		
+		http::redirect('index.php');
+	}
+	else
+	{
+		if (isset($_COOKIE['dc_admin'])) {
+			unset($_COOKIE['dc_admin']);
+			setcookie('dc_admin',false,-600,'','',DC_ADMIN_SSL);
+		}
+		$err = __('Wrong username or password');
+	}
 }
 
 if (isset($_GET['user'])) {
-	$form->user_id = $_GET['user'];
-}
-
-$form->setup();
-
-$core->tpl->display('auth.html.twig');
+	$user_id = $_GET['user'];
+}
+
+header('Content-Type: text/html; charset=UTF-8');
 ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+xml:lang="<?php echo $dlang; ?>" lang="<?php echo $dlang; ?>">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  <meta http-equiv="Content-Script-Type" content="text/javascript" />
+  <meta http-equiv="Content-Style-Type" content="text/css" />
+  <meta http-equiv="Content-Language" content="<?php echo $dlang; ?>" />
+  <meta name="ROBOTS" content="NOARCHIVE,NOINDEX,NOFOLLOW" />
+  <meta name="GOOGLEBOT" content="NOSNIPPET" />
+  <title><?php echo html::escapeHTML(DC_VENDOR_NAME); ?></title>
+  
+<?php
+echo dcPage::jsLoadIE7();
+echo dcPage::jsCommon();
+?>
+  
+	<link rel="stylesheet" href="style/default.css" type="text/css" media="screen" />
+	 
+  <?php
+  # --BEHAVIOR-- loginPageHTMLHead
+  $core->callBehavior('loginPageHTMLHead');
+  ?>
+  
+  <script type="text/javascript">
+  //<![CDATA[
+  $(window).load(function() {
+    var uid = $('input[name=user_id]');
+    var upw = $('input[name=user_pwd]');
+    uid.focus();
+    
+    if (upw.length == 0) { return; }
+    
+    if ($.browser.mozilla) {
+      uid.keypress(processKey);
+    } else {
+      uid.keydown(processKey);
+    }
+    function processKey(evt) {
+      if (evt.keyCode == 13 && upw.val() == '') {
+         upw.focus();
+	    return false;
+      }
+	 return true;
+    };
+    $.cookie('dc_admin_test_cookie',true);
+    if ($.cookie('dc_admin_test_cookie')) {
+      $('#cookie_help').hide();
+      $.cookie('dc_admin_test_cookie', '', {'expires': -1});
+    } else {
+      $('#cookie_help').show();
+    }
+    $('#issue #more').toggleWithLegend($('#issue').children().not('#more'));
+  });
+  //]]>
+  </script>
+</head>
+
+<body id="dotclear-admin" class="auth">
+
+<form action="auth.php" method="post" id="login-screen">
+<h1><?php echo html::escapeHTML(DC_VENDOR_NAME); ?></h1>
+
+<?php
+if ($err) {
+	echo '<div class="error">'.$err.'</div>';
+}
+if ($msg) {
+	echo '<p class="message">'.$msg.'</p>';
+}
+
+if ($akey)
+{
+	echo '<p><a href="auth.php">'.__('Back to login screen').'</a></p>';
+}
+elseif ($recover)
+{
+	echo
+	'<fieldset><legend>'.__('Request a new password').'</legend>'.
+	'<p><label for="user_id">'.__('Username:').'</label> '.
+	form::field(array('user_id','user_id'),20,32,html::escapeHTML($user_id)).'</p>'.
+	
+	'<p><label for="user_email">'.__('Email:').'</label> '.
+	form::field(array('user_email','user_email'),20,255,html::escapeHTML($user_email)).'</p>'.
+	
+	'<p><input type="submit" value="'.__('recover').'" />'.
+	form::hidden(array('recover'),1).'</p>'.
+	'</fieldset>'.
+	
+	'<div id="issue">'.
+	'<p><a href="auth.php">'.__('Back to login screen').'</a></p></div>';
+}
+elseif ($change_pwd)
+{
+	echo
+	'<fieldset><legend>'.__('Change your password').'</legend>'.
+	'<p><label for="new_pwd">'.__('New password:').'</label> '.
+	form::password(array('new_pwd','new_pwd'),20,255).'</p>'.
+	
+	'<p><label for="new_pwd_c">'.__('Confirm password:').'</label> '.
+	form::password(array('new_pwd_c','new_pwd_c'),20,255).'</p>'.
+	'</fielset>'.
+	
+	'<p><input type="submit" value="'.__('change').'" />'.
+	form::hidden('login_data',$login_data).'</p>';
+}
+else
+{
+	if (is_callable(array($core->auth,'authForm')))
+	{
+		echo $core->auth->authForm($user_id);
+	}
+	else
+	{
+		if ($safe_mode) {
+			echo '<fieldset>';
+			echo '<legend>'.__('Safe mode login').'</legend>';
+			echo 
+				'<p class="form-note info">'.
+				__('This mode allows you to login without activating any of your plugins. This may be useful to solve compatibility problems').'&nbsp;<br />'.
+				__('Disable or delete any plugin suspected to cause trouble, then log out and log back in normally.').
+				'</p>';
+		}
+		else {
+			echo '<div class="fieldset">';
+		}
+
+		echo
+		'<p><label for="user_id">'.__('Username:').'</label> '.
+		form::field(array('user_id','user_id'),20,32,html::escapeHTML($user_id)).'</p>'.
+		
+		'<p><label for="user_pwd">'.__('Password:').'</label> '.
+		form::password(array('user_pwd','user_pwd'),20,255).'</p>'.
+		
+		'<p>'.
+		form::checkbox(array('user_remember','user_remember'),1).
+		'<label for="user_remember" class="classic">'.
+		__('Remember my ID on this computer').'</label></p>'.
+		
+		'<p><input type="submit" value="'.__('log in').'" /></p>';
+		
+		if (!empty($_REQUEST['blog'])) {
+			echo form::hidden('blog',html::escapeHTML($_REQUEST['blog']));
+		}
+		if($safe_mode) {
+			echo 
+			form::hidden('safe_mode',1).
+			'</fieldset>';
+		}
+		else {
+			echo '</div>';
+		}
+		echo
+		'<p id="cookie_help" class="error">'.__('You must accept cookies in order to use the private area.').'</p>';
+
+		echo '<div id="issue">';
+		
+		if ($safe_mode) {
+			echo
+			'<p><a href="auth.php" id="normal_mode_link">'.__('Get back to normal authentication').'</a></p>';
+		} else {
+			echo '<p id="more"><strong>'.__('Connection issue?').'</strong></p>';
+			if ($core->auth->allowPassChange()) {
+				echo '<p><a href="auth.php?recover=1">'.__('I forgot my password').'</a></p>';
+			}
+			echo '<p><a href="auth.php?safe_mode=1" id="safe_mode_link">'.__('I want to log in in safe mode').'</a></p>';
+		}
+		
+		echo '</div>';
+	}
+}
+?>
+</form>
+</body>
+</html>
Index: admin/categories.php
===================================================================
--- admin/categories.php	(revision 907)
+++ admin/categories.php	(revision 1030)
@@ -20,4 +20,5 @@
 	try
 	{
+		# Check if category to delete exists
 		$c = $core->blog->getCategory((integer) $_POST['del_cat']);
 		if ($c->isEmpty()) {
@@ -25,5 +26,25 @@
 		}
 		unset($c);
+		
+		# Check if category where to move posts exists
+		$mov_cat = (integer) $_POST['mov_cat'];
+		$mov_cat = $mov_cat ? $mov_cat : null;
+		if ($mov_cat !== null) {
+			$c = $core->blog->getCategory((integer) $_POST['del_cat']);
+			if ($c->isEmpty()) {
+				throw new Exception(__('This category does not exist.'));
+			}
+			if ($mov_cat == $_POST['del_cat']) {
+				throw new Exception(__('The entries cannot be moved to the category you choose to delete.'));
+			}
+			unset($c);
+		}
+		
+		# Move posts
+		$core->blog->changePostsCategory($_POST['del_cat'],$mov_cat);
+		
+		# Delete category
 		$core->blog->delCategory($_POST['del_cat']);
+		
 		http::redirect('categories.php?del=1');
 	}
@@ -143,5 +164,6 @@
 if (!$rs->isEmpty())
 {
-	$deletable = array();
+	$cats = array();
+	$dest = array('&nbsp;' => '');
 	$l = $rs->level;
 	$full_name = array($rs->cat_title);
@@ -154,22 +176,22 @@
 		}
 		$full_name[] = html::escapeHTML($rs->cat_title);
-		if ($rs->nb_post == 0) {
-			$deletable[implode(' / ',$full_name)] = $rs->cat_id;
-		}
+		
+		$cats[implode(' / ',$full_name)] = $rs->cat_id;
+		$dest[implode(' / ',$full_name)] = $rs->cat_id;
+		
 		$l = $rs->level;
 	}
 	
-	if (count($deletable) > 0)
-	{
-		echo
-		'<form action="categories.php" method="post" id="delete-category">'.
-		'<fieldset><legend>'.__('Remove a category').'</legend>'.
-		'<p><label for="del_cat">'.__('Choose a category to remove:').' '.
-		form::combo('del_cat',$deletable).'</label></p> '.
-		'<p><input type="submit" value="'.__('Delete').'" class="delete" /></p>'.
-		$core->formNonce().
-		'</fieldset>'.
-		'</form>';
-	}
+	echo
+	'<form action="categories.php" method="post" id="delete-category">'.
+	'<fieldset><legend>'.__('Remove a category').'</legend>'.
+	'<p><label for="del_cat">'.__('Choose a category to remove:').' '.
+	form::combo('del_cat',$cats).'</label></p> '.
+	'<p><label for="mov_cat">'.__('And choose the category which will receive its entries:').' '.
+	form::combo('mov_cat',$dest).'</label></p> '.
+	'<p><input type="submit" value="'.__('Delete').'" class="delete" /></p>'.
+	$core->formNonce().
+	'</fieldset>'.
+	'</form>';
 	
 	echo
Index: admin/comments.php
===================================================================
--- admin/comments.php	(revision 796)
+++ admin/comments.php	(revision 1054)
@@ -15,4 +15,15 @@
 dcPage::check('usage,contentadmin');
 
+if (!empty($_POST['delete_all_spam']))
+{
+	try {
+		$core->blog->delJunkComments();
+		$_SESSION['comments_del_spam'] = true;
+		http::redirect('comments.php');
+	} catch (Exception $e) {
+		$core->error->add($e->getMessage());
+	}
+}
+
 # Creating filter combo boxes
 # Filter form we'll put in html_block
@@ -164,4 +175,9 @@
 if (!$core->error->flag())
 {
+	if (isset($_SESSION['comments_del_spam'])) {
+		dcPage::message(__('Spam comments have been successfully deleted.'));
+		unset($_SESSION['comments_del_spam']);
+	}
+	
 	# Filters
 	if (!$show_filters) {
@@ -210,11 +226,28 @@
 	
 	if (!$with_spam) {
+
 		$spam_count = $core->blog->getComments(array('comment_status'=>-2),true)->f(0);
-		if ($spam_count == 1) {
-			echo '<p>'.sprintf(__('You have one spam comments.'),'<strong>'.$spam_count.'</strong>').' '.
-			'<a href="comments.php?status=-2">'.__('Show it.').'</a></p>';
-		} elseif ($spam_count > 1) {
-			echo '<p>'.sprintf(__('You have %s spam comments.'),'<strong>'.$spam_count.'</strong>').' '.
-			'<a href="comments.php?status=-2">'.__('Show them.').'</a></p>';
+		
+		if ($spam_count > 0) {
+			
+			echo 
+				'<form action="comments.php" method="post" class="fieldset">';
+
+			if ($spam_count == 1) {
+				echo '<p>'.sprintf(__('You have one spam comments.'),'<strong>'.$spam_count.'</strong>').' '.
+				'<a href="comments.php?status=-2">'.__('Show it.').'</a></p>';
+			} elseif ($spam_count > 1) {
+				echo '<p>'.sprintf(__('You have %s spam comments.'),'<strong>'.$spam_count.'</strong>').' '.
+				'<a href="comments.php?status=-2">'.__('Show them.').'</a></p>';
+			}
+			
+			echo
+				$core->formNonce().
+				'<input name="delete_all_spam" class="delete" type="submit" value="'.__('Delete all spams').'" /></p>';
+
+			# --BEHAVIOR-- adminCommentsSpamForm
+			$core->callBehavior('adminCommentsSpamForm',$core);
+
+			echo '</form>';
 		}
 	}
Index: admin/comments_actions.php
===================================================================
--- admin/comments_actions.php	(revision 270)
+++ admin/comments_actions.php	(revision 1041)
@@ -52,4 +52,9 @@
 	$co = $core->blog->getComments($params);
 	
+	$co_ids = array();
+	while ($co->fetch())	{
+		$co_ids[] = $co->comment_id;
+	}
+	
 	# --BEHAVIOR-- adminCommentsActions
 	$core->callBehavior('adminCommentsActions',$core,$co,$action,$redir);
@@ -64,11 +69,8 @@
 		}
 		
-		while ($co->fetch())
-		{
-			try {
-				$core->blog->updCommentStatus($co->comment_id,$status);
-			} catch (Exception $e) {
-				$core->error->add($e->getMessage());
-			}
+		try {
+			$core->blog->updCommentsStatus($co_ids,$status);
+		} catch (Exception $e) {
+			$core->error->add($e->getMessage());
 		}
 		
@@ -79,14 +81,18 @@
 	elseif ($action == 'delete')
 	{
-		while ($co->fetch())
-		{
-			try {
+		try {
+			// Backward compatibility
+			foreach($co_ids as $comment_id)
+			{
 				# --BEHAVIOR-- adminBeforeCommentDelete
-				$core->callBehavior('adminBeforeCommentDelete',$co->comment_id);				
-				
-				$core->blog->delComment($co->comment_id);
-			} catch (Exception $e) {
-				$core->error->add($e->getMessage());
+				$core->callBehavior('adminBeforeCommentDelete',$comment_id);				
 			}
+			
+			# --BEHAVIOR-- adminBeforeCommentsDelete
+			$core->callBehavior('adminBeforeCommentsDelete',$co_ids);
+			
+			$core->blog->delComments($co_ids);
+		} catch (Exception $e) {
+			$core->error->add($e->getMessage());
 		}
 		
Index: admin/index.php
===================================================================
--- admin/index.php	(revision 1056)
+++ admin/index.php	(revision 1000)
@@ -13,9 +13,4 @@
 if (!empty($_GET['pf'])) {
 	require dirname(__FILE__).'/../inc/load_plugin_file.php';
-	exit;
-}
-if (!empty($_GET['tf'])) {
-	define('DC_CONTEXT_ADMIN',true);
-	require dirname(__FILE__).'/../inc/load_theme_file.php';
 	exit;
 }
@@ -161,5 +156,5 @@
 			foreach ($feed->items as $item)
 			{
-				$dt = isset($item->link) ? '<a href="'.$item->link.'"'.$v.'" title="'.$item->title.' '.__('(external link)').'">'.
+				$dt = isset($item->link) ? '<a href="'.$item->link.'" title="'.$item->title.' '.__('(external link)').'">'.
 					$item->title.'</a>' : $item->title;
 			
@@ -167,10 +162,10 @@
 					$latest_news .=
 					'<dt>'.$dt.'</dt>'.
-					'<dd><p><strong>'.dt::dt2str('%d %B %Y',$item->pubdate,'Europe/Paris').'</strong>: '.
+					'<dd><p><strong>'.dt::dt2str(__('%d %B %Y:'),$item->pubdate,'Europe/Paris').'</strong> '.
 					'<em>'.text::cutString(html::clean($item->content),120).'...</em></p></dd>';
 				} else {
 					$latest_news .=
 					'<dt>'.$dt.'</dt>'.
-					'<dd>'.dt::dt2str('%d %B %Y',$item->pubdate,'Europe/Paris').'</dd>';
+					'<dd>'.dt::dt2str(__('%d %B %Y:'),$item->pubdate,'Europe/Paris').'</dd>';
 				}
 				$i++;
Index: admin/js/_comments.js
===================================================================
--- admin/js/_comments.js	(revision 0)
+++ admin/js/_comments.js	(revision 1049)
@@ -11,4 +11,33 @@
 	
 	td.insertBefore(img,td.firstChild);
+};
+
+dotclear.commentsExpander = function(line,lines) {
+	var td = line.firstChild;
+	
+	var img = document.createElement('img');
+	img.src = dotclear.img_plus_src;
+	img.alt = dotclear.img_plus_alt;
+	img.className = 'expand';
+	$(img).css('cursor','pointer');
+	img.lines = lines;
+	img.onclick = function() { dotclear.viewCommentsContent(this,this.lines); };
+	
+	td.insertBefore(img,td.firstChild);
+};
+
+dotclear.viewCommentsContent = function(img,lines) {
+	lines.each(function() {
+		var td = this.firstChild;
+		td.firstChild.click();
+	});
+
+	if (img.alt == dotclear.img_plus_alt) {
+		img.src = dotclear.img_minus_src;
+		img.alt = dotclear.img_minus_alt;
+	} else {
+		img.src = dotclear.img_plus_src;
+		img.alt = dotclear.img_plus_alt;
+	}
 };
 
@@ -75,4 +104,7 @@
 
 $(function() {
+	$('#form-comments tr:not(.line)').each(function() {
+		dotclear.commentsExpander(this,$('#form-comments tr.line'));
+	});
 	$('#form-comments tr.line').each(function() {
 		dotclear.commentExpander(this);
@@ -83,3 +115,6 @@
 	$('#form-comments td input[type=checkbox]').enableShiftClick();
 	dotclear.commentsActionsHelper();
+	$('form input[type=submit][name=delete_all_spam]').click(function(){
+		return window.confirm(dotclear.msg.confirm_spam_delete);
+	});
 });
Index: admin/js/_media.js
===================================================================
--- admin/js/_media.js	(revision 740)
+++ admin/js/_media.js	(revision 1039)
@@ -17,5 +17,10 @@
 	
 	if (!$.browser.opera) {
-		if (dotclear.candyUpload_force_init == '1') {
+		var upldr = $('<a href="#" id="cu-enable">' + dotclear.msg.activate_enhanced_uploader + '</a>').click( function() {
+			candyUploadInit();
+			return false;
+		});
+		$('#media-upload').append($('<div></div>').append(upldr));
+		if ((dotclear.candyUpload_force_init == '1') || (($.cookie('dc_candy_upl') == 1))) {
 			candyUploadInit();
 		}
@@ -38,4 +43,13 @@
 					this.ctrl.btn_browse.hide();
 					this.ctrl.msg.html(dotclear.msg.load_enhanced_uploader);
+					var l = $('<a href="#">' + dotclear.msg.disable_enhanced_uploader + '</a>').click(function() {
+						_this.upldr.destroy();
+						_this.ctrl.block.empty().remove();
+						$('#media-upload').show();
+						delete _this;
+						$.cookie('dc_candy_upl','',{expires:-1});
+						return false;
+					});
+					this.ctrl.disable = $('<div class="cu-disable"></div>').append(l).appendTo(this.ctrl.block);
 				},
 				flashReady: function() {
Index: admin/js/_posts_actions.js
===================================================================
--- admin/js/_posts_actions.js	(revision 1035)
+++ admin/js/_posts_actions.js	(revision 1035)
@@ -0,0 +1,8 @@
+$(function() {
+	$('#new_auth_id').autocomplete(usersList, 
+	{
+		delay: 1000,
+		matchSubset: true,
+		matchContains: true
+	});
+});
Index: admin/js/_posts_list.js
===================================================================
--- admin/js/_posts_list.js	(revision 740)
+++ admin/js/_posts_list.js	(revision 1034)
@@ -8,7 +8,36 @@
 	$(img).css('cursor','pointer');
 	img.line = line;
-	img.onclick = function() { dotclear.viewPostContent(this,this.line); positionFooter(); };
+	img.onclick = function() { dotclear.viewPostContent(this,this.line); };
 	
 	td.insertBefore(img,td.firstChild);
+};
+
+dotclear.postsExpander = function(line,lines) {
+	var td = line.firstChild;
+
+	var img = document.createElement('img');
+	img.src = dotclear.img_plus_src;
+	img.alt = dotclear.img_plus_alt;
+	img.className = 'expand';
+	$(img).css('cursor','pointer');
+	img.lines = lines;
+	img.onclick = function() { dotclear.viewPostsContent(this,this.lines); };
+
+	td.insertBefore(img,td.firstChild);
+};
+
+dotclear.viewPostsContent = function(img,lines) {
+	lines.each(function() {
+		var td = this.firstChild;
+		td.firstChild.click();
+	});
+
+	if (img.alt == dotclear.img_plus_alt) {
+		img.src = dotclear.img_minus_src;
+		img.alt = dotclear.img_minus_alt;
+	} else {
+		img.src = dotclear.img_plus_src;
+		img.alt = dotclear.img_plus_alt;
+	}
 };
 
@@ -69,4 +98,12 @@
 
 $(function() {
+	// Entry type switcher
+	$('#type').change(function() {
+		this.form.submit();
+	});
+
+	$('#form-entries tr:not(.line)').each(function() {
+		dotclear.postsExpander(this,$('#form-entries tr.line'));
+	});
 	$('#form-entries tr.line').each(function() {
 		dotclear.postExpander(this);
Index: admin/js/common.js
===================================================================
--- admin/js/common.js	(revision 958)
+++ admin/js/common.js	(revision 1002)
@@ -159,6 +159,6 @@
 	var sizeBox = function() {
 		This.css('height','auto');
-		if ($('body').height() > This.height()) {
-			This.css('height',$('body').height() + 'px');
+		if ($('#main').height() > This.height()) {
+			This.css('height',$('#main').height() + 'px');
 		}
 	};
Index: admin/js/jquery/jquery-ui.custom.js
===================================================================
--- admin/js/jquery/jquery-ui.custom.js	(revision 906)
+++ admin/js/jquery/jquery-ui.custom.js	(revision 1048)
@@ -1,3 +1,3 @@
-/*! jQuery UI - v1.9.0 - 2012-10-25
+/*! jQuery UI - v1.9.2 - 2012-11-29
 * http://jqueryui.com
 * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.sortable.js, jquery.ui.accordion.js
@@ -18,5 +18,5 @@
 
 $.extend( $.ui, {
-	version: "1.9.0",
+	version: "1.9.2",
 
 	keyCode: {
@@ -65,5 +65,5 @@
 	scrollParent: function() {
 		var scrollParent;
-		if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
+		if (($.ui.ie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
 			scrollParent = this.parents().filter(function() {
 				return (/(relative|absolute|fixed)/).test($.css(this,'position')) && (/(auto|scroll)/).test($.css(this,'overflow')+$.css(this,'overflow-y')+$.css(this,'overflow-x'));
@@ -124,4 +124,81 @@
 });
 
+// selectors
+function focusable( element, isTabIndexNotNaN ) {
+	var map, mapName, img,
+		nodeName = element.nodeName.toLowerCase();
+	if ( "area" === nodeName ) {
+		map = element.parentNode;
+		mapName = map.name;
+		if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
+			return false;
+		}
+		img = $( "img[usemap=#" + mapName + "]" )[0];
+		return !!img && visible( img );
+	}
+	return ( /input|select|textarea|button|object/.test( nodeName ) ?
+		!element.disabled :
+		"a" === nodeName ?
+			element.href || isTabIndexNotNaN :
+			isTabIndexNotNaN) &&
+		// the element and all of its ancestors must be visible
+		visible( element );
+}
+
+function visible( element ) {
+	return $.expr.filters.visible( element ) &&
+		!$( element ).parents().andSelf().filter(function() {
+			return $.css( this, "visibility" ) === "hidden";
+		}).length;
+}
+
+$.extend( $.expr[ ":" ], {
+	data: $.expr.createPseudo ?
+		$.expr.createPseudo(function( dataName ) {
+			return function( elem ) {
+				return !!$.data( elem, dataName );
+			};
+		}) :
+		// support: jQuery <1.8
+		function( elem, i, match ) {
+			return !!$.data( elem, match[ 3 ] );
+		},
+
+	focusable: function( element ) {
+		return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
+	},
+
+	tabbable: function( element ) {
+		var tabIndex = $.attr( element, "tabindex" ),
+			isTabIndexNaN = isNaN( tabIndex );
+		return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
+	}
+});
+
+// support
+$(function() {
+	var body = document.body,
+		div = body.appendChild( div = document.createElement( "div" ) );
+
+	// access offsetHeight before setting the style to prevent a layout bug
+	// in IE 9 which causes the element to continue to take up space even
+	// after it is removed from the DOM (#8026)
+	div.offsetHeight;
+
+	$.extend( div.style, {
+		minHeight: "100px",
+		height: "auto",
+		padding: 0,
+		borderWidth: 0
+	});
+
+	$.support.minHeight = div.offsetHeight === 100;
+	$.support.selectstart = "onselectstart" in div;
+
+	// set display to none to avoid a layout bug in IE
+	// http://dev.jquery.com/ticket/4014
+	body.removeChild( div ).style.display = "none";
+});
+
 // support: jQuery <1.8
 if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
@@ -171,81 +248,17 @@
 }
 
-// selectors
-function focusable( element, isTabIndexNotNaN ) {
-	var map, mapName, img,
-		nodeName = element.nodeName.toLowerCase();
-	if ( "area" === nodeName ) {
-		map = element.parentNode;
-		mapName = map.name;
-		if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
-			return false;
-		}
-		img = $( "img[usemap=#" + mapName + "]" )[0];
-		return !!img && visible( img );
-	}
-	return ( /input|select|textarea|button|object/.test( nodeName ) ?
-		!element.disabled :
-		"a" === nodeName ?
-			element.href || isTabIndexNotNaN :
-			isTabIndexNotNaN) &&
-		// the element and all of its ancestors must be visible
-		visible( element );
+// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
+if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
+	$.fn.removeData = (function( removeData ) {
+		return function( key ) {
+			if ( arguments.length ) {
+				return removeData.call( this, $.camelCase( key ) );
+			} else {
+				return removeData.call( this );
+			}
+		};
+	})( $.fn.removeData );
 }
 
-function visible( element ) {
-	return !$( element ).parents().andSelf().filter(function() {
-		return $.css( this, "visibility" ) === "hidden" ||
-			$.expr.filters.hidden( this );
-	}).length;
-}
-
-$.extend( $.expr[ ":" ], {
-	data: $.expr.createPseudo ?
-		$.expr.createPseudo(function( dataName ) {
-			return function( elem ) {
-				return !!$.data( elem, dataName );
-			};
-		}) :
-		// support: jQuery <1.8
-		function( elem, i, match ) {
-			return !!$.data( elem, match[ 3 ] );
-		},
-
-	focusable: function( element ) {
-		return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
-	},
-
-	tabbable: function( element ) {
-		var tabIndex = $.attr( element, "tabindex" ),
-			isTabIndexNaN = isNaN( tabIndex );
-		return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
-	}
-});
-
-// support
-$(function() {
-	var body = document.body,
-		div = body.appendChild( div = document.createElement( "div" ) );
-
-	// access offsetHeight before setting the style to prevent a layout bug
-	// in IE 9 which causes the element to continue to take up space even
-	// after it is removed from the DOM (#8026)
-	div.offsetHeight;
-
-	$.extend( div.style, {
-		minHeight: "100px",
-		height: "auto",
-		padding: 0,
-		borderWidth: 0
-	});
-
-	$.support.minHeight = div.offsetHeight === 100;
-	$.support.selectstart = "onselectstart" in div;
-
-	// set display to none to avoid a layout bug in IE
-	// http://dev.jquery.com/ticket/4014
-	body.removeChild( div ).style.display = "none";
-});
-
 
 
@@ -253,4 +266,10 @@
 
 // deprecated
+
+(function() {
+	var uaMatch = /msie ([\w.]+)/.exec( navigator.userAgent.toLowerCase() ) || [];
+	$.ui.ie = uaMatch.length ? true : false;
+	$.ui.ie6 = parseFloat( uaMatch[ 1 ], 10 ) === 6;
+})();
 
 $.fn.extend({
@@ -424,5 +443,5 @@
 		// always use the name + a colon as the prefix, e.g., draggable:start
 		// don't prefix for widgets that aren't DOM-based
-		widgetEventPrefix: name
+		widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
 	}, prototype, {
 		constructor: constructor,
@@ -465,6 +484,15 @@
 		for ( key in input[ inputIndex ] ) {
 			value = input[ inputIndex ][ key ];
-			if (input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
-				target[ key ] = $.isPlainObject( value ) ? $.widget.extend( {}, target[ key ], value ) : value;
+			if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
+				// Clone objects
+				if ( $.isPlainObject( value ) ) {
+					target[ key ] = $.isPlainObject( target[ key ] ) ?
+						$.widget.extend( {}, target[ key ], value ) :
+						// Don't extend strings, arrays, etc. with objects
+						$.widget.extend( {}, value );
+				// Copy everything else by reference
+				} else {
+					target[ key ] = value;
+				}
 			}
 		}
@@ -474,5 +502,5 @@
 
 $.widget.bridge = function( name, object ) {
-	var fullName = object.prototype.widgetFullName;
+	var fullName = object.prototype.widgetFullName || name;
 	$.fn[ name ] = function( options ) {
 		var isMethodCall = typeof options === "string",
@@ -510,5 +538,5 @@
 					instance.option( options || {} )._init();
 				} else {
-					new object( options, this );
+					$.data( this, fullName, new object( options, this ) );
 				}
 			});
@@ -519,5 +547,5 @@
 };
 
-$.Widget = function( options, element ) {};
+$.Widget = function( /* options, element */ ) {};
 $.Widget._childConstructors = [];
 
@@ -551,5 +579,11 @@
 			$.data( element, this.widgetName, this );
 			$.data( element, this.widgetFullName, this );
-			this._on({ remove: "destroy" });
+			this._on( true, this.element, {
+				remove: function( event ) {
+					if ( event.target === element ) {
+						this.destroy();
+					}
+				}
+			});
 			this.document = $( element.style ?
 				// element within the document
@@ -669,16 +703,26 @@
 	},
 
-	_on: function( element, handlers ) {
+	_on: function( suppressDisabledCheck, element, handlers ) {
+		var delegateElement,
+			instance = this;
+
+		// no suppressDisabledCheck flag, shuffle arguments
+		if ( typeof suppressDisabledCheck !== "boolean" ) {
+			handlers = element;
+			element = suppressDisabledCheck;
+			suppressDisabledCheck = false;
+		}
+
 		// no element argument, shuffle and use this.element
 		if ( !handlers ) {
 			handlers = element;
 			element = this.element;
+			delegateElement = this.widget();
 		} else {
 			// accept selectors, DOM elements
-			element = $( element );
+			element = delegateElement = $( element );
 			this.bindings = this.bindings.add( element );
 		}
 
-		var instance = this;
 		$.each( handlers, function( event, handler ) {
 			function handlerProxy() {
@@ -686,6 +730,7 @@
 				// - disabled as an array instead of boolean
 				// - disabled class as method for disabling individual parts
-				if ( instance.options.disabled === true ||
-						$( this ).hasClass( "ui-state-disabled" ) ) {
+				if ( !suppressDisabledCheck &&
+						( instance.options.disabled === true ||
+							$( this ).hasClass( "ui-state-disabled" ) ) ) {
 					return;
 				}
@@ -704,5 +749,5 @@
 				selector = match[2];
 			if ( selector ) {
-				instance.widget().delegate( selector, eventName, handlerProxy );
+				delegateElement.delegate( selector, eventName, handlerProxy );
 			} else {
 				element.bind( eventName, handlerProxy );
@@ -831,5 +876,5 @@
 
 $.widget("ui.mouse", {
-	version: "1.9.0",
+	version: "1.9.2",
 	options: {
 		cancel: 'input,textarea,button,select,option',
@@ -916,5 +961,5 @@
 
 		event.preventDefault();
-		
+
 		mouseHandled = true;
 		return true;
@@ -923,5 +968,5 @@
 	_mouseMove: function(event) {
 		// IE mouseup check - mouseup happened when mouse was out of window
-		if ($.browser.msie && !(document.documentMode >= 9) && !event.button) {
+		if ($.ui.ie && !(document.documentMode >= 9) && !event.button) {
 			return this._mouseUp(event);
 		}
@@ -982,5 +1027,5 @@
 
 $.widget("ui.sortable", $.ui.mouse, {
-	version: "1.9.0",
+	version: "1.9.2",
 	widgetEventPrefix: "sort",
 	ready: false,
@@ -1528,12 +1573,11 @@
 		var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
 
-		for (var i=0; i < this.items.length; i++) {
-
+		this.items = $.grep(this.items, function (item) {
 			for (var j=0; j < list.length; j++) {
-				if(list[j] == this.items[i].item[0])
-					this.items.splice(i,1);
+				if(list[j] == item.item[0])
+					return false;
 			};
-
-		};
+			return true;
+		});
 
 	},
@@ -1701,14 +1745,24 @@
 			this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
 			this.containers[innermostIndex].containerCache.over = 1;
-		} else if(this.currentContainer != this.containers[innermostIndex]) {
+		} else {
 
 			//When entering a new container, we will find the item with the least distance and append our item near it
-			var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[innermostIndex].floating ? 'left' : 'top'];
+			var dist = 10000; var itemWithLeastDistance = null;
+			var posProperty = this.containers[innermostIndex].floating ? 'left' : 'top';
+			var sizeProperty = this.containers[innermostIndex].floating ? 'width' : 'height';
+			var base = this.positionAbs[posProperty] + this.offset.click[posProperty];
 			for (var j = this.items.length - 1; j >= 0; j--) {
 				if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) continue;
-				var cur = this.containers[innermostIndex].floating ? this.items[j].item.offset().left : this.items[j].item.offset().top;
+				if(this.items[j].item[0] == this.currentItem[0]) continue;
+				var cur = this.items[j].item.offset()[posProperty];
+				var nearBottom = false;
+				if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
+					nearBottom = true;
+					cur += this.items[j][sizeProperty];
+				}
+
 				if(Math.abs(cur - base) < dist) {
 					dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
-					this.direction = (cur - base > 0) ? 'down' : 'up';
+					this.direction = nearBottom ? "up": "down";
 				}
 			}
@@ -1788,5 +1842,5 @@
 
 		if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
-		|| (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
+		|| (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.ui.ie)) //Ugly IE fix
 			po = { top: 0, left: 0 };
 
@@ -2063,5 +2117,5 @@
 
 $.widget( "ui.accordion", {
-	version: "1.9.0",
+	version: "1.9.2",
 	options: {
 		active: 0,
@@ -2098,6 +2152,6 @@
 			.hide();
 
-		// don't allow collapsible: false and active: false
-		if ( !options.collapsible && options.active === false ) {
+		// don't allow collapsible: false and active: false / null
+		if ( !options.collapsible && (options.active === false || options.active == null) ) {
 			options.active = 0;
 		}
@@ -2114,5 +2168,4 @@
 
 		this._createIcons();
-		this.originalHeight = this.element[0].style.height;
 		this.refresh();
 
@@ -2237,5 +2290,4 @@
 			});
 		if ( this.options.heightStyle !== "content" ) {
-			this.element.css( "height", this.originalHeight );
 			contents.css( "height", "" );
 		}
@@ -2328,5 +2380,4 @@
 			parent = this.element.parent();
 
-		this.element.css( "height", this.originalHeight );
 
 		if ( heightStyle === "fill" ) {
@@ -2367,11 +2418,7 @@
 			this.headers.next()
 				.each(function() {
-					maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
+					maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
 				})
 				.height( maxHeight );
-		}
-
-		if ( heightStyle !== "content" ) {
-			this.element.height( this.element.height() );
 		}
 	},
@@ -2642,5 +2689,5 @@
 			},
 
-			_setOption: function( key, value ) {
+			_setOption: function( key ) {
 				if ( key === "autoHeight" || key === "clearStyle" || key === "fillSpace" ) {
 					this.options.heightStyle = this._mergeHeightStyle();
Index: admin/js/jquery/jquery.autocomplete.js
===================================================================
--- admin/js/jquery/jquery.autocomplete.js	(revision 1035)
+++ admin/js/jquery/jquery.autocomplete.js	(revision 1035)
@@ -0,0 +1,808 @@
+/*
+ * jQuery Autocomplete plugin 1.1
+ *
+ * Copyright (c) 2009 Jörn Zaefferer
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+ *
+ * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $
+ */
+
+;(function($) {
+	
+$.fn.extend({
+	autocomplete: function(urlOrData, options) {
+		var isUrl = typeof urlOrData == "string";
+		options = $.extend({}, $.Autocompleter.defaults, {
+			url: isUrl ? urlOrData : null,
+			data: isUrl ? null : urlOrData,
+			delay: isUrl ? $.Autocompleter.defaults.delay : 10,
+			max: options && !options.scroll ? 10 : 150
+		}, options);
+		
+		// if highlight is set to false, replace it with a do-nothing function
+		options.highlight = options.highlight || function(value) { return value; };
+		
+		// if the formatMatch option is not specified, then use formatItem for backwards compatibility
+		options.formatMatch = options.formatMatch || options.formatItem;
+		
+		return this.each(function() {
+			new $.Autocompleter(this, options);
+		});
+	},
+	result: function(handler) {
+		return this.bind("result", handler);
+	},
+	search: function(handler) {
+		return this.trigger("search", [handler]);
+	},
+	flushCache: function() {
+		return this.trigger("flushCache");
+	},
+	setOptions: function(options){
+		return this.trigger("setOptions", [options]);
+	},
+	unautocomplete: function() {
+		return this.trigger("unautocomplete");
+	}
+});
+
+$.Autocompleter = function(input, options) {
+
+	var KEY = {
+		UP: 38,
+		DOWN: 40,
+		DEL: 46,
+		TAB: 9,
+		RETURN: 13,
+		ESC: 27,
+		COMMA: 188,
+		PAGEUP: 33,
+		PAGEDOWN: 34,
+		BACKSPACE: 8
+	};
+
+	// Create $ object for input element
+	var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
+
+	var timeout;
+	var previousValue = "";
+	var cache = $.Autocompleter.Cache(options);
+	var hasFocus = 0;
+	var lastKeyPressCode;
+	var config = {
+		mouseDownOnSelect: false
+	};
+	var select = $.Autocompleter.Select(options, input, selectCurrent, config);
+	
+	var blockSubmit;
+	
+	// prevent form submit in opera when selecting with return key
+	$.browser.opera && $(input.form).bind("submit.autocomplete", function() {
+		if (blockSubmit) {
+			blockSubmit = false;
+			return false;
+		}
+	});
+	
+	// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
+	$input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
+		// a keypress means the input has focus
+		// avoids issue where input had focus before the autocomplete was applied
+		hasFocus = 1;
+		// track last key pressed
+		lastKeyPressCode = event.keyCode;
+		switch(event.keyCode) {
+		
+			case KEY.UP:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.prev();
+				} else {
+					onChange(0, true);
+				}
+				break;
+				
+			case KEY.DOWN:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.next();
+				} else {
+					onChange(0, true);
+				}
+				break;
+				
+			case KEY.PAGEUP:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.pageUp();
+				} else {
+					onChange(0, true);
+				}
+				break;
+				
+			case KEY.PAGEDOWN:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.pageDown();
+				} else {
+					onChange(0, true);
+				}
+				break;
+			
+			// matches also semicolon
+			case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
+			case KEY.TAB:
+			case KEY.RETURN:
+				if( selectCurrent() ) {
+					// stop default to prevent a form submit, Opera needs special handling
+					event.preventDefault();
+					blockSubmit = true;
+					return false;
+				}
+				break;
+				
+			case KEY.ESC:
+				select.hide();
+				break;
+				
+			default:
+				clearTimeout(timeout);
+				timeout = setTimeout(onChange, options.delay);
+				break;
+		}
+	}).focus(function(){
+		// track whether the field has focus, we shouldn't process any
+		// results if the field no longer has focus
+		hasFocus++;
+	}).blur(function() {
+		hasFocus = 0;
+		if (!config.mouseDownOnSelect) {
+			hideResults();
+		}
+	}).click(function() {
+		// show select when clicking in a focused field
+		if ( hasFocus++ > 1 && !select.visible() ) {
+			onChange(0, true);
+		}
+	}).bind("search", function() {
+		// TODO why not just specifying both arguments?
+		var fn = (arguments.length > 1) ? arguments[1] : null;
+		function findValueCallback(q, data) {
+			var result;
+			if( data && data.length ) {
+				for (var i=0; i < data.length; i++) {
+					if( data[i].result.toLowerCase() == q.toLowerCase() ) {
+						result = data[i];
+						break;
+					}
+				}
+			}
+			if( typeof fn == "function" ) fn(result);
+			else $input.trigger("result", result && [result.data, result.value]);
+		}
+		$.each(trimWords($input.val()), function(i, value) {
+			request(value, findValueCallback, findValueCallback);
+		});
+	}).bind("flushCache", function() {
+		cache.flush();
+	}).bind("setOptions", function() {
+		$.extend(options, arguments[1]);
+		// if we've updated the data, repopulate
+		if ( "data" in arguments[1] )
+			cache.populate();
+	}).bind("unautocomplete", function() {
+		select.unbind();
+		$input.unbind();
+		$(input.form).unbind(".autocomplete");
+	});
+	
+	
+	function selectCurrent() {
+		var selected = select.selected();
+		if( !selected )
+			return false;
+		
+		var v = selected.result;
+		previousValue = v;
+		
+		if ( options.multiple ) {
+			var words = trimWords($input.val());
+			if ( words.length > 1 ) {
+				var seperator = options.multipleSeparator.length;
+				var cursorAt = $(input).selection().start;
+				var wordAt, progress = 0;
+				$.each(words, function(i, word) {
+					progress += word.length;
+					if (cursorAt <= progress) {
+						wordAt = i;
+						return false;
+					}
+					progress += seperator;
+				});
+				words[wordAt] = v;
+				// TODO this should set the cursor to the right position, but it gets overriden somewhere
+				//$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
+				v = words.join( options.multipleSeparator );
+			}
+			v += options.multipleSeparator;
+		}
+		
+		$input.val(v);
+		hideResultsNow();
+		$input.trigger("result", [selected.data, selected.value]);
+		return true;
+	}
+	
+	function onChange(crap, skipPrevCheck) {
+		if( lastKeyPressCode == KEY.DEL ) {
+			select.hide();
+			return;
+		}
+		
+		var currentValue = $input.val();
+		
+		if ( !skipPrevCheck && currentValue == previousValue )
+			return;
+		
+		previousValue = currentValue;
+		
+		currentValue = lastWord(currentValue);
+		if ( currentValue.length >= options.minChars) {
+			$input.addClass(options.loadingClass);
+			if (!options.matchCase)
+				currentValue = currentValue.toLowerCase();
+			request(currentValue, receiveData, hideResultsNow);
+		} else {
+			stopLoading();
+			select.hide();
+		}
+	};
+	
+	function trimWords(value) {
+		if (!value)
+			return [""];
+		if (!options.multiple)
+			return [$.trim(value)];
+		return $.map(value.split(options.multipleSeparator), function(word) {
+			return $.trim(value).length ? $.trim(word) : null;
+		});
+	}
+	
+	function lastWord(value) {
+		if ( !options.multiple )
+			return value;
+		var words = trimWords(value);
+		if (words.length == 1) 
+			return words[0];
+		var cursorAt = $(input).selection().start;
+		if (cursorAt == value.length) {
+			words = trimWords(value)
+		} else {
+			words = trimWords(value.replace(value.substring(cursorAt), ""));
+		}
+		return words[words.length - 1];
+	}
+	
+	// fills in the input box w/the first match (assumed to be the best match)
+	// q: the term entered
+	// sValue: the first matching result
+	function autoFill(q, sValue){
+		// autofill in the complete box w/the first match as long as the user hasn't entered in more data
+		// if the last user key pressed was backspace, don't autofill
+		if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
+			// fill in the value (keep the case the user has typed)
+			$input.val($input.val() + sValue.substring(lastWord(previousValue).length));
+			// select the portion of the value not typed by the user (so the next character will erase)
+			$(input).selection(previousValue.length, previousValue.length + sValue.length);
+		}
+	};
+
+	function hideResults() {
+		clearTimeout(timeout);
+		timeout = setTimeout(hideResultsNow, 200);
+	};
+
+	function hideResultsNow() {
+		var wasVisible = select.visible();
+		select.hide();
+		clearTimeout(timeout);
+		stopLoading();
+		if (options.mustMatch) {
+			// call search and run callback
+			$input.search(
+				function (result){
+					// if no value found, clear the input box
+					if( !result ) {
+						if (options.multiple) {
+							var words = trimWords($input.val()).slice(0, -1);
+							$input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
+						}
+						else {
+							$input.val( "" );
+							$input.trigger("result", null);
+						}
+					}
+				}
+			);
+		}
+	};
+
+	function receiveData(q, data) {
+		if ( data && data.length && hasFocus ) {
+			stopLoading();
+			select.display(data, q);
+			autoFill(q, data[0].value);
+			select.show();
+		} else {
+			hideResultsNow();
+		}
+	};
+
+	function request(term, success, failure) {
+		if (!options.matchCase)
+			term = term.toLowerCase();
+		var data = cache.load(term);
+		// recieve the cached data
+		if (data && data.length) {
+			success(term, data);
+		// if an AJAX url has been supplied, try loading the data now
+		} else if( (typeof options.url == "string") && (options.url.length > 0) ){
+			
+			var extraParams = {
+				timestamp: +new Date()
+			};
+			$.each(options.extraParams, function(key, param) {
+				extraParams[key] = typeof param == "function" ? param() : param;
+			});
+			
+			$.ajax({
+				// try to leverage ajaxQueue plugin to abort previous requests
+				mode: "abort",
+				// limit abortion to this input
+				port: "autocomplete" + input.name,
+				dataType: options.dataType,
+				url: options.url,
+				data: $.extend({
+					q: lastWord(term),
+					limit: options.max
+				}, extraParams),
+				success: function(data) {
+					var parsed = options.parse && options.parse(data) || parse(data);
+					cache.add(term, parsed);
+					success(term, parsed);
+				}
+			});
+		} else {
+			// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
+			select.emptyList();
+			failure(term);
+		}
+	};
+	
+	function parse(data) {
+		var parsed = [];
+		var rows = data.split("\n");
+		for (var i=0; i < rows.length; i++) {
+			var row = $.trim(rows[i]);
+			if (row) {
+				row = row.split("|");
+				parsed[parsed.length] = {
+					data: row,
+					value: row[0],
+					result: options.formatResult && options.formatResult(row, row[0]) || row[0]
+				};
+			}
+		}
+		return parsed;
+	};
+
+	function stopLoading() {
+		$input.removeClass(options.loadingClass);
+	};
+
+};
+
+$.Autocompleter.defaults = {
+	inputClass: "ac_input",
+	resultsClass: "ac_results",
+	loadingClass: "ac_loading",
+	minChars: 1,
+	delay: 400,
+	matchCase: false,
+	matchSubset: true,
+	matchContains: false,
+	cacheLength: 10,
+	max: 100,
+	mustMatch: false,
+	extraParams: {},
+	selectFirst: true,
+	formatItem: function(row) { return row[0]; },
+	formatMatch: null,
+	autoFill: false,
+	width: 0,
+	multiple: false,
+	multipleSeparator: ", ",
+	highlight: function(value, term) {
+		return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
+	},
+    scroll: true,
+    scrollHeight: 180
+};
+
+$.Autocompleter.Cache = function(options) {
+
+	var data = {};
+	var length = 0;
+	
+	function matchSubset(s, sub) {
+		if (!options.matchCase) 
+			s = s.toLowerCase();
+		var i = s.indexOf(sub);
+		if (options.matchContains == "word"){
+			i = s.toLowerCase().search("\\b" + sub.toLowerCase());
+		}
+		if (i == -1) return false;
+		return i == 0 || options.matchContains;
+	};
+	
+	function add(q, value) {
+		if (length > options.cacheLength){
+			flush();
+		}
+		if (!data[q]){ 
+			length++;
+		}
+		data[q] = value;
+	}
+	
+	function populate(){
+		if( !options.data ) return false;
+		// track the matches
+		var stMatchSets = {},
+			nullData = 0;
+
+		// no url was specified, we need to adjust the cache length to make sure it fits the local data store
+		if( !options.url ) options.cacheLength = 1;
+		
+		// track all options for minChars = 0
+		stMatchSets[""] = [];
+		
+		// loop through the array and create a lookup structure
+		for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
+			var rawValue = options.data[i];
+			// if rawValue is a string, make an array otherwise just reference the array
+			rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
+			
+			var value = options.formatMatch(rawValue, i+1, options.data.length);
+			if ( value === false )
+				continue;
+				
+			var firstChar = value.charAt(0).toLowerCase();
+			// if no lookup array for this character exists, look it up now
+			if( !stMatchSets[firstChar] ) 
+				stMatchSets[firstChar] = [];
+
+			// if the match is a string
+			var row = {
+				value: value,
+				data: rawValue,
+				result: options.formatResult && options.formatResult(rawValue) || value
+			};
+			
+			// push the current match into the set list
+			stMatchSets[firstChar].push(row);
+
+			// keep track of minChars zero items
+			if ( nullData++ < options.max ) {
+				stMatchSets[""].push(row);
+			}
+		};
+
+		// add the data items to the cache
+		$.each(stMatchSets, function(i, value) {
+			// increase the cache size
+			options.cacheLength++;
+			// add to the cache
+			add(i, value);
+		});
+	}
+	
+	// populate any existing data
+	setTimeout(populate, 25);
+	
+	function flush(){
+		data = {};
+		length = 0;
+	}
+	
+	return {
+		flush: flush,
+		add: add,
+		populate: populate,
+		load: function(q) {
+			if (!options.cacheLength || !length)
+				return null;
+			/* 
+			 * if dealing w/local data and matchContains than we must make sure
+			 * to loop through all the data collections looking for matches
+			 */
+			if( !options.url && options.matchContains ){
+				// track all matches
+				var csub = [];
+				// loop through all the data grids for matches
+				for( var k in data ){
+					// don't search through the stMatchSets[""] (minChars: 0) cache
+					// this prevents duplicates
+					if( k.length > 0 ){
+						var c = data[k];
+						$.each(c, function(i, x) {
+							// if we've got a match, add it to the array
+							if (matchSubset(x.value, q)) {
+								csub.push(x);
+							}
+						});
+					}
+				}				
+				return csub;
+			} else 
+			// if the exact item exists, use it
+			if (data[q]){
+				return data[q];
+			} else
+			if (options.matchSubset) {
+				for (var i = q.length - 1; i >= options.minChars; i--) {
+					var c = data[q.substr(0, i)];
+					if (c) {
+						var csub = [];
+						$.each(c, function(i, x) {
+							if (matchSubset(x.value, q)) {
+								csub[csub.length] = x;
+							}
+						});
+						return csub;
+					}
+				}
+			}
+			return null;
+		}
+	};
+};
+
+$.Autocompleter.Select = function (options, input, select, config) {
+	var CLASSES = {
+		ACTIVE: "ac_over"
+	};
+	
+	var listItems,
+		active = -1,
+		data,
+		term = "",
+		needsInit = true,
+		element,
+		list;
+	
+	// Create results
+	function init() {
+		if (!needsInit)
+			return;
+		element = $("<div/>")
+		.hide()
+		.addClass(options.resultsClass)
+		.css("position", "absolute")
+		.appendTo(document.body);
+	
+		list = $("<ul/>").appendTo(element).mouseover( function(event) {
+			if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
+	            active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
+			    $(target(event)).addClass(CLASSES.ACTIVE);            
+	        }
+		}).click(function(event) {
+			$(target(event)).addClass(CLASSES.ACTIVE);
+			select();
+			// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
+			input.focus();
+			return false;
+		}).mousedown(function() {
+			config.mouseDownOnSelect = true;
+		}).mouseup(function() {
+			config.mouseDownOnSelect = false;
+		});
+		
+		if( options.width > 0 )
+			element.css("width", options.width);
+			
+		needsInit = false;
+	} 
+	
+	function target(event) {
+		var element = event.target;
+		while(element && element.tagName != "LI")
+			element = element.parentNode;
+		// more fun with IE, sometimes event.target is empty, just ignore it then
+		if(!element)
+			return [];
+		return element;
+	}
+
+	function moveSelect(step) {
+		listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
+		movePosition(step);
+        var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
+        if(options.scroll) {
+            var offset = 0;
+            listItems.slice(0, active).each(function() {
+				offset += this.offsetHeight;
+			});
+            if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
+                list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
+            } else if(offset < list.scrollTop()) {
+                list.scrollTop(offset);
+            }
+        }
+	};
+	
+	function movePosition(step) {
+		active += step;
+		if (active < 0) {
+			active = listItems.size() - 1;
+		} else if (active >= listItems.size()) {
+			active = 0;
+		}
+	}
+	
+	function limitNumberOfItems(available) {
+		return options.max && options.max < available
+			? options.max
+			: available;
+	}
+	
+	function fillList() {
+		list.empty();
+		var max = limitNumberOfItems(data.length);
+		for (var i=0; i < max; i++) {
+			if (!data[i])
+				continue;
+			var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
+			if ( formatted === false )
+				continue;
+			var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
+			$.data(li, "ac_data", data[i]);
+		}
+		listItems = list.find("li");
+		if ( options.selectFirst ) {
+			listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
+			active = 0;
+		}
+		// apply bgiframe if available
+		if ( $.fn.bgiframe )
+			list.bgiframe();
+	}
+	
+	return {
+		display: function(d, q) {
+			init();
+			data = d;
+			term = q;
+			fillList();
+		},
+		next: function() {
+			moveSelect(1);
+		},
+		prev: function() {
+			moveSelect(-1);
+		},
+		pageUp: function() {
+			if (active != 0 && active - 8 < 0) {
+				moveSelect( -active );
+			} else {
+				moveSelect(-8);
+			}
+		},
+		pageDown: function() {
+			if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
+				moveSelect( listItems.size() - 1 - active );
+			} else {
+				moveSelect(8);
+			}
+		},
+		hide: function() {
+			element && element.hide();
+			listItems && listItems.removeClass(CLASSES.ACTIVE);
+			active = -1;
+		},
+		visible : function() {
+			return element && element.is(":visible");
+		},
+		current: function() {
+			return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
+		},
+		show: function() {
+			var offset = $(input).offset();
+			element.css({
+				width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
+				top: offset.top + input.offsetHeight,
+				left: offset.left
+			}).show();
+            if(options.scroll) {
+                list.scrollTop(0);
+                list.css({
+					maxHeight: options.scrollHeight,
+					overflow: 'auto'
+				});
+				
+                if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
+					var listHeight = 0;
+					listItems.each(function() {
+						listHeight += this.offsetHeight;
+					});
+					var scrollbarsVisible = listHeight > options.scrollHeight;
+                    list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
+					if (!scrollbarsVisible) {
+						// IE doesn't recalculate width when scrollbar disappears
+						listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
+					}
+                }
+                
+            }
+		},
+		selected: function() {
+			var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
+			return selected && selected.length && $.data(selected[0], "ac_data");
+		},
+		emptyList: function (){
+			list && list.empty();
+		},
+		unbind: function() {
+			element && element.remove();
+		}
+	};
+};
+
+$.fn.selection = function(start, end) {
+	if (start !== undefined) {
+		return this.each(function() {
+			if( this.createTextRange ){
+				var selRange = this.createTextRange();
+				if (end === undefined || start == end) {
+					selRange.move("character", start);
+					selRange.select();
+				} else {
+					selRange.collapse(true);
+					selRange.moveStart("character", start);
+					selRange.moveEnd("character", end);
+					selRange.select();
+				}
+			} else if( this.setSelectionRange ){
+				this.setSelectionRange(start, end);
+			} else if( this.selectionStart ){
+				this.selectionStart = start;
+				this.selectionEnd = end;
+			}
+		});
+	}
+	var field = this[0];
+	if ( field.createTextRange ) {
+		var range = document.selection.createRange(),
+			orig = field.value,
+			teststring = "<->",
+			textLength = range.text.length;
+		range.text = teststring;
+		var caretAt = field.value.indexOf(teststring);
+		field.value = orig;
+		this.selection(caretAt, caretAt + textLength);
+		return {
+			start: caretAt,
+			end: caretAt + textLength
+		}
+	} else if( field.selectionStart !== undefined ){
+		return {
+			start: field.selectionStart,
+			end: field.selectionEnd
+		}
+	}
+};
+
+})(jQuery);
Index: admin/js/jquery/jquery.js
===================================================================
--- admin/js/jquery/jquery.js	(revision 906)
+++ admin/js/jquery/jquery.js	(revision 1048)
@@ -1,4 +1,4 @@
 /*!
- * jQuery JavaScript Library v1.8.2
+ * jQuery JavaScript Library v1.8.3
  * http://jquery.com/
  *
@@ -10,5 +10,5 @@
  * http://jquery.org/license
  *
- * Date: Thu Sep 20 2012 21:13:05 GMT-0400 (Eastern Daylight Time)
+ * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time)
  */
 (function( window, undefined ) {
@@ -187,5 +187,5 @@
 
 	// The current version of jQuery being used
-	jquery: "1.8.2",
+	jquery: "1.8.3",
 
 	// The default length of a jQuery object is 0
@@ -1000,6 +1000,8 @@
 						jQuery.each( args, function( _, arg ) {
 							var type = jQuery.type( arg );
-							if ( type === "function" && ( !options.unique || !self.has( arg ) ) ) {
-								list.push( arg );
+							if ( type === "function" ) {
+								if ( !options.unique || !self.has( arg ) ) {
+									list.push( arg );
+								}
 							} else if ( arg && arg.length && type !== "string" ) {
 								// Inspect recursively
@@ -1254,22 +1256,21 @@
 		div = document.createElement("div");
 
-	// Preliminary tests
+	// Setup
 	div.setAttribute( "className", "t" );
 	div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
 
+	// Support tests won't run in some limited or non-browser environments
 	all = div.getElementsByTagName("*");
 	a = div.getElementsByTagName("a")[ 0 ];
-	a.style.cssText = "top:1px;float:left;opacity:.5";
-
-	// Can't get basic test support
-	if ( !all || !all.length ) {
+	if ( !all || !a || !all.length ) {
 		return {};
 	}
 
-	// First batch of supports tests
+	// First batch of tests
 	select = document.createElement("select");
 	opt = select.appendChild( document.createElement("option") );
 	input = div.getElementsByTagName("input")[ 0 ];
 
+	a.style.cssText = "top:1px;float:left;opacity:.5";
 	support = {
 		// IE strips leading whitespace when .innerHTML is used
@@ -1313,5 +1314,5 @@
 		getSetAttribute: div.className !== "t",
 
-		// Tests for enctype support on a form(#6743)
+		// Tests for enctype support on a form (#6743)
 		enctype: !!document.createElement("form").enctype,
 
@@ -2218,24 +2219,23 @@
 		select: {
 			get: function( elem ) {
-				var value, i, max, option,
+				var value, option,
+					options = elem.options,
 					index = elem.selectedIndex,
-					values = [],
-					options = elem.options,
-					one = elem.type === "select-one";
-
-				// Nothing was selected
-				if ( index < 0 ) {
-					return null;
-				}
+					one = elem.type === "select-one" || index < 0,
+					values = one ? null : [],
+					max = one ? index + 1 : options.length,
+					i = index < 0 ?
+						max :
+						one ? index : 0;
 
 				// Loop through all the selected options
-				i = one ? index : 0;
-				max = one ? index + 1 : options.length;
 				for ( ; i < max; i++ ) {
 					option = options[ i ];
 
-					// Don't return options that are disabled or in a disabled optgroup
-					if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
-							(!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
+					// oldIE doesn't update selected after form reset (#2551)
+					if ( ( option.selected || i === index ) &&
+							// Don't return options that are disabled or in a disabled optgroup
+							( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
+							( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
 
 						// Get the specific value for the option
@@ -2250,9 +2250,4 @@
 						values.push( value );
 					}
-				}
-
-				// Fixes Bug #2551 -- select.val() broken in IE after form.reset()
-				if ( one && !values.length && options.length ) {
-					return jQuery( options[ index ] ).val();
 				}
 
@@ -3234,5 +3229,5 @@
 		if ( elem.detachEvent ) {
 
-			// #8545, #7054, preventing memory leaks for custom events in IE6-8 –
+			// #8545, #7054, preventing memory leaks for custom events in IE6-8
 			// detachEvent needed property on element, by name of that event, to properly expose it to GC
 			if ( typeof elem[ name ] === "undefined" ) {
@@ -3726,5 +3721,6 @@
 			}
 
-			return (cache[ key ] = value);
+			// Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157)
+			return (cache[ key + " " ] = value);
 		}, cache );
 	},
@@ -4260,11 +4256,11 @@
 
 		"CLASS": function( className ) {
-			var pattern = classCache[ expando ][ className ];
-			if ( !pattern ) {
-				pattern = classCache( className, new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)") );
-			}
-			return function( elem ) {
-				return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
-			};
+			var pattern = classCache[ expando ][ className + " " ];
+
+			return pattern ||
+				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+				classCache( className, function( elem ) {
+					return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
+				});
 		},
 
@@ -4512,5 +4508,5 @@
 		"focus": function( elem ) {
 			var doc = elem.ownerDocument;
-			return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href);
+			return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
 		},
 
@@ -4520,9 +4516,9 @@
 
 		// Positional types
-		"first": createPositionalPseudo(function( matchIndexes, length, argument ) {
+		"first": createPositionalPseudo(function() {
 			return [ 0 ];
 		}),
 
-		"last": createPositionalPseudo(function( matchIndexes, length, argument ) {
+		"last": createPositionalPseudo(function( matchIndexes, length ) {
 			return [ length - 1 ];
 		}),
@@ -4532,5 +4528,5 @@
 		}),
 
-		"even": createPositionalPseudo(function( matchIndexes, length, argument ) {
+		"even": createPositionalPseudo(function( matchIndexes, length ) {
 			for ( var i = 0; i < length; i += 2 ) {
 				matchIndexes.push( i );
@@ -4539,5 +4535,5 @@
 		}),
 
-		"odd": createPositionalPseudo(function( matchIndexes, length, argument ) {
+		"odd": createPositionalPseudo(function( matchIndexes, length ) {
 			for ( var i = 1; i < length; i += 2 ) {
 				matchIndexes.push( i );
@@ -4660,5 +4656,7 @@
 Sizzle.uniqueSort = function( results ) {
 	var elem,
-		i = 1;
+		duplicates = [],
+		i = 1,
+		j = 0;
 
 	hasDuplicate = baseHasDuplicate;
@@ -4668,6 +4666,9 @@
 		for ( ; (elem = results[i]); i++ ) {
 			if ( elem === results[ i - 1 ] ) {
-				results.splice( i--, 1 );
-			}
+				j = duplicates.push( i );
+			}
+		}
+		while ( j-- ) {
+			results.splice( duplicates[ j ], 1 );
 		}
 	}
@@ -4681,6 +4682,7 @@
 
 function tokenize( selector, parseOnly ) {
-	var matched, match, tokens, type, soFar, groups, preFilters,
-		cached = tokenCache[ expando ][ selector ];
+	var matched, match, tokens, type,
+		soFar, groups, preFilters,
+		cached = tokenCache[ expando ][ selector + " " ];
 
 	if ( cached ) {
@@ -4697,5 +4699,6 @@
 		if ( !matched || (match = rcomma.exec( soFar )) ) {
 			if ( match ) {
-				soFar = soFar.slice( match[0].length );
+				// Don't consume trailing commas as valid
+				soFar = soFar.slice( match[0].length ) || soFar;
 			}
 			groups.push( tokens = [] );
@@ -4716,6 +4719,5 @@
 		for ( type in Expr.filter ) {
 			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
-				// The last two arguments here are (context, xml) for backCompat
-				(match = preFilters[ type ]( match, document, true ))) ) {
+				(match = preFilters[ type ]( match ))) ) {
 
 				tokens.push( matched = new Token( match.shift() ) );
@@ -4837,10 +4839,5 @@
 	}
 	return markFunction(function( seed, results, context, xml ) {
-		// Positional selectors apply to seed elements, so it is invalid to follow them with relative ones
-		if ( seed && postFinder ) {
-			return;
-		}
-
-		var i, elem, postFilterIn,
+		var temp, i, elem,
 			preMap = [],
 			postMap = [],
@@ -4848,5 +4845,5 @@
 
 			// Get initial elements from seed or context
-			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [], seed ),
+			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
 
 			// Prefilter to get matcher input, preserving a map for seed-results synchronization
@@ -4873,11 +4870,11 @@
 		// Apply postFilter
 		if ( postFilter ) {
-			postFilterIn = condense( matcherOut, postMap );
-			postFilter( postFilterIn, [], context, xml );
+			temp = condense( matcherOut, postMap );
+			postFilter( temp, [], context, xml );
 
 			// Un-match failing elements by moving them back to matcherIn
-			i = postFilterIn.length;
+			i = temp.length;
 			while ( i-- ) {
-				if ( (elem = postFilterIn[i]) ) {
+				if ( (elem = temp[i]) ) {
 					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
 				}
@@ -4885,13 +4882,31 @@
 		}
 
-		// Keep seed and results synchronized
 		if ( seed ) {
-			// Ignore postFinder because it can't coexist with seed
-			i = preFilter && matcherOut.length;
-			while ( i-- ) {
-				if ( (elem = matcherOut[i]) ) {
-					seed[ preMap[i] ] = !(results[ preMap[i] ] = elem);
-				}
-			}
+			if ( postFinder || preFilter ) {
+				if ( postFinder ) {
+					// Get the final matcherOut by condensing this intermediate into postFinder contexts
+					temp = [];
+					i = matcherOut.length;
+					while ( i-- ) {
+						if ( (elem = matcherOut[i]) ) {
+							// Restore matcherIn since elem is not yet a final match
+							temp.push( (matcherIn[i] = elem) );
+						}
+					}
+					postFinder( null, (matcherOut = []), temp, xml );
+				}
+
+				// Move matched elements from seed to results to keep them synchronized
+				i = matcherOut.length;
+				while ( i-- ) {
+					if ( (elem = matcherOut[i]) &&
+						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+
+						seed[temp] = !(results[temp] = elem);
+					}
+				}
+			}
+
+		// Add elements to results, through postFinder if defined
 		} else {
 			matcherOut = condense(
@@ -4934,5 +4949,4 @@
 			matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
 		} else {
-			// The concatenated values are (context, xml) for backCompat
 			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
 
@@ -5063,5 +5077,5 @@
 		setMatchers = [],
 		elementMatchers = [],
-		cached = compilerCache[ expando ][ selector ];
+		cached = compilerCache[ expando ][ selector + " " ];
 
 	if ( !cached ) {
@@ -5086,9 +5100,9 @@
 };
 
-function multipleContexts( selector, contexts, results, seed ) {
+function multipleContexts( selector, contexts, results ) {
 	var i = 0,
 		len = contexts.length;
 	for ( ; i < len; i++ ) {
-		Sizzle( selector, contexts[i], results, seed );
+		Sizzle( selector, contexts[i], results );
 	}
 	return results;
@@ -5168,13 +5182,12 @@
 			rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
 
-			// qSa(:focus) reports false when true (Chrome 21),
+			// qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA
 			// A support test would require too much code (would include document ready)
-			rbuggyQSA = [":focus"],
-
-			// matchesSelector(:focus) reports false when true (Chrome 21),
+			rbuggyQSA = [ ":focus" ],
+
 			// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
 			// A support test would require too much code (would include document ready)
 			// just skip matchesSelector for :active
-			rbuggyMatches = [ ":active", ":focus" ],
+			rbuggyMatches = [ ":active" ],
 			matches = docElem.matchesSelector ||
 				docElem.mozMatchesSelector ||
@@ -5230,5 +5243,5 @@
 			// when this is not xml,
 			// and when no QSA bugs apply
-			if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+			if ( !seed && !xml && !rbuggyQSA.test( selector ) ) {
 				var groups, i,
 					old = true,
@@ -5299,5 +5312,5 @@
 
 				// rbuggyMatches always contains :active, so no need for an existence check
-				if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) {
+				if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) {
 					try {
 						var ret = matches.call( elem, expr );
@@ -6534,5 +6547,5 @@
 	rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
 	rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ),
-	elemdisplay = {},
+	elemdisplay = { BODY: "block" },
 
 	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
@@ -6815,5 +6828,7 @@
 		if ( computed ) {
 
-			ret = computed[ name ];
+			// getPropertyValue is only needed for .css('filter') in IE9, see #12537
+			ret = computed.getPropertyValue( name ) || computed[ name ];
+
 			if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
 				ret = jQuery.style( elem, name );
@@ -7844,7 +7859,10 @@
 		// A cross-domain request is in order when we have a protocol:host:port mismatch
 		if ( s.crossDomain == null ) {
-			parts = rurl.exec( s.url.toLowerCase() ) || false;
-			s.crossDomain = parts && ( parts.join(":") + ( parts[ 3 ] ? "" : parts[ 1 ] === "http:" ? 80 : 443 ) ) !==
-				( ajaxLocParts.join(":") + ( ajaxLocParts[ 3 ] ? "" : ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) );
+			parts = rurl.exec( s.url.toLowerCase() );
+			s.crossDomain = !!( parts &&
+				( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
+					( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+						( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
+			);
 		}
 
@@ -8465,5 +8483,5 @@
 									try {
 										responses.text = xhr.responseText;
-									} catch( _ ) {
+									} catch( e ) {
 									}
 
@@ -8618,5 +8636,7 @@
 			var currentTime = fxNow || createFxNow(),
 				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
-				percent = 1 - ( remaining / animation.duration || 0 ),
+				// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
+				temp = remaining / animation.duration || 0,
+				percent = 1 - temp,
 				index = 0,
 				length = animation.tweens.length;
@@ -8770,5 +8790,5 @@
 
 function defaultPrefilter( elem, props, opts ) {
-	var index, prop, value, length, dataShow, tween, hooks, oldfire,
+	var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire,
 		anim = this,
 		style = elem.style,
@@ -8844,4 +8864,5 @@
 		if ( rfxtypes.exec( value ) ) {
 			delete props[ index ];
+			toggle = toggle || value === "toggle";
 			if ( value === ( hidden ? "hide" : "show" ) ) {
 				continue;
@@ -8854,4 +8875,12 @@
 	if ( length ) {
 		dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
+		if ( "hidden" in dataShow ) {
+			hidden = dataShow.hidden;
+		}
+
+		// store state if its toggle - enables .stop().toggle() to "reverse"
+		if ( toggle ) {
+			dataShow.hidden = !hidden;
+		}
 		if ( hidden ) {
 			jQuery( elem ).show();
@@ -9150,4 +9179,6 @@
 		i = 0;
 
+	fxNow = jQuery.now();
+
 	for ( ; i < timers.length; i++ ) {
 		timer = timers[ i ];
@@ -9161,4 +9192,5 @@
 		jQuery.fx.stop();
 	}
+	fxNow = undefined;
 };
 
Index: admin/js/jsToolBar/jsToolBar.js
===================================================================
--- admin/js/jsToolBar/jsToolBar.js	(revision 530)
+++ admin/js/jsToolBar/jsToolBar.js	(revision 1047)
@@ -333,9 +333,8 @@
 	},
 	wiki: {
-		list: ['nonebis','h2','h3','h4','h5'],
+		list: ['nonebis','h3','h4','h5'],
 		fn: function(opt) {
 			switch (opt) {
 				case 'nonebis': this.textarea.focus(); break;
-				case 'h2': this.encloseSelection('!!!!'); break;
 				case 'h3': this.encloseSelection('!!!'); break;
 				case 'h4': this.encloseSelection('!!'); break;
Index: admin/media.php
===================================================================
--- admin/media.php	(revision 977)
+++ admin/media.php	(revision 1039)
@@ -251,5 +251,5 @@
 	"</script>".
 	dcPage::jsLoad('js/_media.js').
-	(($user_ui_enhanceduploader && $core_media_writable) ? dcPage::jsCandyUpload(array('d='.$d)) : '')
+	($core_media_writable ? dcPage::jsCandyUpload(array('d='.$d)) : '')
 	);
 
@@ -356,7 +356,11 @@
 	'<p><label for="upfiletitle">'.__('Title:').form::field(array('upfiletitle','upfiletitle'),35,255).'</label></p>'.
 	'<p><label for="upfilepriv" class="classic">'.form::checkbox(array('upfilepriv','upfilepriv'),1).' '.
-	__('Private').'</label></p>'.
-	'<p class="form-help info">'.__('To send several files at the same time, you can activate the enhanced uploader in').
-	' <a href="preferences.php?tab=user-options">'.__('My preferences').'</a></p>'.
+	__('Private').'</label></p>';
+	if (!$user_ui_enhanceduploader) {
+		echo
+		'<p class="form-help info">'.__('To send several files at the same time, you can activate the enhanced uploader in').
+		' <a href="preferences.php?tab=user-options">'.__('My preferences').'</a></p>';
+	}
+	echo
 	'<p><input type="submit" value="'.__('Send').'" />'.
 	form::hidden(array('d'),$d).'</p>'.
Index: admin/media_item.php
===================================================================
--- admin/media_item.php	(revision 950)
+++ admin/media_item.php	(revision 1038)
@@ -293,7 +293,7 @@
 		echo
 		'<h3>'.__('Video size').'</h3>'.
-		'<p><label for="video_w" class="classic">'.__('Width:').' '.
+		'<p><label for="video_w" class="classic">'.__('Width:').'</label> '.
 		form::field('video_w',3,4,400).'  '.
-		'<label for="video_h" class="classic">'.__('Height:').' '.
+		'<label for="video_h" class="classic">'.__('Height:').'</label> '.
 		form::field('video_h',3,4,300).
 		'</p>';
@@ -339,5 +339,5 @@
 echo
 '<div class="multi-part" title="'.__('Media details').'" id="media-details-tab">'.
-'<p id="media-icon"><img src="'.$file->media_icon.'" alt="" /></p>';
+'<p id="media-icon"><img src="'.$file->media_icon.'?'.time()*rand().'" alt="" /></p>';
 
 echo
@@ -363,10 +363,10 @@
 	
 	if (isset($file->media_thumb[$thumb_size])) {
-		echo '<p><img src="'.$file->media_thumb[$thumb_size].'" alt="" /></p>';
+		echo '<p><img src="'.$file->media_thumb[$thumb_size].'?'.time()*rand().'" alt="" /></p>';
 	} elseif ($thumb_size == 'o') {
 		$S = getimagesize($file->file);
 		$class = ($S[1] > 500) ? ' class="overheight"' : '';
 		unset($S);
-		echo '<p id="media-original-image"'.$class.'><img src="'.$file->file_url.'" alt="" /></p>';
+		echo '<p id="media-original-image"'.$class.'><img src="'.$file->file_url.'?'.time()*rand().'" alt="" /></p>';
 	}
 }
Index: admin/plugins.php
===================================================================
--- admin/plugins.php	(revision 961)
+++ admin/plugins.php	(revision 1055)
@@ -253,6 +253,6 @@
 	'</tr>';
 	
-	$distrib_plugins = array('aboutConfig','akismet','antispam','attachments','blogroll','blowupConfig','fairTrackbacks',
-		'importExport','maintenance','pages','pings','simpleMenu','tags','themeEditor','userPref','widgets');
+	$distrib_plugins = array('aboutConfig','akismet','antispam','attachments','blogroll','blowupConfig','daInstaller',
+		'fairTrackbacks','importExport','maintenance','pages','pings','simpleMenu','tags','themeEditor','userPref','widgets');
 	$distrib_img = '<img src="images/dotclear_pw.png"'.
 		' alt="'.__('Plugin from official distribution').'" title="'.__('Plugin from official distribution').'" />';
Index: admin/popup_posts.php
===================================================================
--- admin/popup_posts.php	(revision 557)
+++ admin/popup_posts.php	(revision 996)
@@ -20,4 +20,17 @@
 $nb_per_page =  10;
 
+$post_types = $core->getPostTypes();
+foreach ($post_types as $k => $v) {
+ 	$type_combo[__($k)] = (string) $k;
+}
+$type = !empty($_POST['type']) ? $_POST['type'] : null;
+if (!$type && $q) {
+	// Cope with search form
+	$type = !empty($_GET['type']) ? $_GET['type'] : null;
+}
+if (!in_array($type, $type_combo)) {
+	$type = null;
+}
+
 $params = array();
 $params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page);
@@ -29,4 +42,8 @@
 }
 
+if ($type) {
+	$params['post_type'] = $type;
+}
+
 dcPage::openPopup(__('Add a link to an entry'),
 	dcPage::jsLoad('js/_posts_list.js').
@@ -35,8 +52,15 @@
 echo '<h2 class="page-title">'.__('Add a link to an entry').'</h2>';
 
+echo '<form action="popup_posts.php" method="post">'.
+	'<p><label for"type" class="classic">'.__('Entry type:').' '.form::combo('type',$type_combo,$type).'</label></p>'.
+	$core->formNonce().
+	'<noscript><div><input type="submit" value="'.__('Ok').'" /></div></noscript>'.
+	'</form>';
+
 echo '<form action="popup_posts.php" method="get">'.
-'<p><label for="q" class="classic">'.__('Search entry:').' '.form::field('q',30,255,html::escapeHTML($q)).'</label> '.
-' <input type="submit" value="'.__('Search').'" /></p>'.
-'</form>';
+	'<p><label for="q" class="classic">'.__('Search entry:').' '.form::field('q',30,255,html::escapeHTML($q)).'</label> '.
+	' <input type="submit" value="'.__('Search').'" /></p>'.
+	form::hidden('type',html::escapeHTML($type)).
+	'</form>';
 
 try {
Index: admin/post.php
===================================================================
--- admin/post.php	(revision 1056)
+++ admin/post.php	(revision 957)
@@ -15,13 +15,21 @@
 dcPage::check('usage,contentadmin');
 
-function savePost($form) {
-	global $_ctx;
-	$_ctx->setMessage('save');
-
-}
-
-function deletePost($form) {
-	print_r($form); exit;
-}
+$post_id = '';
+$cat_id = '';
+$post_dt = '';
+$post_format = $core->auth->getOption('post_format');
+$post_password = '';
+$post_url = '';
+$post_lang = $core->auth->getInfo('user_lang');
+$post_title = '';
+$post_excerpt = '';
+$post_excerpt_xhtml = '';
+$post_content = '';
+$post_content_xhtml = '';
+$post_notes = '';
+$post_status = $core->auth->getInfo('user_post_status');
+$post_selected = false;
+$post_open_comment = $core->blog->settings->system->allow_comments;
+$post_open_tb = $core->blog->settings->system->allow_trackbacks;
 
 $page_title = __('New entry');
@@ -39,5 +47,5 @@
 # If user can't publish
 if (!$can_publish) {
-	$form->post_status = -2;
+	$post_status = -2;
 }
 
@@ -47,8 +55,8 @@
 	$categories = $core->blog->getCategories(array('post_type'=>'post'));
 	while ($categories->fetch()) {
-		$categories_combo[$categories->cat_id] = 
-			str_repeat('&nbsp;&nbsp;',$categories->level-1).
-			($categories->level-1 == 0 ? '' : '&bull; ').
-			html::escapeHTML($categories->cat_title);
+		$categories_combo[] = new formSelectOption(
+			str_repeat('&nbsp;&nbsp;',$categories->level-1).($categories->level-1 == 0 ? '' : '&bull; ').html::escapeHTML($categories->cat_title),
+			$categories->cat_id
+		);
 	}
 } catch (Exception $e) { }
@@ -56,6 +64,7 @@
 # Status combo
 foreach ($core->blog->getAllPostStatus() as $k => $v) {
-	$status_combo[$k] = $v;
-}
+	$status_combo[$v] = (string) $k;
+}
+$img_status_pattern = '<img class="img_select_option" alt="%1$s" title="%1$s" src="images/%2$s" />';
 
 # Formaters combo
@@ -79,57 +88,7 @@
 unset($rs);
 
-$form = new dcForm($core,'post','post.php');
-$form
-	->addField(
-		new dcFieldText('post_title','', array(
-			'size'		=> 20,
-			'required'	=> true,
-			'label'		=> __('Title'))))
-	->addField(
-		new dcFieldTextArea('post_excerpt','', array(
-			'cols'		=> 50,
-			'rows'		=> 5,
-			'label'		=> __("Excerpt:"))))
-	->addField(
-		new dcFieldTextArea('post_content','', array(
-			'required'	=> true,
-			'label'		=> __("Content:"))))
-	->addField(
-		new dcFieldTextArea('post_notes','', array(
-			'label'		=> __("Notes"))))
-	->addField(
-		new dcFieldSubmit('save',__('Save'),array(
-			'action' => 'savePost')))
-	->addField(
-		new dcFieldSubmit('delete',__('Delete'),array(
-			'action' => 'deletePost')))
-	->addField(
-		new dcFieldCombo('post_status',$core->auth->getInfo('user_post_status'),$status_combo,array(
-			'disabled' => !$can_publish,
-			'label' => __('Entry status:'))))
-	->addField(
-		new dcFieldCombo('cat_id','',$categories_combo,array(
-			"label" => __('Category:'))))
-	->addField(
-		new dcFieldText('post_dt','',array(
-			"label" => __('Published on:'))))
-	->addField(
-		new dcFieldCombo('post_format',$core->auth->getOption('post_format'),$formaters_combo,array(
-			"label" => __('Text formating:'))))
-	->addField(
-		new dcFieldCheckbox ('post_open_comment',$core->blog->settings->system->allow_comments,array(
-			"label" => __('Accept comments'))))
-	->addField(
-		new dcFieldCheckbox ('post_open_tb',$core->blog->settings->system->allow_trackbacks,array(
-			"label" => __('Accept trackbacks'))))
-	->addField(
-		new dcFieldCheckbox ('post_selected',false,array(
-			"label" => __('Selected entry'))))
-	->addField(
-		new dcFieldCombo ('post_lang',$core->auth->getInfo('user_lang'),$lang_combo, array(
-			"label" => __('Entry lang:'))))
-	->addField(
-		new dcFieldHidden ('id',''))
-;
+# Validation flag
+$bad_dt = false;
+
 # Get entry informations
 if (!empty($_REQUEST['id']))
@@ -146,47 +105,181 @@
 	else
 	{
-		$form->id = $post->post_id;
-		$form->cat_id = $post->cat_id;
-		$form->post_dt = date('Y-m-d H:i',strtotime($post->post_dt));
-		$form->post_format = $post->post_format;
-		$form->post_password = $post->post_password;
-		$form->post_url = $post->post_url;
-		$form->post_lang = $post->post_lang;
-		$form->post_title = $post->post_title;
-		$form->post_excerpt = $post->post_excerpt;
-		$form->post_excerpt_xhtml = $post->post_excerpt_xhtml;
-		$form->post_content = $post->post_content;
-		$form->post_content_xhtml = $post->post_content_xhtml;
-		$form->post_notes = $post->post_notes;
-		$form->post_status = $post->post_status;
-		$form->post_selected = (boolean) $post->post_selected;
-		$form->post_open_comment = (boolean) $post->post_open_comment;
-		$form->post_open_tb = (boolean) $post->post_open_tb;
-		$form->can_edit_post = $post->isEditable();
-		$form->can_delete= $post->isDeletable();
-		
-	}
-}
-
-$form->setup();
-
-
-
-// TODO: Do it better later, required by some javascripts
-$some_globals = array(
-	'rtl' => l10n::getTextDirection($_lang) == 'rtl',
-	'Nonce' => $core->getNonce(),
-	'sess_id' => session_id(),
-	'sess_uid' => $_SESSION['sess_browser_uid'],
-	'media_manage' => $core->auth->check('media,media_admin',$core->blog->id),
-	'enable_wysiwyg' => isset($core->auth) && $core->auth->getOption('enable_wysiwyg'),
-	'edit_size' => $core->auth->getOption('edit_size')
-);
-foreach($some_globals as $name => $value) {
-	$_ctx->$name = $value;
-};
-//
-
-
+		$post_id = $post->post_id;
+		$cat_id = $post->cat_id;
+		$post_dt = date('Y-m-d H:i',strtotime($post->post_dt));
+		$post_format = $post->post_format;
+		$post_password = $post->post_password;
+		$post_url = $post->post_url;
+		$post_lang = $post->post_lang;
+		$post_title = $post->post_title;
+		$post_excerpt = $post->post_excerpt;
+		$post_excerpt_xhtml = $post->post_excerpt_xhtml;
+		$post_content = $post->post_content;
+		$post_content_xhtml = $post->post_content_xhtml;
+		$post_notes = $post->post_notes;
+		$post_status = $post->post_status;
+		$post_selected = (boolean) $post->post_selected;
+		$post_open_comment = (boolean) $post->post_open_comment;
+		$post_open_tb = (boolean) $post->post_open_tb;
+		
+		$page_title = __('Edit entry');
+		
+		$can_edit_post = $post->isEditable();
+		$can_delete= $post->isDeletable();
+		
+		$next_rs = $core->blog->getNextPost($post,1);
+		$prev_rs = $core->blog->getNextPost($post,-1);
+		
+		if ($next_rs !== null) {
+			$next_link = sprintf($post_link,$next_rs->post_id,
+				html::escapeHTML($next_rs->post_title),__('next entry').'&nbsp;&#187;');
+			$next_headlink = sprintf($post_headlink,'next',
+				html::escapeHTML($next_rs->post_title),$next_rs->post_id);
+		}
+		
+		if ($prev_rs !== null) {
+			$prev_link = sprintf($post_link,$prev_rs->post_id,
+				html::escapeHTML($prev_rs->post_title),'&#171;&nbsp;'.__('previous entry'));
+			$prev_headlink = sprintf($post_headlink,'previous',
+				html::escapeHTML($prev_rs->post_title),$prev_rs->post_id);
+		}
+		
+		try {
+			$core->media = new dcMedia($core);
+		} catch (Exception $e) {}
+	}
+}
+
+# Format excerpt and content
+if (!empty($_POST) && $can_edit_post)
+{
+	$post_format = $_POST['post_format'];
+	$post_excerpt = $_POST['post_excerpt'];
+	$post_content = $_POST['post_content'];
+	
+	$post_title = $_POST['post_title'];
+	
+	$cat_id = (integer) $_POST['cat_id'];
+	
+	if (isset($_POST['post_status'])) {
+		$post_status = (integer) $_POST['post_status'];
+	}
+	
+	if (empty($_POST['post_dt'])) {
+		$post_dt = '';
+	} else {
+		try
+		{
+			$post_dt = strtotime($_POST['post_dt']);
+			if ($post_dt == false || $post_dt == -1) {
+				$bad_dt = true;
+				throw new Exception(__('Invalid publication date'));
+			}
+			$post_dt = date('Y-m-d H:i',$post_dt);
+		}
+		catch (Exception $e)
+		{
+			$core->error->add($e->getMessage());
+		}
+	}
+	
+	$post_open_comment = !empty($_POST['post_open_comment']);
+	$post_open_tb = !empty($_POST['post_open_tb']);
+	$post_selected = !empty($_POST['post_selected']);
+	$post_lang = $_POST['post_lang'];
+	$post_password = !empty($_POST['post_password']) ? $_POST['post_password'] : null;
+	
+	$post_notes = $_POST['post_notes'];
+	
+	if (isset($_POST['post_url'])) {
+		$post_url = $_POST['post_url'];
+	}
+	
+	$core->blog->setPostContent(
+		$post_id,$post_format,$post_lang,
+		$post_excerpt,$post_excerpt_xhtml,$post_content,$post_content_xhtml
+	);
+}
+
+# Create or update post
+if (!empty($_POST) && !empty($_POST['save']) && $can_edit_post && !$bad_dt)
+{
+	$cur = $core->con->openCursor($core->prefix.'post');
+	
+	$cur->post_title = $post_title;
+	$cur->cat_id = ($cat_id ? $cat_id : null);
+	$cur->post_dt = $post_dt ? date('Y-m-d H:i:00',strtotime($post_dt)) : '';
+	$cur->post_format = $post_format;
+	$cur->post_password = $post_password;
+	$cur->post_lang = $post_lang;
+	$cur->post_title = $post_title;
+	$cur->post_excerpt = $post_excerpt;
+	$cur->post_excerpt_xhtml = $post_excerpt_xhtml;
+	$cur->post_content = $post_content;
+	$cur->post_content_xhtml = $post_content_xhtml;
+	$cur->post_notes = $post_notes;
+	$cur->post_status = $post_status;
+	$cur->post_selected = (integer) $post_selected;
+	$cur->post_open_comment = (integer) $post_open_comment;
+	$cur->post_open_tb = (integer) $post_open_tb;
+	
+	if (isset($_POST['post_url'])) {
+		$cur->post_url = $post_url;
+	}
+	
+	# Update post
+	if ($post_id)
+	{
+		try
+		{
+			# --BEHAVIOR-- adminBeforePostUpdate
+			$core->callBehavior('adminBeforePostUpdate',$cur,$post_id);
+			
+			$core->blog->updPost($post_id,$cur);
+			
+			# --BEHAVIOR-- adminAfterPostUpdate
+			$core->callBehavior('adminAfterPostUpdate',$cur,$post_id);
+			
+			http::redirect('post.php?id='.$post_id.'&upd=1');
+		}
+		catch (Exception $e)
+		{
+			$core->error->add($e->getMessage());
+		}
+	}
+	else
+	{
+		$cur->user_id = $core->auth->userID();
+		
+		try
+		{
+			# --BEHAVIOR-- adminBeforePostCreate
+			$core->callBehavior('adminBeforePostCreate',$cur);
+			
+			$return_id = $core->blog->addPost($cur);
+			
+			# --BEHAVIOR-- adminAfterPostCreate
+			$core->callBehavior('adminAfterPostCreate',$cur,$return_id);
+			
+			http::redirect('post.php?id='.$return_id.'&crea=1');
+		}
+		catch (Exception $e)
+		{
+			$core->error->add($e->getMessage());
+		}
+	}
+}
+
+if (!empty($_POST['delete']) && $can_delete)
+{
+	try {
+		# --BEHAVIOR-- adminBeforePostDelete
+		$core->callBehavior('adminBeforePostDelete',$post_id);
+		$core->blog->delPost($post_id);
+		http::redirect('posts.php');
+	} catch (Exception $e) {
+		$core->error->add($e->getMessage());
+	}
+}
 
 /* DISPLAY
@@ -199,7 +292,394 @@
 	$default_tab = 'comments';
 }
-$_ctx->default_tab = $default_tab;
-$_ctx->setPageTitle($page_title);
-
-$core->tpl->display('post.html.twig');
+
+dcPage::open($page_title.' - '.__('Entries'),
+	dcPage::jsDatePicker().
+	dcPage::jsToolBar().
+	dcPage::jsModal().
+	dcPage::jsMetaEditor().
+	dcPage::jsLoad('js/_post.js').
+	dcPage::jsConfirmClose('entry-form','comment-form').
+	# --BEHAVIOR-- adminPostHeaders
+	$core->callBehavior('adminPostHeaders').
+	dcPage::jsPageTabs($default_tab).
+	$next_headlink."\n".$prev_headlink
+);
+
+if (!empty($_GET['upd'])) {
+	dcPage::message(__('Entry has been successfully updated.'));
+}
+elseif (!empty($_GET['crea'])) {
+	dcPage::message(__('Entry has been successfully created.'));
+}
+elseif (!empty($_GET['attached'])) {
+	dcPage::message(__('File has been successfully attached.'));
+}
+elseif (!empty($_GET['rmattach'])) {
+	dcPage::message(__('Attachment has been successfully removed.'));
+}
+
+if (!empty($_GET['creaco'])) {
+	dcPage::message(__('Comment has been successfully created.'));
+}
+
+# XHTML conversion
+if (!empty($_GET['xconv']))
+{
+	$post_excerpt = $post_excerpt_xhtml;
+	$post_content = $post_content_xhtml;
+	$post_format = 'xhtml';
+	
+	dcPage::message(__('Don\'t forget to validate your XHTML conversion by saving your post.'));
+}
+
+echo '<h2>'.html::escapeHTML($core->blog->name).' &rsaquo; '.'<a href="posts.php">'.__('Entries').'</a> &rsaquo; <span class="page-title">'.$page_title;
+if ($post_id) {
+	switch ($post_status) {
+		case 1:
+			$img_status = sprintf($img_status_pattern,__('published'),'check-on.png');
+			break;
+		case 0:
+			$img_status = sprintf($img_status_pattern,__('unpublished'),'check-off.png');
+			break;
+		case -1:
+			$img_status = sprintf($img_status_pattern,__('scheduled'),'scheduled.png');
+			break;
+		case -2:
+			$img_status = sprintf($img_status_pattern,__('pending'),'check-wrn.png');
+			break;
+		default:
+			$img_status = '';
+	}
+	echo ' &ldquo;'.$post_title.'&rdquo;'.' '.$img_status;
+}
+echo	'</span></h2>';
+
+if ($post_id && $post->post_status == 1) {
+	echo '<p><a href="'.$post->getURL().'" onclick="window.open(this.href);return false;" title="'.$post_title.' ('.__('new window').')'.'">'.__('Go to this entry on the site').' <img src="images/outgoing-blue.png" alt="" /></a></p>';
+}
+if ($post_id)
+{
+	echo '<p>';
+	if ($prev_link) { echo $prev_link; }
+	if ($next_link && $prev_link) { echo ' - '; }
+	if ($next_link) { echo $next_link; }
+	
+	# --BEHAVIOR-- adminPostNavLinks
+	$core->callBehavior('adminPostNavLinks',isset($post) ? $post : null);
+	
+	echo '</p>';
+}
+
+# Exit if we cannot view page
+if (!$can_view_page) {
+	dcPage::helpBlock('core_post');
+	dcPage::close();
+	exit;
+}
+
+/* Post form if we can edit post
+-------------------------------------------------------- */
+if ($can_edit_post)
+{
+	echo '<div class="multi-part" title="'.__('Edit entry').'" id="edit-entry">';
+	echo '<form action="post.php" method="post" id="entry-form">';
+	echo '<div id="entry-wrapper">';
+	echo '<div id="entry-content"><div class="constrained">';
+	
+	echo
+	'<p class="col"><label class="required"><abbr title="'.__('Required field').'">*</abbr> '.__('Title:').
+	form::field('post_title',20,255,html::escapeHTML($post_title),'maximal').
+	'</label></p>'.
+	
+	'<p class="area" id="excerpt-area"><label for="post_excerpt">'.__('Excerpt:').'</label> '.
+	form::textarea('post_excerpt',50,5,html::escapeHTML($post_excerpt)).
+	'</p>'.
+	
+	'<p class="area"><label class="required" '.
+	'for="post_content"><abbr title="'.__('Required field').'">*</abbr> '.__('Content:').'</label> '.
+	form::textarea('post_content',50,$core->auth->getOption('edit_size'),html::escapeHTML($post_content)).
+	'</p>'.
+	
+	'<p class="area" id="notes-area"><label for="post_notes">'.__('Notes:').'</label>'.
+	form::textarea('post_notes',50,5,html::escapeHTML($post_notes)).
+	'</p>';
+	
+	# --BEHAVIOR-- adminPostForm
+	$core->callBehavior('adminPostForm',isset($post) ? $post : null);
+	
+	echo
+	'<p>'.
+	($post_id ? form::hidden('id',$post_id) : '').
+	'<input type="submit" value="'.__('Save').' (s)" '.
+	'accesskey="s" name="save" /> ';
+	if ($post_id) {
+		$preview_url =
+		$core->blog->url.$core->url->getURLFor('preview',$core->auth->userID().'/'.
+		http::browserUID(DC_MASTER_KEY.$core->auth->userID().$core->auth->getInfo('user_pwd')).
+		'/'.$post->post_url);
+		echo '<a id="post-preview" href="'.$preview_url.'" class="button">'.__('Preview').'</a> ';
+	}
+	echo
+	($can_delete ? '<input type="submit" class="delete" value="'.__('Delete').'" name="delete" />' : '').
+	$core->formNonce().
+	'</p>';
+	
+	echo '</div></div>';		// End #entry-content
+	echo '</div>';		// End #entry-wrapper
+
+	echo '<div id="entry-sidebar">';
+	
+	echo
+	'<p><label for="cat_id">'.__('Category:').
+	form::combo('cat_id',$categories_combo,$cat_id,'maximal').
+	'</label></p>'.
+	
+	'<p><label for="post_status">'.__('Entry status:').
+	form::combo('post_status',$status_combo,$post_status,'','',!$can_publish).
+	'</label></p>'.
+	
+	'<p><label for="post_dt">'.__('Published on:').
+	form::field('post_dt',16,16,$post_dt,($bad_dt ? 'invalid' : '')).
+	'</label></p>'.
+	
+	'<p><label for="post_format">'.__('Text formating:').
+	form::combo('post_format',$formaters_combo,$post_format).
+	'</label>'.
+	'</p>'.
+	'<p>'.($post_id && $post_format != 'xhtml' ? '<a id="convert-xhtml" class="button" href="post.php?id='.$post_id.'&amp;xconv=1">'.__('Convert to XHTML').'</a>' : '').'</p>'.
+	
+	'<p><label for="post_open_comment" class="classic">'.form::checkbox('post_open_comment',1,$post_open_comment).' '.
+	__('Accept comments').'</label></p>'.
+	($core->blog->settings->system->allow_comments ? 
+		(isContributionAllowed($post_id,strtotime($post_dt),true) ? 
+			'' :
+			'<p class="form-note warn">'.__('Warning: Comments are not more accepted for this entry.').'</p>') : 
+		'<p class="form-note warn">'.__('Warning: Comments are not accepted on this blog.').'</p>').
+
+	'<p><label for="post_open_tb" class="classic">'.form::checkbox('post_open_tb',1,$post_open_tb).' '.
+	__('Accept trackbacks').'</label></p>'.
+	($core->blog->settings->system->allow_trackbacks ? 
+		(isContributionAllowed($post_id,strtotime($post_dt),false) ? 
+			'' :
+			'<p class="form-note warn">'.__('Warning: Trackbacks are not more accepted for this entry.').'</p>') : 
+		'<p class="form-note warn">'.__('Warning: Trackbacks are not accepted on this blog.').'</p>').
+
+	'<p><label for="post_selected" class="classic">'.form::checkbox('post_selected',1,$post_selected).' '.
+	__('Selected entry').'</label></p>'.
+	
+	'<p><label for="post_lang">'.__('Entry lang:').
+	form::combo('post_lang',$lang_combo,$post_lang).
+	'</label></p>'.
+	
+	'<p><label for="post_password">'.__('Entry password:').
+	form::field('post_password',10,32,html::escapeHTML($post_password),'maximal').
+	'</label></p>'.
+	
+	'<div class="lockable">'.
+	'<p><label for="post_url">'.__('Basename:').
+	form::field('post_url',10,255,html::escapeHTML($post_url),'maximal').
+	'</label></p>'.
+	'<p class="form-note warn">'.
+	__('Warning: If you set the URL manually, it may conflict with another entry.').
+	'</p>'.
+	'</div>';
+	
+	# --BEHAVIOR-- adminPostFormSidebar
+	$core->callBehavior('adminPostFormSidebar',isset($post) ? $post : null);
+	
+	echo '</div>';		// End #entry-sidebar
+
+	echo '</form>';
+	
+	# --BEHAVIOR-- adminPostForm
+	$core->callBehavior('adminPostAfterForm',isset($post) ? $post : null);
+	
+	echo '</div>';
+	
+	if ($post_id && $post->post_status == 1) {
+		echo '<p><a href="trackbacks.php?id='.$post_id.'" class="multi-part">'.
+		__('Ping blogs').'</a></p>';
+	}
+	
+}
+
+
+/* Comments and trackbacks
+-------------------------------------------------------- */
+if ($post_id)
+{
+	$params = array('post_id' => $post_id, 'order' => 'comment_dt ASC');
+	
+	$comments = $core->blog->getComments(array_merge($params,array('comment_trackback'=>0)));
+	$trackbacks = $core->blog->getComments(array_merge($params,array('comment_trackback'=>1)));
+	
+	# Actions combo box
+	$combo_action = array();
+	if ($can_edit_post && $core->auth->check('publish,contentadmin',$core->blog->id))
+	{
+		$combo_action[__('publish')] = 'publish';
+		$combo_action[__('unpublish')] = 'unpublish';
+		$combo_action[__('mark as pending')] = 'pending';
+		$combo_action[__('mark as junk')] = 'junk';
+	}
+	
+	if ($can_edit_post && $core->auth->check('delete,contentadmin',$core->blog->id))
+	{
+		$combo_action[__('Delete')] = 'delete';
+	}
+	
+	# --BEHAVIOR-- adminCommentsActionsCombo
+	$core->callBehavior('adminCommentsActionsCombo',array(&$combo_action));
+	
+	$has_action = !empty($combo_action) && (!$trackbacks->isEmpty() || !$comments->isEmpty());
+	
+	echo
+	'<div id="comments" class="multi-part" title="'.__('Comments').'">';
+	
+	if ($has_action) {
+		echo '<form action="comments_actions.php" id="form-comments" method="post">';
+	}
+	
+	echo '<h3>'.__('Trackbacks').'</h3>';
+	
+	if (!$trackbacks->isEmpty()) {
+		showComments($trackbacks,$has_action,true);
+	} else {
+		echo '<p>'.__('No trackback').'</p>';
+	}
+	
+	echo '<h3>'.__('Comments').'</h3>';
+	if (!$comments->isEmpty()) {
+		showComments($comments,$has_action);
+	} else {
+		echo '<p>'.__('No comment').'</p>';
+	}
+	
+	if ($has_action) {
+		echo
+		'<div class="two-cols">'.
+		'<p class="col checkboxes-helpers"></p>'.
+		
+		'<p class="col right"><label for="action" class="classic">'.__('Selected comments action:').'</label> '.
+		form::combo('action',$combo_action).
+		form::hidden('redir','post.php?id='.$post_id.'&amp;co=1').
+		$core->formNonce().
+		'<input type="submit" value="'.__('ok').'" /></p>'.
+		'</div>'.
+		'</form>';
+	}
+	
+	echo '</div>';
+}
+
+/* Add a comment
+-------------------------------------------------------- */
+if ($post_id)
+{
+	echo
+	'<div class="multi-part" id="add-comment" title="'.__('Add a comment').'">'.
+	'<h3>'.__('Add a comment').'</h3>'.
+	
+	'<form action="comment.php" method="post" id="comment-form">'.
+	'<div class="constrained">'.
+	'<p><label for="comment_author" class="required"><abbr title="'.__('Required field').'">*</abbr> '.__('Name:').
+	form::field('comment_author',30,255,html::escapeHTML($core->auth->getInfo('user_cn'))).
+	'</label></p>'.
+	
+	'<p><label for="comment_email">'.__('Email:').
+	form::field('comment_email',30,255,html::escapeHTML($core->auth->getInfo('user_email'))).
+	'</label></p>'.
+	
+	'<p><label for="comment_site">'.__('Web site:').
+	form::field('comment_site',30,255,html::escapeHTML($core->auth->getInfo('user_url'))).
+	'</label></p>'.
+	
+	'<p class="area"><label for="comment_content" class="required"><abbr title="'.__('Required field').'">*</abbr> '.
+	__('Comment:').'</label> '.
+	form::textarea('comment_content',50,8,html::escapeHTML('')).
+	'</p>'.
+	
+	'<p>'.form::hidden('post_id',$post_id).
+	$core->formNonce().
+	'<input type="submit" name="add" value="'.__('Save').'" /></p>'.
+	'</div>'.
+	'</form>'.
+	'</div>';
+}
+
+# Controls comments or trakbacks capabilities
+function isContributionAllowed($id,$dt,$com=true)
+{
+	global $core;
+
+	if (!$id) {
+		return true;
+	}
+	if ($com) {
+		if (($core->blog->settings->system->comments_ttl == 0) || 
+			(time() - $core->blog->settings->system->comments_ttl*86400 < $dt)) {
+			return true;
+		}
+	} else {
+		if (($core->blog->settings->system->trackbacks_ttl == 0) || 
+			(time() - $core->blog->settings->system->trackbacks_ttl*86400 < $dt)) {
+			return true;
+		}
+	}
+	return false;
+}
+
+# Show comments or trackbacks
+function showComments($rs,$has_action,$tb=false)
+{
+	echo
+	'<table class="comments-list"><tr>'.
+	'<th colspan="2">'.__('Author').'</th>'.
+	'<th>'.__('Date').'</th>'.
+	'<th class="nowrap">'.__('IP address').'</th>'.
+	'<th>'.__('Status').'</th>'.
+	'<th>&nbsp;</th>'.
+	'</tr>';
+	
+	while($rs->fetch())
+	{
+		$comment_url = 'comment.php?id='.$rs->comment_id;
+		
+		$img = '<img alt="%1$s" title="%1$s" src="images/%2$s" />';
+		switch ($rs->comment_status) {
+			case 1:
+				$img_status = sprintf($img,__('published'),'check-on.png');
+				break;
+			case 0:
+				$img_status = sprintf($img,__('unpublished'),'check-off.png');
+				break;
+			case -1:
+				$img_status = sprintf($img,__('pending'),'check-wrn.png');
+				break;
+			case -2:
+				$img_status = sprintf($img,__('junk'),'junk.png');
+				break;
+		}
+		
+		echo
+		'<tr class="line'.($rs->comment_status != 1 ? ' offline' : '').'"'.
+		' id="c'.$rs->comment_id.'">'.
+		
+		'<td class="nowrap">'.
+		($has_action ? form::checkbox(array('comments[]'),$rs->comment_id,'','','',0,'title="'.($tb ? __('select this trackback') : __('select this comment')).'"') : '').'</td>'.
+		'<td class="maximal">'.html::escapeHTML($rs->comment_author).'</td>'.
+		'<td class="nowrap">'.dt::dt2str(__('%Y-%m-%d %H:%M'),$rs->comment_dt).'</td>'.
+		'<td class="nowrap"><a href="comments.php?ip='.$rs->comment_ip.'">'.$rs->comment_ip.'</a></td>'.
+		'<td class="nowrap status">'.$img_status.'</td>'.
+		'<td class="nowrap status"><a href="'.$comment_url.'">'.
+		'<img src="images/edit-mini.png" alt="" title="'.__('Edit this comment').'" /></a></td>'.
+		
+		'</tr>';
+	}
+	
+	echo '</table>';
+}
+
+dcPage::helpBlock('core_post','core_wiki');
+dcPage::close();
 ?>
Index: admin/posts_actions.php
===================================================================
--- admin/posts_actions.php	(revision 500)
+++ admin/posts_actions.php	(revision 1035)
@@ -59,4 +59,9 @@
 	$posts = $core->blog->getPosts($params);
 	
+	$posts_ids = array();
+	while ($posts->fetch())	{
+		$posts_ids[] = $posts->post_id;
+	}
+	
 	# --BEHAVIOR-- adminPostsActions
 	$core->callBehavior('adminPostsActions',$core,$posts,$action,$redir);
@@ -73,39 +78,41 @@
 		try
 		{
-			while ($posts->fetch()) {
-				$core->blog->updPostStatus($posts->post_id,$status);
+			$core->blog->updPostsStatus($posts_ids,$status);
+			
+			http::redirect($redir);
+		}
+		catch (Exception $e)
+		{
+			$core->error->add($e->getMessage());
+		}
+	}
+	elseif ($action == 'selected' || $action == 'unselected')
+	{
+		try
+		{
+			$core->blog->updPostsSelected($posts_ids,$action == 'selected');
+			
+			http::redirect($redir);
+		}
+		catch (Exception $e)
+		{
+			$core->error->add($e->getMessage());
+		}
+	}
+	elseif ($action == 'delete')
+	{
+		try
+		{
+			// Backward compatibility
+			foreach($posts_ids as $post_id)
+			{
+				# --BEHAVIOR-- adminBeforePostDelete
+				$core->callBehavior('adminBeforePostDelete',(integer) $post_id);
 			}
 			
-			http::redirect($redir);
-		}
-		catch (Exception $e)
-		{
-			$core->error->add($e->getMessage());
-		}
-	}
-	elseif ($action == 'selected' || $action == 'unselected')
-	{
-		try
-		{
-			while ($posts->fetch()) {
-				$core->blog->updPostSelected($posts->post_id,$action == 'selected');
-			}
-			
-			http::redirect($redir);
-		}
-		catch (Exception $e)
-		{
-			$core->error->add($e->getMessage());
-		}
-	}
-	elseif ($action == 'delete')
-	{
-		try
-		{
-			while ($posts->fetch()) {
-				# --BEHAVIOR-- adminBeforePostDelete
-				$core->callBehavior('adminBeforePostDelete',$posts->post_id);				
-				$core->blog->delPost($posts->post_id);
-			}
+			# --BEHAVIOR-- adminBeforePostsDelete
+			$core->callBehavior('adminBeforePostsDelete',$posts_ids);
+			
+			$core->blog->delPosts($posts_ids);
 			
 			http::redirect($redir);
@@ -121,9 +128,6 @@
 		try
 		{
-			while ($posts->fetch())
-			{
-				$new_cat_id = (integer) $_POST['new_cat_id'];
-				$core->blog->updPostCategory($posts->post_id,$new_cat_id);
-			}
+			$core->blog->updPostsCategory($posts_ids,$_POST['new_cat_id']);
+			
 			http::redirect($redir);
 		}
@@ -144,10 +148,7 @@
 			}
 			
-			while ($posts->fetch())
-			{
-				$cur = $core->con->openCursor($core->prefix.'post');
-				$cur->user_id = $new_user_id;
-				$cur->update('WHERE post_id = '.(integer) $posts->post_id);
-			}
+			$cur = $core->con->openCursor($core->prefix.'post');
+			$cur->user_id = $new_user_id;
+			$cur->update('WHERE post_id '.$core->con->in($posts_ids));
 			
 			http::redirect($redir);
@@ -162,6 +163,26 @@
 /* DISPLAY
 -------------------------------------------------------- */
+// Get current users list
+$usersList = '';
+if ($action == 'author' && $core->auth->check('admin',$core->blog->id)) {
+	$params = array(
+		'limit' => 100,
+		'order' => 'nb_post DESC'
+		);
+	$rs = $core->getUsers($params);
+	while ($rs->fetch())
+	{
+		$usersList .= ($usersList != '' ? ',' : '').'"'.$rs->user_id.'"';
+	}
+}
 dcPage::open(
 	__('Entries'),
+	'<script type="text/javascript">'."\n".
+	"//<![CDATA[\n".
+	'usersList = ['.$usersList.']'."\n".
+	"\n//]]>\n".
+	"</script>\n".
+	dcPage::jsLoad('js/jquery/jquery.autocomplete.js').
+	dcPage::jsLoad('js/_posts_actions.js').
 	dcPage::jsMetaEditor().
 	# --BEHAVIOR-- adminBeforePostDelete
Index: admin/style/candyUpload/style.css
===================================================================
--- admin/style/candyUpload/style.css	(revision 951)
+++ admin/style/candyUpload/style.css	(revision 1065)
@@ -61,7 +61,4 @@
 	font-weight: bold;
 	border-radius: 2px;
-	-o-border-radius: 2px;
-	-moz-border-radius: 2px;
-	-webkit-border-radius: 2px;
 }
 .cu-btn {
Index: admin/style/default.css
===================================================================
--- admin/style/default.css	(revision 972)
+++ admin/style/default.css	(revision 1065)
@@ -148,4 +148,8 @@
 	height: 2em;
 }
+#info-box1 p {
+	margin: 0;
+	display: inline;
+}
 #info-box1 select {
 	width: 15em;
@@ -193,7 +197,4 @@
 	padding: 1em;
 	background: #fff;
-	-webkit-border-radius: .5em;
-	-moz-border-radius: .5em;
-	-o-border-radius: .5em;
 	border-radius: .5em;
 	border: 1px solid #ddd;
@@ -253,8 +254,4 @@
 	background: #dfdfdf;
 	text-decoration: none;
-	-webkit-border-top-left-radius: .3em;
-	-webkit-border-top-right-radius: .3em;
-	-moz-border-radius-topleft: .3em;
-	-moz-border-radius-topright: .3em;
 	border-top-left-radius: .3em;
 	border-top-right-radius: .3em;
@@ -300,5 +297,5 @@
 	padding: .2em 0 0 32px;
 	background-repeat: no-repeat;
-	background-position: 12px .2em;
+	background-position: 12px .4em;
 }
 #main-menu a {
@@ -372,7 +369,4 @@
 	border: 1px solid #999;
 	padding: 1em 1em 0 1em;
-	-o-border-radius: 4px;
-	-moz-border-radius: 4px;
-	-webkit-border-radius: 4px;
 	border-radius: 4px;
 }
@@ -507,7 +501,4 @@
 	border: 1px solid #ccc;
 	border-left: 1em solid #E5E3DA;
-	-o-border-radius: .3em;
-	-moz-border-radius: .3em;
-	-webkit-border-radius: .3em;
 	border-radius: .3em;
 }
@@ -660,7 +651,4 @@
 	border: 1px solid #ddd;
 	padding: .2em;
-	-o-border-radius: .5em;
-	-moz-border-radius: .5em;
-	-webkit-border-radius: .5em;
 	border-radius: .5em;
 }
@@ -757,7 +745,4 @@
 	border: 1px solid #f3f3f3;
 	cursor: pointer;
-	-o-border-radius: 4px;
-	-moz-border-radius: 4px;
-	-webkit-border-radius: 4px;
 	border-radius: 4px;
 }
@@ -810,7 +795,4 @@
 	padding: .33em .75em .33em 1em;
 	border-radius: 1em 0 0 1em;
-	-moz-border-radius: 1em 0 0 1em;
-	-webkit-border-top-left-radius: 1em;
-	-webkit-border-bottom-left-radius: 1em;
 	color: #444;
 }
@@ -876,7 +858,4 @@
 	padding: 0.5em 0.5em 0.5em 48px;
 	margin-bottom: 1em;
-	-o-border-radius: 8px;
-	-moz-border-radius: 8px;
-	-webkit-border-radius: 8px;
 	border-radius: 8px;
 }
@@ -1283,7 +1262,4 @@
 	padding: .1em .5em 0 .5em;
 	text-shadow: 0 1px 1px rgba(0,0,0,.3);
-	-webkit-border-radius: .2em;
-	-moz-border-radius: .2em;
-	-o-border-radius: .2em;
 	border-radius: .2em;
 	margin-bottom: .1em;
@@ -1367,7 +1343,4 @@
 }
 a.button.add {
-	-webkit-border-radius: .5em;
-	-moz-border-radius: .5em;
-	-o-border-radius: .5em;
 	border-radius: .5em;
 	margin-bottom: .1em;	
@@ -1380,2 +1353,35 @@
 	background-color: #2373A8;
 }
+
+/* jQuery Autocomplete plugin */
+.ac_results {
+	padding: 0px;
+	border: 1px dotted #f90;
+	background-color: white;
+	overflow: hidden;
+	z-index: 99999;
+}
+.ac_results ul {
+	width: 100%;
+	list-style-position: outside;
+	list-style: none;
+	padding: 0;
+	margin: 0;
+}
+.ac_results li {
+	margin: 0px;
+	padding: 2px 5px;
+	cursor: default;
+	display: block;
+	font: menu;
+	font-size: 1em;
+	line-height: 16px;
+	overflow: hidden;
+}
+.ac_loading {
+	background: transparent url('loader.gif') right center no-repeat;
+}
+.ac_over {
+	background-color: #06c;
+	color: white;
+}
Index: admin/style/install.css
===================================================================
--- admin/style/install.css	(revision 951)
+++ admin/style/install.css	(revision 1065)
@@ -19,7 +19,4 @@
 	padding:1em 2em;
 	border:1px #ccc solid;
-	-o-border-radius:8px;
-	-moz-border-radius:8px;
-	-webkit-border-radius:8px;
 	border-radius: 8px;
 }
@@ -49,7 +46,4 @@
 	padding:10px 10px 10px 60px;
 	border-radius:8px;
-	-o-border-radius:8px;
-	-moz-border-radius:8px;
-	-webkit-border-radius:8px;
 }
 body.install .warning {
@@ -72,7 +66,4 @@
 	margin-bottom: 1em;
 	border-radius: 8px;
-	-o-border-radius: 8px;
-	-moz-border-radius: 8px;
-	-webkit-border-radius: 8px;
 }
 p.error, p.message, p.static-msg {
@@ -135,7 +126,4 @@
 	padding: 0 10px;
 	border-radius: 2px;
-	-o-border-radius: 2px;
-	-moz-border-radius: 2px;
-	-webkit-border-radius: 2px;
 }
 
@@ -149,7 +137,4 @@
 	padding: .1em .5em;
 	text-shadow: 0 1px 1px rgba(0,0,0,.3);
-	-webkit-border-radius: .2em;
-	-moz-border-radius: .2em;
-	-o-border-radius: .2em;
 	border-radius: .2em;
 	margin-bottom: .1em;
Index: admin/style/jsToolBar/jsToolBar.css
===================================================================
--- admin/style/jsToolBar/jsToolBar.css	(revision 951)
+++ admin/style/jsToolBar/jsToolBar.css	(revision 1065)
@@ -82,7 +82,4 @@
 	color: #fff;
 	background: #999;
-	-o-border-radius: 0 0 2px 2px;
-	-moz-border-radius: 0 0 2px 2px;
-	-webkit-border-radius: 0 0 2px 2px;
 	border-radius: 0 0 2px 2px;
 }
